Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
abc05f4
build: centralize local runtime metadata paths
ceane Sep 14, 2026
0daded6
feat: persist I/Q capture downloads and identify mock sources
ceane Sep 14, 2026
c797302
docs: regenerate article output
ceane Sep 14, 2026
543805b
chore: refresh audited npm dependencies
ceane Sep 14, 2026
fc3753f
fix: avoid hard-coded crypto value false positive
ceane Sep 14, 2026
0633ec7
feat: verify I/Q capture file integrity
ceane Sep 14, 2026
51b552f
fix: unclamp spectrum scrolling and remove HackRF retune latency
ceane Sep 15, 2026
bde259a
fix: stop clamping live acquisition to the active channel
ceane Sep 15, 2026
e12483c
fix: stop re-anchoring the live range to the channel on every change
ceane Sep 15, 2026
d920874
fix: give the HackRF baseband filter no off state
ceane Sep 16, 2026
47101ab
fix: keep the baseband toggle and make its auto value follow the samp…
ceane Sep 16, 2026
80af96e
chore: harden dependency and tooling compatibility
ceane Sep 16, 2026
db0504d
feat: harden build and Rust hot reload handoff
ceane Sep 16, 2026
0586d58
fix: release SDR ownership during backend shutdown
ceane Sep 16, 2026
1f9e52f
fix: stabilize spectrum source and renderer lifecycle
ceane Sep 16, 2026
fc0004f
fix: align article elapsed-time heading
ceane Sep 16, 2026
9894b16
docs: refine repository working guidance
ceane Sep 16, 2026
8c6d86a
fix: hold the sample-rate mode and pending rate as rendered state
ceane Sep 16, 2026
ba30247
chore(deps): bump Rust server-stack crates to current majors
ceane Sep 16, 2026
20471a2
chore(deps): bump aes-gcm to 0.11 and migrate off deprecated nonce API
ceane Sep 16, 2026
22a220e
chore(deps): align the WASM crate generation and drop a dead manifest
ceane Sep 16, 2026
cee29ba
chore(deps): apply in-range npm updates
ceane Sep 16, 2026
b016b48
chore(deps): bump frontend deps to current minor versions
ceane Sep 16, 2026
7b070b2
chore(deps): bump vitest to 5 and jest-dom to 7
ceane Sep 16, 2026
b8bb957
chore(deps): bump runtime deps to current majors
ceane Sep 16, 2026
f1542a0
chore(deps): bump oxlint to 1.82 and oxfmt to 0.67
ceane Sep 16, 2026
b9a8001
chore(ci): add Dependabot config and pin the Node line
ceane Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .commandcode/taste/taste/taste.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
- Rejects invented/hardcoded default values as "fake numbers" — UI defaults and fallback lists (e.g., sample rates) must be derived from the canonical config source (signals.yaml: the global `sample_rate` default plus per-device `sample_rate` specs, resolved via the same `resolveSampleRateSpec` utility the live view uses), never hardcoded literals like `[3.2M, 1.6M, 800k]`; explicitly stated "3.2, 1.6, 800k are fake numbers... the default sample rates are derived from signals.yaml." Confidence: 0.85
- Works concurrently with the agent in the same working tree (e.g., added the SDR++ colormap attribution section and its test to `SettingsRoute.tsx` mid-session); expects the agent to recognize such in-progress work via git diff and leave it untouched rather than treating it as its own change, "fixing" it, or reverting it. Confidence: 0.6
- Wants third-party open-source assets used in the app credited to their authors (e.g., the SDR++ waterfall colormaps attributed to Youssef Touil in the settings page), consistent with the existing insistence on attribution for third-party data and legal/licensing notices. Confidence: 0.6
- During a long, multi-step debugging investigation, expects the agent to carry the work through to completion autonomously; when the agent pauses mid-analysis, the user nudges with a bare "Continue" rather than giving new direction the agent should proceed through diagnosis, implementation, and testing without stopping to check in at each stage. Confidence: 0.55
- During a long, multi-step debugging investigation, expects the agent to carry the work through to completion autonomously; when the agent pauses mid-analysis — including stopping to ask clarifying diagnostic multiple-choice questions — the user nudges with a bare "Continue" rather than answering or giving new direction, expecting the agent to investigate/diagnose the mechanism itself and proceed through diagnosis, implementation, and testing without stopping to check in at each stage. Confidence: 0.65
- When a full-project test run reports failures in files/areas outside the task's scope, expects the agent to verify whether each failure is pre-existing (via read-only `git status`/`git diff`/`git log`) and, if pre-existing WIP unrelated to the task, to leave it unfixed and report it accurately as such rather than expanding scope to "fix" an unrelated broken test. During the fuzz-initiative verification the full `test/ts` suite failed only on a `componentArchitecture` violation in `CaptureStitchingGuide.tsx` (a file never touched — pre-existing branch WIP); the agent confirmed non-responsibility via git and did not let it count against the task's own verification ("no new failures"), which is the accepted outcome. Confidence: 0.55
- When asked to pick a primary performance goal for a client-side optimization, wants shipped-load wins (initial page load / bundle size / first paint) addressed alongside dev hot-reload — chose "Both" over the recommended dev-only option, and the executed plan led with bundle-size/first-paint cuts (drop dead deps, shrink imports) with memoization/caching as the secondary dev-speed win. Confidence: 0.5
- Prefers removing genuinely dead dependencies/pipeline stages from the bundle entirely over keeping them with lazy-load fallbacks for hypothetical future content — e.g., chose to drop rehype-highlight + highlight.js (and its CSS) outright because the current article has zero highlightable code fences, rather than "keep but lazy-load" or "keep as-is"; dead weight in a bundle is a cost to eliminate, not a safety net. Confidence: 0.6
Expand Down Expand Up @@ -165,3 +165,16 @@ ion — the agent should proceed through diagnosis, implementation, and testing
- When display settings change while paused, wants them served through the same one-shot frame-fetch path that VFO scrolling uses — a fresh frame is requested from the device at the new settings — rather than only client-side reprocessing of the held frozen frame; the paused view is expected to reflect the actual device output at the new FFT size/window/sample rate, not a local re-derivation (chose the fetch-new-frame approach over the "reprocess frozen frame" recommendation). Confidence: 0.55
- Prefers the repo to end up in a clean lint state (zero warnings/errors) and will explicitly task the agent to sweep pre-existing lint warnings — e.g., "go over the lint warnings" was one of only two approved last-day items, and the user accepted the 214→0 sweep (including removing dead code and unused imports, not just suppressing). Warning-noise cleanup is valued deliverable work, not busywork. Confidence: 0.6
- Expects completed work to be committed as its own logically-scoped commit once verified: after the feature commit landed, the user explicitly requested "Commit the lint fixes after fixing them" — fix → verify (lint/tsc/tests green) → commit is the expected delivery flow, and the lint sweep should land as a separate commit from the feature rather than being folded in. Confidence: 0.6
- Monitors the agent's context budget and expects it to be kept under the model's input limit: when a long investigation ballooned the prompt past the max input length (repeatedly re-reading huge files like FFTCanvas.tsx/SpectrumSidebar.tsx in many slices), the user asked the agent to "try to compact right now" (and said they weren't sure why the window got so big) rather than just retrying "Continue" against an over-limit context — an explicit compaction/scope-down of the working context is expected immediately, followed by resuming the task. Confidence: 0.5
- Spectrum navigation/pan/scroll bounds must come from the device's tunable spectrum (or the global advertised spectrum bounds when device bounds haven't hydrated), never from the active channel — reported "it's currently clamped when I try to scroll, it's like not letting me scroll the spectrum away from the channel's bounds" as a bug. The VFO/viewport is a device acquisition control, so the selected channel may drive whole-channel/selection display but must not gate where the acquired window or viewport can move. Confidence: 0.6
- Treats device-level retune cost the same as frontend throttling — i.e., as artificial latency to be removed at its source, not merely capped: chose retuning in place while the RX stream keeps running (one `hackrf_set_freq`, no `stop_streaming`/`ensure_streaming` per tick, no discard/settle frames) plus a one-frame (~16 ms) publish cadence over "keep the stop/start but coalesce the restarts" or "leave the device path untouched". Accepts the higher-risk, hardware-verifiable option when it delivers the lowest-latency interaction. Confidence: 0.55
- Distinguishes an "elastic snap-back" from a plain clamp and expects the agent to find the re-application loop, not just the gesture-handler cap: when a scroll/pan visibly springs back to the channel bounds ("it like elastically snaps back to the channel bounds which renders it ineffective to browse the spectrum, something something in the code is triggering this auto-scroll/hard clamp but I can't find it"), the defect is not the one-time clamp in the wheel/drag path but a state-sync layer that re-clamps the range on every update (e.g., a store effect whose acquisition-bounds fallback re-applies the active channel's bounds on each range sync, so each scrolled range is immediately re-clamped). A fix that only removes the interaction-site clamp is an incomplete diagnosis — reported as "The first issue still was misunderstood" — so the agent must trace every place the constrained range is recomputed/enforced (resolvers, sync effects, selectors), not just the direct input handler. Reinforced by a later instance reported as a scroll trap ("I am trapped in some kind of loop, I can scroll but then it resets back to where it thinks at 651kHz after getting to like 6MHz or so"): the culprit was a reconciliation effect that rebuilt the acquisition window *with* the active channel bounds and republished whenever the span disagreed, so the window kept getting pulled back toward the channel edge and could oscillate. The accepted fix makes the reconciliation span-only and centre-anchored — position is owned by explicit channel/sample-rate actions, never re-derived by the self-heal path. Confidence: 0.6
- Toggle controls must always remain in the UI — a broken on/off *state* is fixed, never the toggle itself. When the agent read the user's choice of "remove the off state" literally and deleted the HackRF baseband filter toggle, the user immediately pushed back ("Oh no there should always be a toggle!"); the accepted design keeps the toggle and makes OFF a held/pinned state that never publishes the invalid zero width (zero stays in state as the UI sentinel but is never sent to the device). Never delete/remove an existing control or affordance as the fix for a broken state — fix the state's semantics and keep the control. Confidence: 0.85
- For a physically-coupled numeric setting, "off" must be modeled as a held/pinned state distinct from the published value: the HackRF baseband filter's OFF pins the control (so auto-tracking can't silently switch it back on) while holding a zero sentinel only in state — zero is never written to the device because it is not a valid MAX2837 width and tears the stream down. The state layer may hold values the device layer must never receive, and the agent is expected to keep those two layers separate explicitly. Confidence: 0.75
- Prefers custom values for hardware-coupled numeric inputs to stay free-form: accept any value the user types and let the driver round down to the nearest hardware-supported width, rather than restricting the input to the device's discrete valid values (chose "keep as-is (round down)" over "restrict to valid widths"). Confidence: 0.5
- When the agent flags a pre-existing, out-of-scope failing test (e.g., a stale assertion unrelated to the task) and offers to fix it as a separate decision, the user approves fixing it rather than leaving it red ("Can you fix that one liner"). The same holds for other pre-existing defects the agent surfaces but deliberately leaves alone (dead code, latent type errors, an unused exported helper): the user green-lights the cleanup with a terse "Yeah let's clean that up" once it's raised. Pre-existing problems should be surfaced as an explicit fix-or-leave choice and reported accurately instead of being silently fixed or ignored, with the user typically approving the cleanup once asked. Confidence: 0.6
- Keeps real SDR hardware on hand and connects it specifically so the agent can verify fixes against the actual device (announced "I've connected the HackRF One" right after approving the fix) — expects end-to-end verification on physical hardware to be the goal once the device is available, not just unit tests. Confidence: 0.5
- Wants cleanup to remove genuinely dead code but fix the root cause rather than trimming symptoms: truly unreferenced code (a helper whose only reference was its own self-satisfying test) is deleted, while functionality that is documented and still consumed (CLI commands whose modules a linter had misclassified as unreachable and wrongly deleted) is restored verbatim and the misclassifying linter is fixed so it won't re-delete them — chosen over retiring the documented commands or leaving the area broken. Cleanup is expected to be made durable (e.g., adding the previously-uncompiled `scripts/` dir to `tsconfig`'s `include` so the type errors surface in the normal `typecheck` gate), and the work verified with the standard gates (typecheck, lint, full jest suite) plus a confirmation that restored files are byte-identical to their pre-deletion version. Confidence: 0.55
- Wants interactive toggles/state switches (e.g., the Rx/Tx "opacity switch") to hold exactly one state change per click: a control "supposed to hold 1 state/click so one can toggle back and forth between rx and tx" must register every click, and any throttle/debounce/guard that swallows rapid clicks is a defect — a switch that misses clicks when swapping back and forth is simply "not working." Confidence: 0.7
- Never gate a stop/halt action behind a guard: Stop (e.g., "Stop Tx") must always be allowed to fire regardless of the current state — "always allow Tx to be stopped and don't do any switch/click guard over that state ever." Confidence: 0.65
- Prefers an explicit mode toggle to behave like plain play/pause and let the state stand where it is rather than auto-resuming: leaving Tx and returning to Rx should land on a held-paused state with matching chrome (button reads the resume label, paused banner shown) and stream again only on an explicit click — silently auto-playing/auto-resuming on a mode transition is wrong, and the label plus paused overlay must reflect the real frozen stream on the first render. A transition that needs one click to correct the chrome and another to actually act is reported as a "ghost state." Confidence: 0.6
83 changes: 83 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
version: 2

