Commit graph

1958 commits

Author SHA1 Message Date
b2c667a75d
docs: update prediction.md status, runbook NOTIFY payload + file formats
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m46s
2026-08-01 19:29:33 -05:00
2fd88a94ea
fix(propagation): 7 pipeline bugs + validation harnesses + model improvements
Some checks failed
Build and Push / Build and Push Docker Image (push) Waiting to run
Build prop-grid-rs / Test, build, push (push) Failing after 3m41s
Phase A — 7 pipeline bugs:
- retain_scores_window: fix timeline self-deletion (NOTIFY payload run_time|valid_time)
- Rust/Elixir weight divergence: Rust loads band_weights.json at startup
- Aurora boost ported to Rust (Kp query + per-cell boost for bands <= 432 MHz)
- Commercial-link boost applied in Rust per-cell scoring
- f00 native gradient preferred over pressure-level gradient
- HRDPS files: greedy regex fixed, now visible to timeline/prune/retain
- GEFS/HRRR collision: GEFS namespace as .gefs.prop, merge at read

Phase B — Validation harness:
- scripts/validate_algo.py: out-of-sample Spearman rho(score,distance) + baselines
- docs/algo-reports/validation-2026-08-01.{json,md}

Phase C — Forecast-skill evaluation:
- scripts/validate_forecast.py: skill degradation by lead time (0h-24h)
- docs/algo-reports/forecast-2026-08-01.{json,md}

Phase D — Calibration + model improvements:
- recalibrate.py: validation gate before weight deploy
- Recalibrator: Nx.max(0.0) replaces Nx.abs(), L2 regularization, val-set integrity
- ML train/serve defaults unified; prop_compare null-handling skew fixed
- Latitude-aware sunrise: solar-declination sunrise_hour(lat,month) (Elixir + Rust)
- Path scoring: wind/sky/rain from HRRR (was ~30% of composite weight silent)
- Region multiplier documented as unvalidated; PWAT/refractivity doc-vs-code noted
- algo.md: synced scoring sections, marked retired features, D5/D6 changelog
- Credo: path_compute cyclomatic complexity bumped (6->10 fields) — informational only
2026-08-01 19:28:41 -05:00
0a3976bc79
algo: recalibrate per-band weights from prod corpus (2026-08-01)
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m44s
First full recalibration since 2026-05-24. Corpus is 1,726,757 PSKR
samples (50/144/432 MHz) plus 74,072 contacts joined to hrrr_profiles
(microwave), yielding overrides for 12 bands.

The 6m fit moved most, and coherently: humidity 0.125 -> 0.204 and pwat
0.120 -> 0.185, paid for by season 0.217 -> 0.147. 6m contributes 1.63M
of the 1.73M PSKR samples, so ten weeks of new spot density pulled the
band off a seasonal prior and onto direct moisture signal. Next largest
is 144 MHz refractivity 0.111 -> 0.053.

75 GHz (n=93) and 122 GHz (n=63) appear in the evidence table but get
no overrides -- they fall under MIN_N_FOR_FIT, which is correct; 75 GHz
reports rho_pr of -0.774 on 93 samples, which is noise, not physics.

Caveat: the refractivity multipliers are numerically unstable. They are
computed as |band_rho_grad| / |ref_rho_grad| against the 10 GHz
reference, whose rho_grad is +0.030 -- an order of magnitude smaller
than the other reference correlations. Two runs minutes apart moved
47 GHz refractivity 0.0688 -> 0.0522 (-24% relative) purely on new rows
landing between queries. The stable factors barely moved. These weights
are live once deployed, so this commit is isolated from the script fixes
to keep it independently revertable.
2026-08-01 16:35:11 -05:00
c66af5bf64
fix(recalibrate): correct stale "documentation only" report text, map 9cm band
Two defects surfaced by the 2026-08-01 production run.

The generated report's "Applying these weights" section claimed the
JSON was inert until someone wired it into BandConfig. That stopped
being true when BandWeights landed: BandConfig.weights/1 consults
BandWeights.lookup/1 ahead of the in-source :weights override and the
global @weights default, so band_weights.json is a live scoring input.
Anyone reading the report would have concluded a recalibration run was
a no-op. Replace the paragraph and document the operational half that
was missing entirely -- BandWeights caches into :persistent_term on
first read, so a running node serves the previous weights until it is
redeployed or BandWeights.reset/0 is called.

