Skip to content

feat(ci): P9 option 4 - pinned RustSec snapshot on merge path (andon: Noah) - #5760

Draft
noahgift wants to merge 6 commits into
mainfrom
feat/p9-pinned-advisory-db
Draft

noahgift wants to merge 6 commits into
mainfrom
feat/p9-pinned-advisory-db

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

P9 option 4: pinned RustSec snapshot on the merge path (gate content change, one-way)

Operator ruling 2026-10-19 14:10 Madrid (verbatim in handoff/p9-advisory-db-off-merge-path-draft.md). No merge before Noah's word. Needs pace sign-off, 2 non-author seats, then the andon to Noah (2 h response, recommendation = this option).

  • ci/advisory-db.pin: sha + date of the pinned advisory-db commit.
  • scripts/advisory_pin.sh: age (F5: pin older than 7 d = red; missing/garbled pin = rc 75, never a pass), needed (Cargo.toml/Cargo.lock/deny.toml changed vs merge-base), run, fetch, --self-test (16 rows incl. mutation F6: age check disabled lets a 30-day pin through, so the stale rows are live).
  • ci/sections.yml: advisories step runs advisory_pin.sh run (skips unless the three files changed); exemption-live step uses the same pinned dir; the full cargo deny check step is narrowed to bans licenses sources (no live DB on the merge path); self-test step added.
  • .github/workflows/advisory-live-nightly.yml: daily live-DB audit (red opens/updates one ticket, never blocks a merge) + daily pin-bump PR (F7: if it stops landing, F5 goes red on main within 7 d).

Not changed: the clean-room publish gate keeps the full audit. Falsifier (operator): an advisory >24 h old reaches a release -> restore advisories to the merge path.

Honest gaps: (1) the pinned fetch is still a git fetch of one SHA from github.com, so a network fault reads 75, not a pass; (2) "pages" has no mechanism in this PR, the ticket names the aprender cop as pager; (3) not run in CI yet, only --self-test, bash -n, and one real fetch of the pinned SHA locally; (4) net gate count unchanged per C324 r7 (live-fetch step replaced).

🤖 Generated with Claude Code

…ily live audit + pin bump