# Several decisions here mirror constraints that already exist in the repo:
#
# * `.npmrc` sets `min-release-age=7`, so Dependabot is given the same 7-day
# cooldown rather than proposing versions the install would refuse.
# * The repo pins exact versions (`save-exact=true`), so version updates are
# expected to arrive as explicit bumps rather than range widenings.
# * `mock_apt_metal` and the WASM crate train move in lockstep, so related
# packages are grouped to avoid half-applied upgrades.
# * Note: `Cargo.lock` is currently gitignored (see .gitignore). Dependabot's
# cargo support expects a lockfile it can reason about, so that entry may not
# produce useful PRs until Cargo.lock is committed.
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
cooldown:
default-days: 7
open-pull-requests-limit: 5
commit-message:
prefix: "chore(deps)"
groups:
# The vitest cluster must move as one unit: the coverage and browser
# providers peer on the vitest major.
vitest:
patterns:
- vitest
- "@vitest/*"
# These trios are version-locked in practice.
react-router:
patterns:
- react-router
- "@react-router/*"
tailwind:
patterns:
- tailwindcss
- "@tailwindcss/*"
radix:
patterns:
- "@radix-ui/*"
playwright:
patterns:
- playwright
- "@playwright/test"
# Type packages trail their runtime by design (e.g. @types/three must not
# outrun three), so keep them together rather than interleaved.
types:
patterns:
- "@types/*"
minor-and-patch:
applies-to: version-updates
update-types:
- minor
- patch

- package-ecosystem: cargo
directory: /
schedule:
interval: weekly
day: monday
cooldown:
default-days: 7
open-pull-requests-limit: 5
commit-message:
prefix: "chore(deps)"

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
cooldown:
default-days: 7
open-pull-requests-limit: 5
commit-message:
prefix: "chore(ci)"
groups:
actions:
patterns:
- "*"
8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,8 @@ redis/backups
.redis_data/
.redis_data

# Local rebuild metadata
.rebuild_status.json
.n-apt-build-orchestrator.lock
.n-apt-backend-handoff-proxy.lock
.n-apt-backend-target.json
.n-apt-backend-target.json.tmp
# Local app runtime metadata
.n-apt/

# Rust-specific files
*.pdb
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
26
23 changes: 0 additions & 23 deletions Cargo-wasm.toml

This file was deleted.

36 changes: 22 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ mock_apt_metal = ["dep:metal"]
rustfft = "6"
num-complex = "0.4"
rand = ">=0.10.1, <0.11"
paste = { package = "pastey", version = "0.1.1" }
paste = { package = "pastey", version = "0.2.3" }
rayon = "1.12"
getrandom = { version = "0.4", features = ["wasm_js"] }
anyhow = ">=1.0.103, <2"
Expand All @@ -152,39 +152,39 @@ clap = { version = "4.6", features = ["derive"] }
byteorder = "1.5"
env_logger = "0.11"
dotenvy = "0.15"
wasm-bindgen = "0.2.121"
wasm-bindgen = "0.2.128"
serde-wasm-bindgen = "0.6"

# WASM dependencies (only when targeting WASM)
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3.98"
web-sys = { version = "0.3.98", features = ["console"] }
js-sys = "0.3.105"
web-sys = { version = "0.3.105", features = ["console"] }

[target.'cfg(target_os = "macos")'.dependencies]
metal = { version = "0.31", optional = true }
metal = { version = "0.33", optional = true }

# Server dependencies (only when not targeting WASM)
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.52", features = ["full"] }
tokio-util = { version = "0.7", features = ["io"] }
tokio-tungstenite = "0.29"
redis = { version = "1.6", features = ["aio", "tokio-comp"] }
tokio-tungstenite = "0.30"
redis = { version = "1.7", features = ["aio", "tokio-comp"] }
env_logger = "0.11"
futures-util = "0.3"
chrono = { version = "0.4", features = ["serde", "wasmbind"] }
# Encryption: AES-256-GCM for payload encryption, HMAC/PBKDF2 for auth
aes-gcm = "0.10"
aes-gcm = "0.11"
hmac = "0.13"
sha2 = "0.11"
pbkdf2 = { version = "0.13", features = ["password-hash"] }
base64 = "0.22"
base64 = "0.23"
zip = { version = "8", default-features = false }
# HTTP client for CoreML service communication
reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] }
# HTTP + WebSocket server (axum replaces raw TcpListener + tokio-tungstenite)
axum = { version = "0.8", features = ["ws"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "set-header", "compression-gzip"] }
tower-http = { version = "0.7", features = ["cors", "set-header", "compression-gzip"] }
# WebAuthn passkey authentication
webauthn-rs = { version = "0.5", default-features = false }
webauthn-rs-proto = "0.5"
Expand All @@ -195,24 +195,24 @@ notify = "8.2"
serde_yaml = "0.9"
regex = "1"
# Input validation and sanitization
validator = { version = "0.20", features = ["derive"] }
validator = { version = "0.21", features = ["derive"] }
indexmap = { version = "2.14", features = ["serde"] }
crossbeam-channel = "0.5"
yawc = { version = "0.3.3", features = ["axum", "zlib"] }
yawc = { version = "0.4", features = ["axum", "zlib"] }
tempfile = "3"
serde_urlencoded = "0.7"
rusb = "0.9"

[dev-dependencies]
doc-comment = "0.3"
tempfile = "3"
serial_test = "3.2"
serial_test = "4"
criterion = { version = "0.8", features = ["html_reports"] }
proptest = "1.6"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio-test = "0.4"
axum-test = { version = "20", features = ["ws"] }
axum-test = { version = "21", features = ["ws"] }
tower = "0.5"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
Expand Down Expand Up @@ -257,3 +257,11 @@ overflow-checks = false
[[bin]]
name = "test_fft"
path = "src/rs/bin/test_fft.rs"

# `metal` (used by the `mock_apt_metal` feature) depends on `block`, whose latest
# release 0.1.6 trips the deny-by-default `uninhabited_static` future-incompat lint
# (`static _NSConcreteStackBlock: Class` with an empty `enum Class { }`). No upstream
# release fixes it, so vendor a one-line patched copy (opaque struct instead of an
# uninhabited enum). Drop this once `metal` no longer pulls in `block`.
[patch.crates-io]
block = { path = "vendor/block" }
Loading
Loading