Skip to content

fix(sync): divergence_reviewed carries one blanket date, re-creating the claim the field split removed #3244

Description

@stranske

Why

#3183 / PR #3203 split the drift allowlist's single reason field into divergence,
divergence_reviewed and fingerprint_refreshed — and then populated the two new fields with the
same blanket date on every pair, which re-creates the exact false assertion the split existed to
remove, now in a machine-readable field.

#3183 §4 stated the problem: one field carried two different events — a mechanical fingerprint refresh
when Renovate moves a pinned SHA, and a judgement that two files SHOULD differ — so "the file asserts
the second whenever the first happens"
. The remedy was to make a hash refresh unable to touch the
review claim.

Measured at HEAD a377fd30 over config/template-drift-allowlist.txt:

sections: 20
divergence_reviewed:   {'2026-08-23': 19, '2026-08-24': 1}
fingerprint_refreshed: {'2026-08-23': 19, '2026-08-24': 1}

The two fields are identical for all twenty pairs, and the coupling has already survived one day of
real traffic: the single pair whose fingerprint moved on 2026-08-24 had its divergence_reviewed
moved with it. Nineteen pairs assert a 2026-08-23 review while their own divergence prose dates the
judgement elsewhere — [pair.3] says "re-reviewed 2026-06-20", [pair.5] "updated 2026-08-05",
[pair.10] "re-baselined 2026-06-30", [pair.15] "re-baselined 2026-07-14".

The gate cannot see any of this. tests/scripts/test_template_drift_allowlist.py:12 asserts only
non-emptiness:

assert divergence and "Existing reviewed baseline drift" not in divergence
assert reviewed
assert refreshed

A blanket divergence_reviewed = <today> on every pair passes it, and so would a value that moves in
lockstep with fingerprint_refreshed forever. The field has no drain: nothing decrements it, nothing
can distinguish a real re-review from a copy of the refresh date, and the reader is told the whole
file was re-reviewed today whenever any one SHA moves.

Also worth folding in: #3183's named live gate
tests/scripts/test_sync_manifest_docs.py::test_manifest_issue_references_are_open is currently
vacuous. It only iterates citations explicitly marked open:, and .github/sync-manifest.yml
contains five resolved: citations and zero open: ones, so the node-id the acceptance criterion
names verifies nothing even with a token present. The strict check that does the work lives under a
different name, test_manifest_issue_citations_are_explicitly_stateful.

Full trace: Code/Audits/Workflows/2026-08-24-implementation-verification.md.

Scope

config/template-drift-allowlist.txt (set each pair's divergence_reviewed to the date its own
divergence prose records), scripts/check_template_drift.py (surface the divergence between the two
dates), and tests/scripts/test_template_drift_allowlist.py (assert the property, not the presence).
Plus the vacuous-gate rename in tests/scripts/test_sync_manifest_docs.py.

Non-Goals

  • Do NOT add a human review, approval, sign-off, or recurring owner step. The refresh traffic is
    agent-borne and must stay that way; this is about what the file is allowed to CLAIM, not about
    adding a reviewer.
  • Do NOT rewrite the twenty divergence rationales. Read the date out of each one; leave the prose.
  • Do NOT delete divergence_reviewed. A field that cannot be trusted is worse than none, but the
    answer is making it checkable.
  • Do NOT make the drift check fail on a stale divergence_reviewed. Reporting a stale review date must
    never block a fingerprint refresh, or the gate blocks its own drain.
  • Do NOT change any main_sha256 or template_sha256 value, or the drift comparison semantics.
  • Do NOT introduce a new config format for the allowlist; it stays a configparser file.
  • A scaffolded stub, a TODO, a renamed file, or a test that passes without exercising the change
    does NOT count as done and is a failure of this issue.

Tasks

  • Set each pair's divergence_reviewed in config/template-drift-allowlist.txt to the date stated in that pair's own divergence text, leaving fingerprint_refreshed at the date the hashes last moved.
  • Add a divergence_reviewed age to the report emitted by scripts/check_template_drift.py, printed beside each allowlisted pair, so a stale review date is visible where the pair is read.
  • Print a single summary line in scripts/check_template_drift.py counting pairs whose divergence_reviewed equals their fingerprint_refreshed, which is the signature of a coupled bump.
  • Replace the non-emptiness assertions in tests/scripts/test_template_drift_allowlist.py with an assertion that fewer than half the pairs share an identical divergence_reviewed value, so a blanket stamp fails.
  • Add test_divergence_reviewed_is_not_a_copy_of_fingerprint_refreshed to tests/scripts/test_template_drift_allowlist.py asserting no pair has the two dates equal unless its divergence text names that same date.
  • Add a parser to tests/scripts/test_template_drift_allowlist.py that extracts the date from each divergence string and asserts it matches that pair's divergence_reviewed.
  • Rename test_manifest_issue_citations_are_explicitly_stateful in tests/scripts/test_sync_manifest_docs.py to test_manifest_issue_references_are_open and give the network-gated live check a name that says it only checks open: citations.

Acceptance Criteria

  • Named test gate: python3 -m pytest tests/scripts/test_template_drift_allowlist.py::test_divergence_reviewed_is_not_a_copy_of_fingerprint_refreshed -q exits 0.
  • Second named gate: python3 -m pytest tests/scripts/test_template_drift_allowlist.py::test_every_pair_states_its_divergence -q exits 0 and its assertions compare the extracted prose date against divergence_reviewed rather than testing for a non-empty string.
  • Deliberate break: set every divergence_reviewed in config/template-drift-allowlist.txt to today's date; python3 -m pytest tests/scripts/test_template_drift_allowlist.py -q must FAIL naming both tests. Revert the file; both must pass. Quote both transcripts as raw pytest output in the PR body.
  • Second deliberate break: bump one pair's fingerprint_refreshed and its divergence_reviewed together; the named gate must FAIL. Bump only fingerprint_refreshed; it must pass. Quote both transcripts as raw pytest output.
  • python3 scripts/check_template_drift.py exits 0 and reports 20 pairs with 0 unallowlisted, unchanged by the metadata edits.
  • python3 scripts/check_template_drift.py output states how many pairs have divergence_reviewed equal to fingerprint_refreshed.
  • python3 -m pytest tests/scripts -q exits 0 and the skipped count does not increase.
  • No approval step, review gate, label handoff, or queue is introduced anywhere in the diff.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions