Repository navigation
Make the ledger's merge provenance real - #280
Merged
Merged
Conversation
Found by the external QA voice in #278, verified independently, and true in a way I did not expect: the release tagger I added the day before consumes MERGE_COMMIT as authoritative provenance, while AUTHOR_RUNBOOK calls that field optional. Nothing filled it, because nothing could — the row lands inside the AUTHOR's own pull request and the squash commit does not exist until that pull request merges. Eleven of twenty-three rows were blank, eight of them IMPLEMENTED. Optional and nobody's job are the same thing. The cost lands on the digest: coverage_digest hashes (REQ_ID, MERGE_COMMIT) pairs, so with blanks a milestone repaired at new commits hashes identically to the one released before, and the patch tag that exists for exactly that case is never cut. So backfill_merge_commits asks the forge where each row's merged pull request landed and writes it down — no judgement, the mapping is a fact GitHub already holds — and the tagger now refuses a milestone whose provenance is still blank rather than hashing an empty string. The second defect was quieter. Ten rows parsed into more than the six declared fields because EVIDENCE held an unquoted comma; DictReader files the surplus under the key None, validate() never looked, and every consumer read only the text before that comma. REQ-CORE-005 was rendering 120 characters of a 620-character cell. The check now names such a row and the ledger is rewritten through a real CSV writer, so nothing this repo writes can create one again. No STATUS is touched. REQ-CORE-006 stays PARTIAL here even though #278 argues convincingly that merged #239 completed it: deciding a requirement is satisfied is the ACCEPTOR's call against acceptance criteria, not an operator's while repairing data. That half of #278 stays open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6 tasks done
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.
Closes #279
The mechanical half of #278, which stays open for the judgement half.
Goal
Fill the provenance field nobody was responsible for filling, stop rows losing their
evidence to an unquoted comma, and refuse to release a milestone on provenance that does
not exist.
Evidence
Found by the external QA voice in #278 and verified independently.
MERGE_COMMITblank on 11 of 23 rows, 8 of themIMPLEMENTED. The AUTHOR cannotwrite it — the row lands inside its own pull request and the squash commit does not exist
until that pull request merges — so
AUTHOR_RUNBOOK.mdcalled it optional, whilerelease_tag.py, added the day before, consumes it as authoritative provenance.The cost is on the digest:
coverage_digesthashes(REQ_ID, MERGE_COMMIT)pairs, sowith blanks a milestone repaired at new commits hashes identically to the one released
before and the patch tag that exists for exactly that case is never cut.
10 of 23 rows parsed into more than six fields. An unquoted comma in
EVIDENCEsplits the row;
csv.DictReaderfiles the surplus underNone,validate()neverlooked, and every consumer read only the text before it.
REQ-CORE-005was rendering120 characters of a 620-character cell.
Scope — every changed path
docs/spec/implementation_status.csv— 11 rows gain their real merge commit; 10 rowsregain the evidence that was being truncated; written through a real CSV writer.
docs/spec/IMPLEMENTATION_STATUS.md— regenerated.scripts/implementation_status.py—validate()rejects a row that parses into morethan the six declared fields.
scripts/backfill_merge_commits.py— new. Asks the forge where each row's merged pullrequest landed. Never rewrites a recorded commit, never touches an unmerged pull
request, never changes
STATUS.scripts/release_tag.py—missing_provenance; a complete milestone with a blankcommit warns and is not tagged.
.github/workflows/release-tag.yml— backfill runs before the tagger; git identityhoisted so both steps share it.
scripts/tests/test_backfill_merge_commits.py— new, 12 tests.docs/zendev/AUTHOR_RUNBOOK.md— the field is filled after the merge, not optional;quote
EVIDENCEthat holds a comma.Non-goals
No
STATUSis touched.REQ-CORE-006staysPARTIALhere even though #278 arguesconvincingly that merged #239 completed it. Deciding a requirement is satisfied is the
ACCEPTOR's judgement against acceptance criteria, not an operator's while repairing data.
Acceptance criteria
repository's own ledger.
--checkrejects an over-wide row, naming it and why.nothing.
Verification
Before and after on one row:
REQ-CORE-005evidence parsed as 120 characters, now 620;overflow rows 10 → 0; blank merge commits 11 → 0.
🤖 Generated with Claude Code