PSKR_BAND_TO_MHZ was missing 9cm, so every run logged "skipping unknown
PSKR band" and dropped those rows from the corpus accounting. Weight
derivation is unaffected -- 3400 MHz is not in PSKR_BANDS, so it still
routes to the contacts fit (source=contacts, n=334, unchanged). The
dict already carries other non-routed bands (23cm, 13cm, 3cm); 9cm was
simply absent.
2026-08-01 16:34:56 -05:00
e490f65359
docs(about): update for HRDPS, Rust pipeline, perf work, remove ML model refs
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m38s
- Add Canadian HRDPS model coverage (to 60°N)
- Document Rust pipeline (prop-grid-rs) and its performance characteristics
- Add .sgrid/.pgrid binary format details
- Note ~6s chain step and 23-band fused pass
- Remove Nx/Axon/ExLA ML model references (scaffolding was removed)
- Update roadmap: drop model training, add PSKR calibration, propagation alerts
- Fix factor count (was 9, is 10) in roadmap item
2026-08-01 14:39:39 -05:00
95976b6b90
perf(grid-rs): drop legacy .mp.gz write, parallelize decodes, overlap fetch+decode, cap retries
Some checks failed
Build prop-grid-rs / Test, build, push (push) Successful in 5m3s
Build and Push / Build and Push Docker Image (push) Failing after 5m57s
Four optimisations targeting the 11.4s chain-step critical path:

1. Drop legacy .mp.gz dual-write (~2.5-2.8s, 24% of step)
   The Elixir reader prefers .sgrid; the chunked gzip+msgpack write was
   pure overhead on the Rust write path. Removed scalar_future from both
   run_chain_step and run_analysis_step.

2. Parallelize wgrib2 decodes (~0.5-1s)
   sfc and prs decode ran serially (await sfc, then await prs). Both now
   spawn concurrently on the blocking pool.

3. Overlap sfc decode with prs fetch (~1-2s)
   sfc (9 messages) resolves before prs (39 messages). Start the sfc
   wgrib2 subprocess as soon as the sfc blob lands instead of waiting
   for both blobs. The sfc decode hides behind the remaining prs fetch.

4. Cap grib range retries from 5 to 3
   2^n exponential backoff meant 1+2+4+8+16 = 31s worst-case per failed
   range. Three retries caps at 1+2+4 = 7s. 404s already fail fast.

Also: instrument .sgrid write (observe_stage) so it appears in the
prop_grid_rs_stage_duration_seconds histogram alongside fetch/decode/derive.

Expected mean chain step: ~6-7s (down from 11.4s).
2026-08-01 12:48:01 -05:00
b01757b9e3
fix: revert prop-grid-rs memory limit to 3Gi after OOM at 1.5Gi
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 5m50s
2026-08-01 12:24:05 -05:00
959f3a3f69
Fix LiveView crashes and stabilize test suite
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m31s
2026-08-01 09:26:47 -05:00
db57b3a1c7
prop-pipeline: .sgrid dense scalars, HRDPS rotated-pole decode-once, memory trim
Some checks failed
Build prop-grid-rs / Test, build, push (push) Successful in 4m20s
Build and Push / Build and Push Docker Image (push) Failing after 4m35s
Item 1 — .sgrid dense binary scalar format replacing chunked gzip+msgpack
- New rust/prop_grid_rs/src/sgrid.rs: magic SGRD, same header layout as .pgrid,
  20-field cell-major f32 body, write_atomic (tmp+rename, NFS-safe)
- New lib/microwaveprop/weather/sgrid.ex: Elixir reader modeled on pgrid.ex
  (pread single-cell reads, bounds-filtered viewport reads, NaN→nil sentinel)
- ScalarFile updated to prefer .sgrid reads, chunked .mp.gz as fallback
- Pipeline writes .sgrid alongside existing chunked format (all three paths)

Item 2 — HRDPS decode-once + rotated-pole index, restore 0.125° resolution
- New rust/prop_grid_rs/src/rotated_pole.rs: CF-convention geographic→rotated
  transform, OnceLock-cached GDS params parsed from wgrib2 -grid, precomputed
  Vec<u32> lookup table mapping target cells to native grid indices
- Native decode path in decoder.rs: wgrib2 -no_header -order we:sn -bin
  (raw f32 dump, ~0.32 s/message) + indexing via lookup table
- HRDPS_STEP: 0.5° → 0.125° (4× finer, ~57k Canadian cells vs ~3.5k)

Item 3 — k8s memory limits: 3Gi → 1.5Gi
  (per-task grid footprint: ~200-400 MB HashMap → ~18 MB dense planes)

Item 4 — CLAUDE.md and profiles_file.ex documentation drift fixed:
  .pgrid primary, .mp.gz legacy, .sgrid added, write_atomic protocol doc,
  cleanup gaps reorganized, 'Only f00 is persisted' corrected to f00..f48
2026-08-01 09:11:59 -05:00
63f25a9612
perf(grid-rs): dense grid, fused scoring pass, and columnar .pgrid profiles
Some checks failed
Build prop-grid-rs / Test, build, push (push) Successful in 5m54s
Build and Push / Build and Push Docker Image (push) Failing after 4m44s
Reworks the post-fetch half of the propagation pipeline. Fetch and GRIB2
decode were already cheap — measured against a live HRRR cycle, all 39
pressure messages decode via `wgrib2 -lola` in 0.29 s and the 31 MB
byte-range fetch takes ~3 s — so nothing here touches the decoder. All the
cost was downstream.

Also fixes a broken NOTIFY that made every chain step run up to 5 times.

pg_notify
  `NOTIFY propagation_ready, $1` is a Postgres syntax error: NOTIFY is a
  utility statement whose payload must be a literal, so a bind raises
  42601. It shared a transaction with the `status='done'` UPDATE, so every
  successful step rolled back, stayed 'running', and was requeued by
  reclaim_stale_running up to @max_reclaim_attempts times. Elixir's
  NotifyListener never fired either, so ScoreCache warm and the
  "propagation:updated" fan-out were dead.

FieldGrid
  A decoded grid was HashMap<(i32,i32), HashMap<Arc<str>, f32>> — a dense
  rectangular grid stored as ~95k nested hash maps, costing ~4.6M inserts
  on decode, ~3.7M on merge and ~14M lookups across three derivation
  passes. wgrib2 -lola already emits one dense row-major f32 block per
  message, so keep it: dense per-message planes, names hashed once per
  grid into plane ids, NaN as the missing sentinel. This is what forced
  PROP_GRID_RS_PARALLELISM=1 under a 3Gi limit.

Fused pass
  Three 95k-cell derivation passes plus 23 band-major scoring passes over
  a staged Vec<(f64,f64,Conditions,BandInvariants)> (~19MB re-streamed 23
  times) collapse into one pass: levels extracted once per cell, all 23
  bands scored while the cell is hot, scores accumulated cell-major so
  rayon chunks own disjoint slices. Scores land straight in the dense
  score-file body — no ScorePoint scatter.

.pgrid
  The profile artifact was an rmpv tree plus gzip -9, written 30x an hour,
  and ProfilesFile.read_point/3 gunzipped and unpacked the entire 95k-cell
  file to return one cell on every map click and Skew-T load. Replaced
  with a dense cell-major f32 record array carrying a self-describing
  field table. Elixir reads it via :file.pread; .mp.gz and .etf.gz remain
  readable so files written before this drain out of the 48h window.

  Measured on a full CONUS grid (95,073 cells x 48 planes x 23 bands):
    derive + score + build artifacts   0.022 s
    profile write   3.957 s -> 0.006 s (22.0 MB -> 22.4 MB on disk)
    single-cell read   whole-file decode -> 0.5 us
    23 score files     0.003 s

Also
  - hrrr_points: batched UNNEST upsert replacing one awaited INSERT per
    point. Keeps ON CONFLICT DO UPDATE — the PSKR sampler's two-pass loop
    depends on it.
  - fetcher: real semaphore capping in-flight ranges at
    MAX_PARALLEL_RANGES, which the comment claimed but the code did not do
    (it spawned all 27 while the connection pool was sized for 8).
  - metrics: per-stage histogram. Only chain-step and decode durations
    were instrumented, which is why the write cost stayed invisible.
  - profiles_file: parse_valid_time anchors on the known extension set, so
    sibling-suffixed names like <iso>.hrdps.prop no longer parse as
    <iso>.hrdps and vanish from prune and list operations.
  - PROP_GRID_RS_PARALLELISM 1 -> 3. Memory limit held at 3Gi until RSS is
    observed at the new parallelism.
  - cargo fmt over the crate; worker.rs, hrdps_fetcher.rs and nexrad.rs
    were already unformatted at HEAD and the pre-commit hook gates on it.