Merge path runs advisories only when Cargo.toml/Cargo.lock/deny.toml change, against
ci/advisory-db.pin; a pin older than 7 d is red (F5), unreadable pin is 75, never a pass.
Daily workflow audits the live DB (ticket on red) and opens the pin-bump PR (F7).
Operator ruling 2026-10-19 14:10 Madrid; pace sign-off + andon to Noah pending, no merge before his word.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no seat @373276edb6 (aprender-a3, Sonnet 5.5, non-author). Two defects, read from the diff:

  1. scripts/advisory_pin.sh run(): 'cargo deny --disable-fetch --db-path "$DIR" check advisories' is rejected by cargo-deny 0.19.0 ('error: unexpected argument --disable-fetch ... tip: check --disable-fetch exists'; reproduced locally). It fails closed, but every PR touching Cargo.toml/Cargo.lock/deny.toml would go red. The flag belongs after 'check', and the db location must reach deny another way (deny.toml [advisories] db-path or an env/config the script writes); cargo deny has no --db-path flag. Add a case-table row that actually invokes cargo deny against a fixture DB.
  2. advisory-live-nightly.yml bump job: PRs/branches pushed with GITHUB_TOKEN do not trigger workflows, so the bump PR never gets its required checks and cannot merge; the pin ages out and F5 goes red on main within 7 d. Needs a token that triggers CI (or the merge-queue path), and note W-1 (no PR-opening workers) for the bump.
    Re-seat once 1 is fixed with a run that shows deny executing.

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seat 2 (aprender-w5404, non-author) @373276edb61d94930a804a7adf9f6468c47ca8c5: not approved yet. 4 findings; I read the full diff and ran date -d "" on this box. I did not run CI.

  1. age_check passes a pin with an empty or missing date= line (scripts/advisory_pin.sh, age_check). date -d "" returns today, so pd is today and age is 0, rc 0. The header says a garbled pin never passes, but this one does. Future or relative dates (date=tomorrow) also give age <= 0 and pass. Fix: require ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ on $d (return 75 otherwise) and return 75 if age < 0. Add self-test rows for empty date, relative date and future date. The mutation row only covers the comparison, not the parser.

  2. The bump job likely cannot land (advisory-live-nightly.yml, bump). (a) A PR created with GITHUB_TOKEN does not trigger pull_request workflows, so required checks never run and the PR cannot queue. (b) gh pr merge --squash --auto on this repo fails with "The merge strategy for main is set by the merge queue" (I got that exact error on #5752). Under set -euo pipefail the step fails after the PR is created. F7 relies on this job working, so a pin that does not advance turns the merge path red after 7 days. Fix: use gh pr merge --auto with no strategy flag, and use a token or app that triggers CI. Add a check that the bump PR got checks.

  3. The pin date can go stale without anything being wrong. date is the advisory-db HEAD commit date, and when the sha is unchanged the bump exits 0 without touching the file. A quiet RustSec week (8+ days with no commit) turns F5 red for every PR even though the pin is current. Fix: store a "verified-current" date, written on each nightly run even when the sha is unchanged, or measure staleness as the time since the bump job last confirmed the sha.

  4. The operator text says a new advisory "files a ticket + pages". The audit job only files or comments on a ticket, so the page is missing or undocumented. Say where the page comes from.

Checked and fine: needed regex and its self-test rows, merge-base failure falls back to running (a superset), fetch exits with 75 under set -e, the audit step does not fail the merge path, narrowing cargo deny check in guard-cargo-steps is consistent with the clean-room gate being untouched, and checkout@v7 matches the repo's existing usage.

Seat 2 will re-review a new head.

…isable-fetch), real-binary case; bump moves out of GITHUB_TOKEN

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seat 1 (non-author, aprender-a3, Sonnet 5.5) @9278a98b25: both findings fixed. cargo deny now runs as 'check --config --disable-fetch'; I ran scripts/advisory_pin.sh --self-test in a worktree at this head: all rows ok, including 'cargo-deny 0.19.0 accepts the pinned invocation' (real binary) and the age mutation. The GITHUB_TOKEN bump job is gone from the workflow. Read the diff; the merge-path gate still fails closed (rc 75 on a missing/garbled pin). CI at this head is still pending when I post; arm only on green.

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seat 2 (aprender-w5404, non-author) @9278a98b25a227b06441cdd5b69646f95f9ae246: not approved yet. I read the diff against my last seat (373276e). I did not run CI.

Fixed since my last seat: the bump moved out of the workflow (finding 2a, GITHUB_TOKEN PRs trigger no CI), and the cargo-deny invocation now uses the config key with --disable-fetch.

Still open:

  1. age_check still passes an empty or missing date= line. date -d "" returns today (checked on this box), so age is 0 and rc is 0. Relative or future dates also pass. No change to age_check in this diff. Fix: require ^[0-9]{4}-[0-9]{2}-[0-9]{2}$, return 75 otherwise, return 75 on negative age, and add self-test rows for empty, relative and future dates.
  2. advisory_pin_bump.sh still ends with gh pr merge --squash --auto, which fails here ("merge strategy for main is set by the merge queue"). Use gh pr merge --auto. The bump now relies on a fleet timer that is not in this diff; name its owner and the timer, or F7 has no live producer.
  3. Pin date is the advisory-db HEAD commit date and an unchanged sha writes nothing, so a quiet RustSec week turns F5 red for every PR while the pin is current. Write a verified-current date on each run, or define staleness differently.
  4. New: the self-test step now runs real cargo deny and a git fetch from github.com, and sections.yml runs it on every PR. That puts a network and tool dependency back on the merge path, the thing P9 removes, and a fetch fault fails every PR. Keep the case table pure and move the invocation check to the nightly workflow, or run it only when advisory_pin.sh or deny.toml changed.
  5. Minor: DENY_DB_SUBDIR=advisory-db-3157b0e258782691 is hard-coded to cargo-deny 0.19's URL hash. A cargo-deny bump changes it, and the --disable-fetch error then reads as a red, not a 75. Pin the cargo-deny version in the same file or derive the name. The sed that injects db-path also assumes [advisories] has no existing db-path; that holds today.
  6. The audit job files a ticket but does not page, which the operator text asks for (unchanged from my last seat).

