Add rustfmt, clippy, type-check and test gates to CI - #60
Merged
Conversation
Four gates the workflow was missing: - cargo fmt --all -- --check - cargo clippy --workspace --all-targets -- -D warnings - npm run check (svelte-check + tsc) - cargo test for cockpit/ui/src-tauri The last one closes a real hole. cockpit/ui/src-tauri declares its own empty [workspace] table, so it is a standalone workspace that the root manifest does not list as a member — 'cargo test --workspace' run from the repo root never reached its tests, and that suite (the app-plugin state machine, manifest validation, discovery) had only ever been run by hand. The fmt and clippy gates run once per manifest for the same reason. Compiling the tauri crate needs the WebKitGTK system deps the build job already installs, plus the fleetd sidecar binary, because tauri-build resolves the externalBin resource at compile time and hard-errors without it. Both the lint and cockpit-test jobs therefore repeat that setup.
Pure rustfmt output, no behavior change — the new 'cargo fmt --all -- --check' gate's baseline. Neither workspace had ever been run through rustfmt, so this is the whole backlog in one pass; there is no rustfmt.toml, so these are the stock defaults. Mostly struct literals and multi-argument calls being split across lines.
The root workspace was already clean under '-D warnings'; the cockpit crate had never been linted and had twelve findings. Three were real and are fixed: a nonminimal_bool in the discovery filter (the pruned test is now a named binding rather than a negated conjunction) and two unnecessary_to_owned in the check_crash tests. The other nine are dead_code on plugin-runtime scaffolding that is deliberately ahead of its caller — the Phase-6 embedding layer that will read the webview manifest surface, the crash watcher that will poll Spawner::has_exited via check_crash, and the STOPPED rest state. Deleting any of it would amputate an in-flight feature, so each site gets a narrowly scoped #[allow(dead_code)] with a comment naming the caller it is waiting on. No blanket allows, and nothing at module or crate level.
The tmp() helper keyed its directory on the process id alone, so all four local_projects tests shared one path while running concurrently in the same test binary. One test's remove_dir_all raced another's create_dir_all and the run failed with AlreadyExists or PermissionDenied — 5 times in 20 on Windows. Pre-existing, and invisible until now because these tests were never run by CI. The new 'cargo test (cockpit)' gate would have inherited it as a ~25% flake rate, so: an atomic counter makes each directory unique. 0 failures in 30 runs after. Test-only change, no production code touched.
keeps_absolute_cwd_as_is leaned on the AUDIENCE_JSON fixture's 'D:/…' cwd. That is only an absolute path on Windows; everywhere else Path::is_absolute says no, resolved_cwd joins it to the manifest dir, and the assertion fails. Production code is fine — is_absolute is doing exactly its job. Only the test baked in a platform. It now picks an absolute path the host agrees with. Surfaced by the new 'cargo test (cockpit)' gate, which is the first time this suite has ever run on Linux.
Fifth gate: 'npm test' in cockpit/ui, 91 tests across 13 files. Same shape as the svelte-check job — node only, no Rust, no system deps. Closes a half-gated pair rather than just a coverage gap. The regression pin for the plugin_launch main-thread freeze is split across two suites: a Rust test, now covered by 'cargo test (cockpit)', and cockpit/ui/src/App.appPlugin.test.ts, covered here. That file is not on main yet — it lands with the plugin-runtime work — so this job gates 91 tests today and picks up the JS half of the pin the moment it merges. Wiring it now means the pin is never half-enforced.
Four of the allows I added claimed the code was waiting on the Phase-6 embedding layer. Verified by grep across origin/main and origin/feat/plugin-runtime — that is false for these: state::check_crash only its own two unit tests call it Manifest::webview read only by test assertions WebviewCfg (all fields) read only by test assertions Manifest::window_title one test assertion, no production caller Spawner::has_exited reachable only via check_crash, so dead with it These are unimplemented features with no caller anywhere, not code whose caller is en route. A plugin author setting 'popups: block' today gets no blocking. The allows stay — deleting the code would amputate work in flight on #49 — but the comments now say plainly that nothing calls this on any branch, so the allow cannot quietly become the permanent resting place of two unbuilt features. PluginManager::url_for keeps its original comment: that one is genuinely waiting on a caller, verified at embedding.rs:99 on feat/plugin-runtime. Comments-only change, no code touched.
adbarc92
marked this pull request as ready for review
August 15, 2026 17:14
adbarc92
added a commit
that referenced
this pull request
Aug 15, 2026
Bootstrap run of the testing-plan skill against a3edc78. Scores 131 gaps on likelihood x impact across seven test tiers, records a trust verdict per runner, and ranks what to close next. The load-bearing discovery is that five of the seven automated tiers did not run in CI: 135 vitest tests, 28 Tauri-host Rust tests, 52 session-state tests and 53 pytest tests were advisory, not gating. `cargo test --workspace` never reached the cockpit crate at all, because `cockpit/ui/src-tauri/Cargo.toml` opens with a bare `[workspace]` and the root manifest lists only crates/fleet-core and crates/fleetd. GAP-057, GAP-111 and GAP-113 are the entries for that; PR #60 closes all three, so those rows and the tier map's `in_ci` flags are already stale and will re-derive on the next run. The app-plugin runtime is a deliberate scope carve-out (section 2): targeted tests for the plugin_launch freeze and the Gate-5 teardown lifecycle were being written concurrently, so GAP-005/009/010 are recorded as context rather than as gaps to act on. The preceding commit is that work; R5 releases them back into the ranking. Six items await human ratification (section 3), including the `spine_weight` table -- which is the Impact axis of every score in the file and is currently a first-run proposal, not a ratified input.
adbarc92
added a commit
that referenced
this pull request
Aug 15, 2026
#60 added `cargo fmt -- --check` to CI and applied the formatter to both workspaces on main. This branch predates that gate, so its own additions -- the Gate-5 tests from 0d05f55, the audience-manifest assertions, and the LRU touch in embedding.rs -- were never formatted and would fail the new check job. Whitespace only, stock defaults, no rustfmt.toml. Verified after: fmt --check clean on both manifests, clippy --all-targets -D warnings clean, cargo test 34+2 (cockpit) and 120 (workspace), npm run check 353 files 0/0, vitest 135 in 19 files.
adbarc92
added a commit
that referenced
this pull request
Aug 15, 2026
…n pass Rewrites the State summary in place and appends a session-log entry. The audit that opened this session found STATUS.md two sessions behind: it was silent on the 8/13 testing plan, the Gate-5 tests, and PR #60 entirely. Also records two wrong predictions so they are not re-derived -- the #60/#49 merge-tree conflict forecast that did not materialise, and the `gh pr merge --delete-branch` failure that was only a local-branch delete failing after a successful merge.
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.
CI gated on three things: the embargo guard,
cargo test --workspace, and the cross-platformtauri buildmatrix. No linting, no formatting check, no frontend gate of any kind — and, it turns out, no coverage of the cockpit crate's tests or the vitest suite. This adds five jobs and clears what they surfaced.All 9 jobs green on this branch, including the untouched 3-OS build matrix.
The two-workspace problem
cockpit/ui/src-tauri/Cargo.tomlopens with an empty[workspace]table, making it a standalone cargo workspace. The root manifest lists onlycrates/fleet-coreandcrates/fleetdas members. Nothing invoked from the repo root — not--workspace, not--all— reaches that crate.The existing
cargo test (workspace)job therefore ran 120 tests fromcrates/and zero of the cockpit crate's 26. That suite covers the app-plugin state machine, manifest validation and discovery, and until now was verified only by hand. Every Rust gate below runs once per manifest for the same reason.Gates added
cockpit/ui/src-taurifmt + clippycargo fmt -- --check--manifest-path)fmt + clippycargo clippy --all-targets -- -D warningsworking-directory)svelte-check + tscnpm run checkvitest (cockpit/ui)npm testcargo test (cockpit)cargo testnpm run checkresolves tosvelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json. There is no ESLint in this project, so there is no lint gate to add on the frontend side.The vitest job closes a half-gated pair, not just a coverage gap: the regression pin for the
plugin_launchmain-thread freeze is split across a Rust test (now covered bycargo test (cockpit)) andcockpit/ui/src/App.appPlugin.test.ts(covered here). That file is not onmainyet — it lands with the plugin-runtime work — so this job gates 91 tests today and picks up the JS half of the pin the moment it merges.Compiling the tauri crate needs the WebKitGTK system deps the
tauri buildjob already installs, plus the fleetd sidecar binary —tauri-buildresolves theexternalBinresource at compile time and hard-errors without it. Both the lint and cockpit-test jobs repeat that setup (apt deps,npm ci,npm run sidecar). rustfmt needs none of it, so both fmt checks run first and fail fast.Existing jobs are untouched;
buildstill gates ontestonly.Debt cleared
style: apply cargo fmt— neither workspace had ever been run through rustfmt, so this is the entire backlog in one pass: 34 files, +2486/-642, all whitespace. There is norustfmt.toml; these are stock defaults, mostly struct literals and multi-arg calls being split across lines. Kept in its own commit so it does not bury the real fixes.fix(cockpit): clear clippy debt— the root workspace was already clean under-D warnings. The cockpit crate had twelve findings. Three were real:nonminimal_boolin the discovery filter — the pruned test is now a named binding instead of a negated conjunctionunnecessary_to_owned(&"audience".to_string()) in thecheck_crashtestsThe other nine are
dead_code, each carrying a narrowly scoped#[allow(dead_code)]with a comment. No blanket allows, nothing at module or crate level. Deleting the code would amputate work in flight on #49, so it stays — but the comments distinguish two very different situations, verified by grep acrossorigin/mainandorigin/feat/plugin-runtime:Genuinely awaiting a caller (verified to exist):
PluginManager::url_forembedding.rs:99onfeat/plugin-runtimeUnwired features with no caller on any branch — read only by test assertions, if at all. The comments say so plainly, so the allow cannot become the permanent resting place of unbuilt work:
state::check_crashSpawner::has_exitedcheck_crash, so dead with it; the real impl has never run outside a testManifest::webviewWebviewCfg(popups,external_links,title)popups: blockgets no blockingManifest::window_titleNot independently verified — comments left as originally written, and each already states plainly that nothing reads the item today:
Running::child_id(written at launch, never read back),Lifecycle::managed(accepted, not acted on),Spawner::kill(documented force-kill fallback),state::STOPPED(rest state, never emitted).test(cockpit): give each local_projects test its own temp dir— a pre-existing flake the new gate would have inherited.tmp()keyed its directory on the process id alone, so all fourlocal_projectstests shared one path while running concurrently in the same binary; one test'sremove_dir_allraced another'screate_dir_all. Measured 5 failures in 20 runs on Windows before, 0 in 30 after. An atomic counter makes each directory unique. Test-only, no production code touched.test(cockpit): make the absolute-cwd test pass off Windows— caught by the first CI run, which is the first time this suite has ever executed on Linux.keeps_absolute_cwd_as_isleaned on theAUDIENCE_JSONfixture'sD:/…cwd, whichPath::is_absoluteonly calls absolute on Windows; elsewhereresolved_cwdcorrectly joins it to the manifest dir and the assertion fails. Production code is right — only the test baked in a platform. It now picks an absolute path the host agrees with.Verification
Green in CI on ubuntu, and run locally on Windows against the final tree:
cargo fmt --all -- --check(both)cargo clippy --workspace --all-targets -- -D warningscargo clippy --all-targets -- -D warnings(cockpit)npm run checkcargo test --workspacecargo test(cockpit)npm testBehavior is unchanged: the clippy fixes are semantics-preserving rewrites, both test fixes touch only test code, and the comment corrections touch only comments. Test counts differ from the operator's hand-recorded figures (28 Rust / 135 JS) because those were taken on
feat/plugin-runtime; this branch is cut fromorigin/main.Note for the operator
No
rustfmt.toml. Stock defaults are what the new gate enforces.use_small_heuristics = "Max"would have roughly halved the formatting diff (measured: +1185/-483 vs +2486/-642) by preserving the compact struct literals the code was written with — worth a look if that style was deliberate, but it is a config choice for the repo owner, not one to make inside a CI PR.The 34-file formatting commit will conflict with
feat/plugin-runtime. That is known and left for the operator to sequence; nothing on that branch was touched here.