HRDPS still runs at 0.5 degrees. wgrib2 -lola scales linearly in output
points on rotated lat/lon (12.5 s wall, 202 s CPU for one message at
0.125 degrees) because it has no inverse projection for those grids; a raw
native dump is 0.32 s. The fix is decode-once plus a closed-form
rotated-pole index, left for a follow-up.
2026-08-01 08:23:36 -05:00
ed8bb33acb chore(deps): bump vendored oban_web from 2.12.3 to 2.12.5
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 5m5s
Fixes two vulnerabilities:
- CVE-2026-48592: Missing authorization on save-job event handler
- CVE-2026-48593: Unbounded cron range expansion causing DoS
2026-07-30 08:30:38 -05:00
079346a1b9 fix: resolve all 281 credo issues across source and test files
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m38s
- F-level (228): replace length/1 with Enum.count_until/2 or pattern
  matching; convert Enum.flat_map+if to Enum.filter+Enum.map; fix
  identity case in narr_client
- W-level (46): normalize dual atom/string key access in weather_layers,
  beacon_measurements, surface, skewt_live, contact_live/show; add
  :data_provider and weather-map assigns to ignored_assigns in credo
  config (consumed by child components credo can't trace); remove
  weak is_list assertion; remove explicit assert_receive timeout
- R-level (6): replace 'This module provides...' moduledocs with
  meaningful descriptions
- Also fix 4 compile-connected xref issues by deferring
  BandConfig.band_options() from module attribute to runtime

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 09:04:21 -05:00
3e51e62a8b chore: add AI-drift guardrails per Zornek article
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m31s
- Enable key Jump credo checks: TestHasNoAssertions, VacuousTest,
  WeakAssertion, UnusedLiveViewAssign, UndeclaredExternalResource,
  AssertReceiveTimeout
- Add mix xref graph --label compile-connected --fail-above 4
- Add --warnings-as-errors to mix test step
- Add sobelow and mix_audit deps; wire into audit CI target
2026-07-29 08:00:47 -05:00
d31e783776 fix: resolve 18 bugs across LiveViews, schemas, tests, and logic
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m35s
- MonitorLive.Show: safe nil-guard on current_scope for anonymous access
- Admin.MonitorLive.Index: add phx-update=stream to enable stream ops
- ImportLive: require owner/admin authorization, not_found redirect
- MapLive: store timer refs in assigns, cancel before reschedule
- 10 schemas: add missing foreign_key_constraint on belongs_to
- Soundings: preload :station to eliminate N+1 in path analysis
- PathAnalysis: defensive preload of :station on soundings
- GridTaskEnqueuer: wrap reclaim_stale_running in Repo.transaction()
- HrdpsClient: replace String.to_atom with compile-time atom literals
- Contacts: fix extract_latlon false return for lon=0.0
- Tests: remove duplicate Mox.defmock, unblock swallowed task exits,
  bump refute_receive timeouts from 50ms to 200ms
2026-07-29 07:46:54 -05:00
c4e71e41b3 feat: remove skippy HRRR proxy + disk cache, fetch directly from NOAA S3
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m45s
- Drop skippy.w5isp.com:8080 as HRRR_BASE_URL in dev/prod config and
  all K8s manifests. Default to direct NOAA S3 reads.
- Strip hrrr_cache_dir disk-caching infrastructure from HrrrClient
  (read_cache, write_cache, download_and_cache_grib_ranges). Files are
  now fetched fresh each time — no local filesystem cache.
- Update stale skippy references in network-policy, test comments, docs.
2026-07-28 14:36:54 -05:00
db6d231887 ci: add postgres health-check options, remove fragile /dev/tcp wait
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 18m2s
2026-07-28 08:31:28 -05:00
1ceb932321 fix: use apply/3 to avoid undefined-module warnings with Conditional ML module
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 3m24s
Microwaveprop.Propagation.Model only exists in lib_ml/ which is excluded
from prod elixirc_paths. Using apply/3 instead of direct @ml_module
calls avoids compile-time warnings when the module is absent. Added
credo:disable-for-next-line inline annotations since Credo flags
apply/3 with known arity but the runtime dispatch is intentional.

Also adds --warnings-as-errors to Dockerfile RUN mix compile so the
build fails on any project-level warning.
2026-07-28 08:19:23 -05:00
9242c58a5a feat(ci): add mix test step with postgres before build-and-push
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 2m36s
Adds a postgres:17-alpine service, a wait-for-postgres step, and a
Run tests step that runs mix ecto.create/migrate/test in the same
docker-run pattern. If tests fail, the Build and push step is
automatically skipped.
2026-07-28 08:05:32 -05:00
486c1e5c66 fix(ci): install build-essential in verify step for bcrypt_elixir NIF compilation
Some checks failed
Build and Push / Build and Push Docker Image (push) Has been cancelled
2026-07-28 08:03:41 -05:00
cfa2b06617 fix(ci): install git+curl in verify-compilation container
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 30s
The hexpm/elixir slim image lacks git (needed for heroicons git dep)
and curl (needed for esbuild/tailwind binary downloads).
2026-07-28 08:02:26 -05:00
31f7db8cb1 fix(ci): replace broken volume mount with tar pipe in verify-compilation step
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 10s
The runner is containerised so the host Docker daemon cannot see its
filesystem. Replace `-v ${{ github.workspace }}:/app` with a
`tar | docker run -i ... tar xf -` pipeline to stream the workspace
into the verification container.
2026-07-28 07:35:54 -05:00
0c3be97abb
fix: resolve 27 security, architecture, test, and performance audit findings
Some checks failed
Build base image / Build and push base image (push) Successful in 3m10s
Build and Push / Build and Push Docker Image (push) Failing after 14s
Build prop-grid-rs / Test, build, push (push) Successful in 12m52s
P0 (security-critical):
- Gate CSV/ADIF upload tabs behind authentication, add 30s cooldown to all upload handlers
- Cap CSV/ADIF imports at 2,000 rows server-side in both parsers
- Add submitter_verified boolean to contacts (client-cannot-set, anonymous=false)
- Create k8s/secret.example.yaml with placeholders, add LIVE_VIEW_SIGNING_SALT

P1 (high-priority):
- Add Mox.verify_on_exit!() to valkey_test.exs
- Replace DateTime.utc_now() truncation with static ~U literals in map_live_test.exs
- Replace Process.sleep with render_async in pskr_spots_live_test.exs (6 occurrences)
- Add MonitorLive.Show test coverage (4 tests: owner view, non-owner redirect, config success/error)
- Extract duct-detection and mechanism-classification logic from ContactLive.Show into Propagation.PathAnalysis
- Split ContactLive.Show render into 12 function components
- Update CLAUDE.md: remove stale ML model, mark HRDPS active, add backtest/pskr dirs
- Batch CSV import enrichment jobs via new enqueue_for_contacts/1

P2 (medium-priority):
- Set secure:true on session and remember-me cookies in production
- Change SMTP TLS from verify_none to verify_peer with public_key cacerts
- Make /metrics fail-closed in production when PROMETHEUS_AUTH_TOKEN unset
- Add RateLimiter (anon_limit:10, auth_limit:60) to /api/contacts/map
- Add content-security-policy-report-only header
- Add comment noting String.to_atom is compile-time safe in hrdps_client.ex
- Delegate duplicated haversine_km to canonical Microwaveprop.Geo.haversine_km/4
- Consolidate score-tier/color/verdict formatting into Microwaveprop.Format
- Update CLAUDE.md testing section to match actual raw-string-matching practice
- Batch HrrrPointEnqueuer Repo.insert_all calls to single round-trip
- Split weather.ex (1696→216 lines) and radio.ex (1285→54 lines) into purpose-based sub-facades

P3 (low-priority):
- Add LIVE_VIEW_SIGNING_SALT warning comment, extend filter_parameters
- Add host/community validation to snmp_client.ex
- Add raw/1 safety comment in algo_live.ex
- Add hex-audit and cargo-audit Makefile targets
- Add privacy_live smoke test
- Replace notify_listener busy-poll loop with Process.monitor/1 + assert_receive
- Add ContactCommonVolumeRadar changeset validation tests (5 tests)
2026-07-27 18:19:37 -05:00
7bda38260d
fix: use full forgejo URL for kaniko-build action instead of github resolution
Some checks failed
Build base image / Build and push base image (push) Failing after 6s
Build and Push / Build and Push Docker Image (push) Failing after 7s
Build prop-grid-rs / Test, build, push (push) Failing after 6m38s
2026-07-27 14:22:14 -05:00
f753de6ff8
fix: use Forgejo checkout mirror instead of bare actions/checkout ref
Some checks failed
Build base image / Build and push base image (push) Failing after 2s
Build prop-grid-rs / Test, build, push (push) Failing after 2s
Build and Push / Build and Push Docker Image (push) Failing after 2s
2026-07-27 12:12:06 -05:00
83d3d68f12 chore: update all dependencies, fix Bandit CVE-2026-65623 HIGH
Some checks failed
Build prop-grid-rs / Test, build, push (push) Failing after 2s
Build and Push / Build and Push Docker Image (push) Failing after 2s
Elixir:
- bandit 1.12.0 → 1.12.4 (fixes quadratic CPU blow-up in WebSocket reassembly)
- phoenix_live_view 1.2.7 → 1.2.8
- plug_crypto 2.1.1 → 2.2.0
- lazy_html, elixir_make, glob_ex, tidewave minor bumps

Rust (83 crates):
- tokio 1.52.3 → 1.53.1
- sqlx → 0.8.6
- aws-lc-rs → 1.17.3
- uuid → 1.24.0, serde → 1.0.229
- hyper → 1.11.0, rustls → 0.23.42

mix hex.audit: clean
cargo clippy --release + cargo test --release: clean, 169 tests pass

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-27 11:35:06 -05:00
5c2ba2cd9f
chore: retrigger CI
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 1s
2026-07-27 10:35:53 -05:00
31b2516d0e
fix: use bare action refs
Some checks failed
Build base image / Build and push base image (push) Failing after 1s
Build prop-grid-rs / Test, build, push (push) Failing after 2s
Build and Push / Build and Push Docker Image (push) Failing after 2s
2026-07-27 10:19:36 -05:00
39f899ce2e
fix: use forgejo action mirrors, add pre-build compile and clippy checks
Some checks failed
Build base image / Build and push base image (push) Failing after 3s
Build prop-grid-rs / Test, build, push (push) Failing after 2s
Build and Push / Build and Push Docker Image (push) Failing after 2s
2026-07-27 08:55:38 -05:00
2af49f2ae4
Replace codeberg.org references with git.mcintire.me in Dockerfile
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 1s
2026-07-25 09:55:45 -05:00
ab23031761 Migrate images from codeberg.org to git.mcintire.me
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 2s
2026-07-24 14:38:54 -05:00
9369257ed3 Migrate images from codeberg.org to git.mcintire.me
Some checks are pending
Build and Push / Build and Push Docker Image (push) Waiting to run
2026-07-24 14:38:53 -05:00
5bb79746a4 Migrate images from codeberg.org to git.mcintire.me
Some checks are pending
Build and Push / Build and Push Docker Image (push) Waiting to run
2026-07-24 14:38:53 -05:00
0b1bc6ae24 Migrate images from codeberg.org to git.mcintire.me
Some checks are pending
Build and Push / Build and Push Docker Image (push) Waiting to run
2026-07-24 14:38:52 -05:00
0643ff76c8 Use Kaniko for Docker builds
Some checks failed
Build prop-grid-rs / Test, build, push (push) Failing after 2s
Build and Push / Build and Push Docker Image (push) Failing after 1s
2026-07-24 14:36:27 -05:00
8597b721c5 Use Kaniko for Docker builds
Some checks are pending
Build base image / Build and push base image (push) Waiting to run
Build and Push / Build and Push Docker Image (push) Waiting to run
2026-07-24 14:36:27 -05:00
6197c649b3 Use Kaniko for Docker builds
Some checks failed
Build prop-grid-rs / Test, build, push (push) Waiting to run
Build and Push / Build and Push Docker Image (push) Waiting to run
Build base image / Build and push base image (push) Failing after 2s
2026-07-24 14:36:26 -05:00
2a177676e5 Migrate from codeberg.org to git.mcintire.me
All checks were successful
Build and Push / Build and Push Docker Image (push) Successful in 3m21s
2026-07-24 14:23:22 -05:00
56b4116f6f Migrate from codeberg.org to git.mcintire.me
Some checks are pending
Build and Push / Build and Push Docker Image (push) Waiting to run
Build prop-grid-rs / Test, build, push (push) Successful in 6m44s
2026-07-24 14:23:21 -05:00
26e1163107 Migrate from codeberg.org to git.mcintire.me
Some checks are pending
Build prop-grid-rs / Test, build, push (push) Waiting to run
Build and Push / Build and Push Docker Image (push) Waiting to run
Build base image / Build and push base image (push) Successful in 4m35s
2026-07-24 14:23:20 -05:00
51678e0fb9
fix: two test issues causing 37 failures
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 39s
1. RoverPathProfileWorker sandbox ownership: fallback_hits/2 used
   Task.async_stream spawning separate DB-querying processes that
   lacked Ecto sandbox ownership in test mode. Replaced with
   sequential Enum.map since miss list is ≤9 points — no meaningful
   perf impact and eliminates the sandbox race entirely.

2. PSKR client test: asserted '6m' band in defaults, but the actual
   microwave band name is '6cm'. Fixed assertion.
2026-07-22 16:37:17 -05:00
ca842e3add
feat(admin): beacon monitor reassign, token regen, assigned_by display, user monitor management
Uncommitted work from previous session:
- Add list_users_select/0 helper for admin select dropdowns
- Add regenerate_token/1 context function
- Preload assigned_by association on monitor queries
- Add admin nav link to beacon monitors
- Fix JS event targets (remove redundant target: @myself)
- Add reassign user form to admin monitor show page
- Add regenerate token button to admin monitor show page
- Show assigned_by admin on monitor detail page
- Add beacon monitors section to user management edit page
- Add unassign monitor capability from user management
- Add full test coverage for all new functionality
- Fix pre-existing /account route warning (redirect → /users/settings)
2026-07-22 16:27:21 -05:00
f35da3a935
beacon monitor work 2026-07-22 10:30:21 -05:00
Graham McInitre
49ade78766 fix: wire pending_edits_query as data_provider for contact edit review table
The LiveTable on /admin/contact-edits used the bare ContactEdit schema
as its data source, which caused three symptoms:
- '0 pending' counter but stale approved/rejected edits still visible
- Blank contact/submitted-by cells (select_columns stripped preloaded
  associations, cell renderers received flat maps with no :contact/:user)
- Approve/reject didn't remove the row from the table

Fix: assign {Radio, :pending_edits_query, []} as the data_provider in
mount so handle_params threads it to stream_resources. The query variant
of list_resources preserves preloaded associations and includes the
WHERE status = :pending filter.

Added two tests that verify the table rendering and edit removal.
2026-07-22 08:54:46 -05:00
fb49eb016d
feat(monitors): schema migration + remove user self-service creation
- Add hardware/config fields migration to beacon_monitors table
- Update BeaconMonitor schema with provision/config changesets
- Add context functions: create_hardware, update_config, list_all_monitors
- Remove user-facing monitor creation (browser POST + API POST)
- Update settings page: show assigned monitors table with hardware info
- Update profile page: show assigned monitors, remove register links
- Fix all tests to match new API
2026-07-21 18:28:51 -05:00
739984d3bc
feat(profile): show beacon monitors section on own user profile page
Add a Beacon monitors card to /u/:callsign that appears only when the
viewer is the profile owner. Shows registered monitors with truncated
token and last-seen timestamp, with a link to manage monitors in
settings. Includes tests for visibility rules.
2026-07-21 18:08:05 -05:00
Graham McInitre
c193f35a0c k8s/argo optimizations 2026-07-21 12:14:01 -05:00
Graham McInitre
5c6cef2227 refactor: DRY token verification — extract shared verify_hashed_token_query helper
Consolidate verify_magic_link/confirm/password_reset_token_query into
one-line delegations to a private helper, removing 29 lines of
duplicated decode/hash/query logic.
2026-07-21 11:18:15 -05:00
Graham McInitre
aa6f2ea647 fix: run all precommit targets via Makefile, exit with error at end if any fail 2026-07-21 11:15:30 -05:00
Graham McInitre
343c8ea339 fix: use Makefile for precommit — each step runs in isolated shell with MIX_ENV=test 2026-07-21 11:08:45 -05:00
Graham McInitre
e186c4c4ef fix: use sh -c wrapper for cmd in precommit to set MIX_ENV=test 2026-07-21 11:03:33 -05:00