Skip to content

Bind the cached signature verdict to the keyring that produced it - #655

Merged
MongLong0214 merged 1 commit into
mainfrom
fix-f003-route-grade
Aug 14, 2026
Merged

Bind the cached signature verdict to the keyring that produced it#655
MongLong0214 merged 1 commit into
mainfrom
fix-f003-route-grade

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Addresses the cause behind F-003, recorded as #653. Not a close of #631 — that finding has its own acceptance and is discussed below.

The defect

The index stores signature_status from %G?, and %G? is not a property of the repository. It is the verdict of whichever process ran git log, and it depends on the keys that process could reach.

1) index built while the keyring was unreachable   => claim
2) keyring restored, same index                    => claim
3) same moment, --no-index                         => directive

Steps 2 and 3 are the same repository, the same record, the same moment and the same binary.

What the test asserts, and why not the obvious thing

The regression asserts the cause — a cached verdict does not outlive the keyring that produced it — rather than the symptom that two routes disagree. The symptom version would go green for the wrong reason: any change that happens to make the routes agree, including one that stopped the scan path grading at all, would satisfy it while the stale verdict remained.

RED on main, at the final assertion, with the --no-index guard passing first so a broken fixture cannot be mistaken for the defect:

× #653 a cached signature verdict does not outlive its keyring
  → expected 'claim' to be 'directive'
  ❯ test/index-signature-generation.test.ts:131:25

Negative control: disabling the generation check reproduces that exact failure; restoring it passes.

The fix

Signature mode records an identifier for the keys the verifier could see, in meta. A reader whose keys differ gets a rebuild reason — which ADR-0003 already makes the ordinary answer, since the index is derived and has no unrecoverable states. No schema change: the existing meta table and the existing healthProblem → rebuild path carry it.

A verifier that cannot list keys at all gets its own generation. It grades differently from one that can, and saying so is the point.

Cost, measured

gpg --list-keys averages 6.2ms, and it runs once per index open, only in signature mode — which is opt-in. cdeb-smoke A/B: 93.32s with the check, 92.12s without, a 1.3% difference on the heaviest file in the suite.

Memoizing per process would remove even that, and is deliberately not done: a long-running server would then keep grading against the keyring it started with, which is the same shelf-life defect just removed from the MCP capture gate in #641.

On #631

#631 was attributed to a stale runtime, and #644 makes the surfaces converge. That is necessary and not sufficient — the reproduction above needs one build. This closes the cache half; #631 should stay open until its own acceptance is met.

Verification

282 cases across index-db, query, signer-authority, doctor and the new file. Full suite: 3067 passed, with cdeb-smoke timing out under parallel load and passing in 93s when run alone — the A/B above shows the check is not the cause. Canonical rebuild bf41f0ff, 280 files in dist.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/main..f85775ea58c8b6802504293bf5bf409c2ab751c4
Active constraints: not read — commitlore: git log --follow accepts exactly one pathspec, so renames are not followed for 10 paths; query one path at a time to follow its rename chain (10 changed paths)

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

The index stores `signature_status` from `%G?`, and `%G?` is not a property of
the repository. It is the verdict of whichever process ran `git log`, and it
depends on the keys that process could reach. An index warmed where the signing
keys were unreachable therefore keeps answering `claim` after they come back,
and the same record grades `directive` through `--no-index` at the same moment,
from the same build (#653).

The cache is not the problem; a cache that cannot say which world it answered
about is. Signature mode now records an identifier for the keys the verifier
could see, and a reader whose keys differ treats the index as needing a
rebuild. ADR-0003 already makes that the ordinary answer: the index is derived,
so there is no unrecoverable reason, only rebuild reasons.

Only signature mode pays anything. `signatureVerifierGeneration` returns null
when the setting is off, and the setting is opt-in — it is also the only place
where a `%G?` can decide whether a record directs an agent.

Blast: module
Undo: easy
Certainty: firm
Ruled-out: memoize the generation per process | a long-running server would then keep grading against the keyring it started with, which is the shelf-life defect just removed from the MCP capture gate
Ruled-out: stop caching signature_status | it is read per graded record, and recomputing it per query costs a git log the index exists to avoid
Ruled-out: assert that two routes agree | that is the symptom, and any change making them agree — including one that stops the scan path grading at all — would satisfy it while the stale verdict remained
Limit: the generation identifies the keys the verifier can list, not whether git would reach the same verdict for other reasons; a signature that fails for expiry or revocation is outside what this binds
Provenance: authored
Record-Id: r-keyringgen653
Verified: RED on main at test/index-signature-generation.test.ts:131 with the --no-index guard passing, GREEN after; negative control — disabling the generation check reproduces "expected 'claim' to be 'directive'", restoring it passes; 282 cases across index-db, query, signer-authority, doctor and the new file; cdeb-smoke A/B measured 93.32s with the check against 92.12s without; docker linux/amd64 build and artifact:manifest; artifact:verify bf41f0ff; 280 files in dist
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