Skip to content

the index caches a signature status that is not a property of the repository, so two routes grade the same record differently #653

Description

@MongLong0214

F-003's field report — the same repository, path, record and moment returning directive on one route and claim on another — reproduces from current main with a single build. Two runtimes are not required.

Reproduced

A repository in signature mode, with the signing key in the allowlist:

commitlore.requireSignedDirective=true
commitlore.trustedAuthor=Router <router@test.invalid>
commitlore.trustedSigner=F64FCAD1D09B89E137C25C6BA5ED7A1AF64EE2EF

git log -1 --format='%G?' reports G and %GF reports that same fingerprint.

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. They disagree.

Deleting the index and rebuilding it with the keyring reachable gives directive on every subsequent run, which confirms the cached value is what decides.

The mechanism

src/core/index-db.ts stores signatureStatus from %G? alongside the record (:699, :715, :847, :862), and src/core/grade.ts:806 refuses directive when it is not G.

But %G? is not a property of the repository. It is the verdict of the process that ran git log, and it depends on which keys that process could reach — the keyring, GNUPGHOME, whether gpg is installed, whether the key has since been imported. The same commit yields G in one environment and something else in another, with no change to the repository at all.

ADR-0003 states the index is a derived, discardable cache of what git already holds. A cached %G? breaks that: it is not derivable from the repository, and nothing invalidates it when the environment that produced it changes. The record's authority then depends on the conditions under which the cache happened to be warmed.

Why this matters for #631 and F-001

The field report was attributed to a stale runtime, and #644 makes surfaces converge on one runtime identity. That is necessary and not sufficient. This divergence needs only one build: any surface reading through the index disagrees with any surface that bypasses it, whenever the cache was warmed without the keys. Closing #631 on runtime convergence alone would leave the reproduction above intact.

Suggested acceptance

  • a signature status is not served from the cache as if it were a fact about the repository — either it is recomputed where it is used, or the cached entry records the verifying environment and is invalidated when that changes
  • a fixture that warms the index without the keys and then reads with them present grades identically on the index and --no-index routes
  • the negative control: restore the caching and show the two routes disagreeing again, so the fix cannot be a test that never exercised it

Note on scope

This is confined to signature mode, which is opt-in (commitlore.requireSignedDirective=true). Author-string mode does not read %G? and is unaffected — measured: with signature mode off, both routes grade directive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions