ci: dependabot NOTICE auto-heal, always-on size gate, honest hermetic matrix, remove-by wired - #554
Merged
Conversation
… matrix, remove-by wired Closes the 2026-08-31 audit's four CI holes (two by change, two verified already closed on this base): 1. Dependabot NOTICE starvation: new `dependabot-notice` job in validate.yml regenerates NOTICE on Dependabot's PR branch with the exact recipe the repo-guards gate uses, pushes it back (contents: write, job-scoped), and re-dispatches Validate on the healed head because GITHUB_TOKEN pushes never trigger workflow runs. 27 Dependabot PRs were rotting on the gate. 2. File-size gate on Rust-only PRs: verified ALREADY CLOSED on this base - `Check file sizes` lives in the unfiltered repo-guards job (moved there 2026-08-14 after #423/#430); confirmed green in live runs. No change. 3. Hermetic vacuous green on push/schedule: the unconditional-matrix half is ALREADY CLOSED (the `!cancelled()` wrapper; live push/schedule runs build for 19-23 min). Remaining half shipped here: 3-attempt retry around both pnpm installs (root + mcp-4da-server) - better-sqlite3 is a direct dep of both, and its 13.x node-gyp fallback flakes on windows-latest. 4. check-remove-by.cjs (#421) wired into repo-guards with --ci annotations; it previously ran nowhere (package.json, husky, and CI all clean of it). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01LrXvdHoDGUj99Fqf1fCYJY
runyourempire
enabled auto-merge (squash)
August 30, 2026 16:02
runyourempire
added a commit
that referenced
this pull request
Aug 31, 2026
…l cause, and dev kills stop hitting peers (#566) Three items from the approved 2026-08-31 recommendation set (recs #4 and #6; #1/#2/#5 are sequenced behind in-flight lanes, #3 behind #554). ## 1. Truthful AI usage panel `UsageStatsSection` rendered `settings.usage` — the **rerank-only ledger** — as "cost today". After #553 split usage recording per feature, that under-reported real spend ~10-30x while the true totals sat dark (`get_ai_usage_summary` existed with zero frontend callers). The panel now shows: - **Cost today vs the configured daily cap** from `get_llm_usage` (global ledger: all features, restart-seeded, millicent-backed), with an 80%/limit color ramp - **Tokens today** from the same ledger - **This month total + per-feature breakdown** from `get_ai_usage_summary` — "what is burning tokens" becomes answerable from Settings instead of SQL New `usage` i18n namespace translated for all 13 locale dirs; unknown `task_type` tags render raw rather than hiding spend. 4 component tests pin the rerank-ledger regression (absurd sentinel values in `settings.usage` must never render). ## 2. Sentinel classifies by meaning, not text Live false positive: a cold `tsc` exceeded the scan's 60s timeout → zero `error TS` lines → "TypeScript compilation failed **(0 errors)**" reported as CRITICAL, deploying an expert against a compiler that never finished. `classifyTscResult` (pure, exported) now goes critical only on actual error lines; timeouts and spawn failures become "check inconclusive — <real cause>" warnings. Same treatment for the Rust branch's non-zero-exit-no-errors path. 5 negative tests per the gate-precision doctrine (`recipe-gate-precision-negative-test`); the script gains a `require.main` guard so tests import it without executing a scan. ## 3. Path-scoped dev kills `taskkill /F /IM fourda.exe` kills every fourda on the machine — two scheduled background-refresh engine runs were killed mid-cycle **today** as collateral from a peer lane's dev restart (task result 0xFFFFFFFF). `scripts/stop-fourda.ps1` stops only instances launched from the caller's tree's `src-tauri/target/`, supports `-Root`/`-DryRun`, and lists what it deliberately left running. CLAUDE.md now prescribes it and bans the image-name kill. ## Verification - vitest: **1,248/1,248**; `tsc --noEmit` clean - `node --test scripts/sentinel-scan.test.cjs`: 5/5; live `--quick` scan runs green through the modified script - `i18n-guard.cjs` clean across all 13 locales; dry-run of `stop-fourda.ps1` verified against live processes 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019RTCgzAcaopjz3KZnaqNrj Co-authored-by: Claude Fable 5 <[email protected]>
runyourempire
added a commit
that referenced
this pull request
Aug 31, 2026
`.husky/commit-msg` guards locally authored messages; `.husky/pre-push` guards outgoing commits. Neither runs when GitHub performs a SQUASH MERGE, because that commit message is composed server-side from the PR title and body. That gap has already fired. Two commits on public `main` — `d5df9e34` (#409) and `053e5813` (#362) — carry the private external-verifier name in their MESSAGES. Removing those two messages now needs a public-history rewrite. This stops the next one, which is the part still in our control. The check runs on `pull_request`, where the title/body are known BEFORE the merge button, and scans title + body + every commit message in the range via the existing hashed detector in scripts/private-asset-guard.cjs (no literal name is added anywhere). Wired into `validate-success`'s `needs`, so it gates through the existing required check — no branch-protection change required. Rebased 2026-08-31 onto the post-#554/#569 validate.yml: the job is re-sited at the end-of-`changes` boundary (the exact seam #554's comment reserved for this PR), pins match main's checkout SHA, and `validate-success.needs` now carries all eight legs including `pr-metadata`. MERGE-QUEUE SAFE, verified explicitly: `github.event.pull_request` does not exist on merge_group entries, so the job's `if: github.event_name == 'pull_request'` SKIPS it there — the identical guard the `changes` detector has run live on every queue entry since #569 — and Validate Success's allowlist counts skipped as pass. The queue never waits on metadata that does not exist; the pull_request run that armed the merge has already scanned it. Two deliberate choices, unchanged from the original: * FAIL-CLOSED. A guard that silently cannot run is not a guard — and silent failure is the exact bug class this check exists to stop. * Title and body reach the script through the ENVIRONMENT, never `${{ }}` inlined into the `run:` block. PR text is attacker-controlled; inlining it is a script-injection primitive. Verified end-to-end against the real detector on 2026-08-31: * range containing `d5df9e34` -> BLOCKED, naming the offending commit (exit 1) * clean range (`adfb68ff`) -> passes (exit 0) * 7/7 unit tests (injected predicate, so no private literal in a tracked test) * workflow YAML parses; 53 inline scripts parse; `validate-success.needs` includes `pr-metadata`; #554's dependabot-notice/repo-guards and #569's merge_group trigger intact Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01LrXvdHoDGUj99Fqf1fCYJY
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.
Lane D of the 2026-08-31 audit remediation: four known CI holes, each re-verified against live state before touching anything. Two needed changes (shipped here); two turned out to be already closed on the current base — documented below so the audit trail reflects what was actually found, not what the brief assumed.
Hole 1 — Dependabot NOTICE starvation (FIXED: new
dependabot-noticejob)The hole.
Repo guardsrequiresnode scripts/generate-notice.cjs --check --requireto pass on every PR: third-party attribution must be regenerated in the same change as any dependency move. Dependabot cannot run repo scripts, so every cargo or production-npm bump it opens failsThird-party attribution is current (NOTICE)and rots. Verified live at step level before fixing: PRs #457 (cargozip) and #516 (npmreact) both fail exactly that step, every other repo-guards step green. 27 Dependabot PRs are currently open, several ecosystems pinned at theiropen-pull-requests-limit— the gate had starved automated dependency updates entirely.The fix. A
dependabot-noticejob in validate.yml, gatedgithub.event_name == 'pull_request' && github.actor == 'dependabot[bot]', that completes the commit on Dependabot's behalf:github.event.pull_request.head.ref), not the unpushable detached merge ref. Dependabot branches always live in this repo, never a fork, so the job token can push them.cargo metadata,pnpm install --frozen-lockfile --ignore-scriptsforpnpm licenses list), so what it generates is byte-for-byte what--checkwill verify. Verified end-to-end locally: that recipe followed by write-modegenerate-notice.cjsreproduces the committed NOTICE with zero diff on a current tree, andgit diff --quiet -- NOTICEis the correct changed-detector.Three GitHub mechanics are load-bearing and documented in the job comment:
GITHUB_TOKENregardless of repo defaults. The job-levelpermissions:block (contents: write,actions: write) elevates this job only; every other job keeps its defaults.GITHUB_TOKENnever trigger workflow runs (GitHub's recursion guard). Without compensation the healed commit would sit forever with noValidate Successon it — and the main ruleset's sole required check (verified viarules/branches/main:Validate Success, strict) reads the PR's head SHA.workflow_dispatchis the documented exception to the recursion guard, and validate.yml already runs its full suite on dispatch (the heavy legs'github.event_name == 'workflow_dispatch'clauses), so the job dispatches Validate on the healed branch tip. Its check runs attach to the new head SHA, which is what the merge gate reads. Hermetic is deliberately NOT re-dispatched: the healed commit differs from the already-hermetic-tested head by NOTICE text only,Hermetic Successis not a required check, and a ~20-min two-OS cacheless matrix would spend metered hosted minutes to learn nothing.pull_requestnor the Dependabot actor test holds). If Dependabot force-pushes a rebase, the NOTICE commit is dropped — but that push is a Dependabot-actoredpull_requestevent, so the heal simply runs again on the new head. A push race with a Dependabot rebase is rejected non-fast-forward and self-corrects the same way.validate-successnow includesdependabot-noticeinneeds: it skips (== pass) on every non-Dependabot PR, but a heal failure on a Dependabot PR is visible in the aggregate instead of vanishing.Expected behavior on a healed PR: the original run's
Repo guardsstill fails against the pre-heal commit (correct — that SHA is stale), and the dispatched run on the healed head goes green and satisfies the ruleset. Bumps that don't move the shipped closure (dev-dep and actions bumps — verified on #517/#518) regenerate to no diff, push nothing, and dispatch nothing.Hole 2 — file-size gate skippable on Rust-only PRs (VERIFIED ALREADY CLOSED — no change)
The brief said
Check file sizesruns inside the path-filtered Frontend job. Checked against the current base before acting: it was moved torepo-guardson 2026-08-14 (the job's own comment documents the #423/#430 incident), the Frontend job carries a tombstone comment pointing there,repo-guardshas noif:and noneeds:so it runs on every PR and dispatch, and live Dependabot runs showCheck file sizesexecuting and passing insideRepo guards.node scripts/check-file-sizes.cjs --ciexits 0 on this tree. Nothing to do; recorded here so the hole is closed with evidence rather than by assumption.Hole 3 — Hermetic vacuously green on main (HALF ALREADY CLOSED, other half FIXED: install retry)
Vacuous-green half: already closed. The
!cancelled()wrapper onfresh-clone'sif:(documented in-file, with the 2026-08-13/14 observations of 7-9s "successes") already forces the full matrix on push/schedule/dispatch; the path filter applies only topull_request. Verified against live runs, not just the YAML: the latestpushrun built for ~19 min and the latestschedulerun for ~23 min — real two-OS cold builds, not 1-second skips. No change.Retry half: shipped.
pnpm install --frozen-lockfilenow retries up to 3 attempts (20s backoff,::warning::per miss,::error::on exhaustion) in both places that build better-sqlite3 natively:mcp-4da-serverinstall line (a direct dependency there too — the open 13.x bump PR chore(mcp-4da)(deps): bump better-sqlite3 from 12.11.1 to 13.0.3 in /mcp-4da-server #446 targets exactly that package; only the install line is retried, build and tests stay single-shot because a failure there is real signal).better-sqlite3 falls back to a node-gyp source build whenever no prebuilt binary matches the image's Node ABI (the 13.x line in particular), and that native build flakes on windows-latest. Three attempts separates network/toolchain weather from a genuine fresh-clone break; anything still failing after 3 tries IS the finding.
Hole 4 —
check-remove-by.cjsunenforced (FIXED: wired intoRepo guards)Shipped in #421 and wired to nothing — verified by grep across package.json,
.husky/, and.github/: zero references. (.husky/pre-commitruns the staged-files-only cousincheck-dead-code-expiry.cjs, which covers only#[allow(dead_code)]; the whole-treeREMOVE BY <date>gate never ran anywhere, so an expired marker could never fail anything.) Now a step inrepo-guards— the unfiltered job that runs on every PR — invoked asnode scripts/check-remove-by.cjs --cifor GitHub annotations. Exits 0 on the current tree (49 markers, all future-dated, 0 expired, 0 allowlisted), so this ratchets from a clean baseline.Verification performed
node scripts/check-file-sizes.cjs --ci— exit 0node scripts/check-remove-by.cjs— exit 0 (49 upcoming, 0 expired)pnpm install --frozen-lockfile --ignore-scripts+node scripts/generate-notice.cjs(write mode) — NOTICE byte-identical, diff-detector confirmednode scripts/check-workflow-shell-syntax.cjs— 53 inline scripts (including the new run blocks) parse cleanlychanges, frontend, mcp-server, relay, repo-guards, dependabot-notice, rust, validate-success; hermetic =changes, fresh-clone, hermetic-successOut of scope (deliberately untouched)
Frontend job's npx/audit steps and its 30-min timeout; release.yml (signing owned elsewhere).
Residual risks, disclosed
cargo metadata); dev-dep bumps pay it only to conclude "no diff". Acceptable against 27 rotting PRs.@dependabot rebasestill works and simply re-triggers the heal on the recreated branch.🤖 Generated with Claude Code
https://claude.ai/code/session_01LrXvdHoDGUj99Fqf1fCYJY