Split the operator TUI out of the detector's supply chain - #19
Merged
Conversation
`flyingsquirrel-watch` shares no code with the detector — it reads the
JSON event log with its own permissive structs, deliberately, so a schema
change can't break it. But as a `[[bin]]` in the root package its
dependency tree was welded to the safety-critical one, and that had real
costs:
* `ratatui -> lru 0.12` put two unsoundness advisories
(RUSTSEC-2026-0002, -0253) into the detector's `cargo audit`. Both
were suppressed with the rationale "ratatui's widget cache, NOT the
detector." True, and unenforced — nothing in the build made it so.
* `ratatui -> instability -> darling` was the SOLE reason the declared
MSRV was 1.88 rather than 1.85.
* A terminal UI rode along in every ARM cross-build, the Docker image,
and `fuzz/Cargo.lock` — the NMEA/MAVLink fuzz harness was receiving
Dependabot PRs for a TUI library, which is how the coupling surfaced.
Move it to `watch/` as a detached workspace with its own lockfile, the
same pattern (and for the same reason) as `fuzz/`. A workspace MEMBER
would not do: members share one lockfile, so `lru` would stay in the
detector's audit scope and the exercise would be pointless.
Result: 34 crates leave the detector (288 -> 254). `lru`, `ratatui`,
`crossterm`, `darling`, `instability` are gone from both the detector's
and the fuzz harness's lockfiles, so the two `lru` ignores are DELETED
from ci.yml rather than argued. They now live in a `watch` job scoped to
the crate that actually pulls them — the boundary is structural.
MSRV stays 1.88. I re-derived it instead of assuming it dropped: with
ratatui gone the binding constraint moved to `nmea -> serde_with ->
time 0.3.47`, verified by a reachability walk over `cargo metadata
--filter-platform` excluding dev-deps. That is a core functional
dependency rather than a dashboard, which is the defensible place for it.
Also in this pass:
* Dependency bumps that were open as separate PRs, applied together so
the tree resolves once: toml 0.8 -> 1.1 and thiserror 1.0 -> 2.0
(both major), tokio-stream, async-trait. Full suite green on both.
* deploy/Dockerfile: the builder image had been bumped 1.88 -> 1.97
while still carrying an "MSRV = 1.88, keep in sync" comment. The
image tracks a current toolchain on purpose; proving the MSRV is the
CI `msrv` job's duty. Split the two so a base-image bump can't
silently invalidate the claim again.
* Delete docs/sitl_driver.py — a deprecated prototype, superseded by
deploy/sitl/sitl_harness.py, guarded to refuse to run, referenced by
nothing. Git history keeps it.
* README: document the dashboard (a shipped binary that had no docs at
all) and correct the now-stale MSRV rationale.
Verified: detector fmt + clippy(-D warnings, --all-targets
--all-features) + 182 lib + 35 integration across all 14 suites + 10
proptests; watch crate fmt + clippy + 4 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Found auditing the repo as a whole rather than file-by-file — it is invisible from inside any single file.
flyingsquirrel-watchshares no code with the detector. It reads the JSON event log with its own permissive structs (deliberately, so a schema change can't break it). Yet as a[[bin]]in the root package, its dependency tree was welded to the safety-critical one:ratatui → lru 0.12put RUSTSEC-2026-0002 and -0253 in the detector'scargo audit. Suppressed as "ratatui's widget cache, NOT the detector" — true, but nothing in the build enforced itratatui → instability → darlingwas the sole reasonrust-versionwas 1.88 rather than 1.85fuzz/Cargo.lockThe last one is how it surfaced: the NMEA/MAVLink fuzz harness was getting Dependabot PRs for a TUI library (#17, ratatui 0.30). A security tool that decodes wire bytes had no business tracking a dashboard's dependencies.
What
Move it to
watch/as a detached workspace with its own lockfile — same pattern, same reason, asfuzz/.A workspace member would not work: members share one
Cargo.lock, solruwould remain in the detector's audit scope and the whole exercise would be pointless. That's why this is a detached crate.Result: 34 crates leave the detector (288 → 254).
lru,ratatui,crossterm,darling,instabilityare gone from the detector's and the fuzz harness's lockfiles — so the twolruignores are deleted fromci.ymlrather than argued. They now live in a newwatchCI job scoped to the crate that actually pulls them. The boundary is structural instead of asserted.MSRV: re-derived, not assumed
It stays 1.88. I expected it to drop to 1.85 and checked rather than claiming it — with ratatui gone the binding constraint moved to
nmea → serde_with → time 0.3.47. Verified by a reachability walk overcargo metadata --filter-platform x86_64-unknown-linux-gnuexcluding dev-deps, because the flat package list includes unlinked entries and gives the wrong answer.That's a better place for the floor: a core functional dependency rather than an operator dashboard.
Also in this pass
toml0.8 → 1.1 andthiserror1.0 → 2.0 (both major), plustokio-streamandasync-trait. Full suite green on both majors. This supersedes Bump crossterm from 0.28.1 to 0.29.0 #8, Bump tokio-stream from 0.1.18 to 0.1.19 #9, Bump toml from 0.8.23 to 1.1.3+spec-1.1.0 #10, Bump async-trait from 0.1.89 to 0.1.92 #11, Bump crossterm from 0.28.1 to 0.29.0 in /fuzz #14, Bump thiserror from 1.0.69 to 2.0.18 #16, Bump ratatui from 0.29.0 to 0.30.2 in /fuzz #17.deploy/Dockerfile: the builder image had been bumped 1.88 → 1.97 while still carrying anMSRV = 1.88, keep this in synccomment — the exact drift risk flagged when that bump was taken. The image tracks a current toolchain on purpose; proving the MSRV is the CImsrvjob's duty. Split the two so a base-image bump can't silently invalidate the claim again.docs/sitl_driver.py— a deprecated prototype, superseded bydeploy/sitl/sitl_harness.py, guarded to refuse to run, referenced by nothing. Git history keeps it.Verified
cargo fmt --check,clippy --all-targets --all-features -D warnings, 182 lib tests, 35 integration tests across all 14 suites (run individually), 10 proptests.watch/:cargo fmt --check,clippy --all-targets -D warnings, 4 tests.lru/ratatui/crossterm/darling/instabilityabsent from bothCargo.lockandfuzz/Cargo.lock.Not changed
No detector logic. Zero lines of detection, nav, MAVLink, or hardware-ingest code were touched — this is packaging, dependency scope, and docs.
🤖 Generated with Claude Code