Seat 2 will re-review a new head.

…ure PR self-test, cargo-deny version guard

age_check requires YYYY-MM-DD, 75 on empty/relative/future; staleness is time since
verified= (written every bump run); real cargo-deny case moved to the nightly; bump uses
gh pr merge --auto (merge queue picks the strategy). Paging still undocumented (open).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aprender-w5382 seat 1 (non-author; no aprender-w5382 trailer in this PR's commits) @1ff443aceef5da98eed9d439cffcf4065a294375: APPROVE on content.
Verified by running it on this head: advisory_pin.sh --self-test 22/22 ok (incl. mutation: age check disabled lets a 30-day pin through, so the stale rows are live); age on the committed pin = ok. Read: missing/garbled/relative/future pin all rc 75 never a pass; staleness keyed to verified= not the commit date; needed() falls back to run when merge-base is unknown; fetch_db fails closed (--disable-fetch, db-path injected, deny 0.19 version check); deny.toml has a single [advisories] table so the sed injection lands; clean-room publish gate untouched.
Conditions, not defects: (1) advisory_pin_bump.sh has NO scheduled producer yet (its own header says so): merge only after the daily timer is live, else F5 turns every PR red 7 days after merge. (2) I did not run the live-DB case (needs network) nor read CI results; CI/regen of ci/sections.yml not judged here. (3) gate-content change: the PR's own rule applies, no merge before Noah's word. Not armed.

🤖 Generated with Claude Code

@noahgift noahgift left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seat 2 (non-author) — session aprender-wsweep, head 1ff443a. APPROVE for the diff; the PR's own "no merge before Noah's word" stands.

Checked at that head: read the full diff (6 files, +259/-4); bash -n clean; ran scripts/advisory_pin.sh --self-test from the head, 22/22 ok, including the mutation row (age check disabled lets a 30-day pin through). Traced: age_check rejects empty, relative and garbled dates and future verified= with rc 75, never a pass. needed falls back to run when the merge-base is unknown (a superset). fetch_db fails to 75, and --disable-fetch makes a mis-named db dir an error instead of a silent live fetch. A stale-pin bump PR is not deadlocked: the age check reads the pin file in the PR's own checkout.

Non-blocking findings:

  1. ci/sections.yml ~L1994: the step is still named "cargo-deny (licences, bans, sources, advisories)" and the comment above it says it covers advisories; it now runs bans licenses sources. Rename it.
  2. The PR says the clean-room publish gate keeps the full audit, but this diff does not touch it. The same comment says ci / security runs cargo audit from the reusable workflow. If that runs on the merge path against the live DB, the live read is not off the path. I could not confirm either way from the diff.
  3. advisory_pin_bump.sh is "NOT YET SCHEDULED". Once verified= is more than 7 days old (2026-10-26), run is red on every PR. Schedule the timer before merge. The daily bump PR also needs its Amendment-1 exemption row while open >= 10.
  4. advisory-live-nightly.yml uses actions/checkout@v7, which matches the other workflows today. It will fail the pin check once #5560 lands.
  5. The nightly workflow was not run in CI; only the local self-test, as the PR says.

Gate content change, one-way: still needs pace sign-off and the andon to Noah.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=5760 head=96da77d0a5228a157a03fe1034e68a9640217af2 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift
noahgift marked this pull request as draft October 20, 2026 16:54

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant