fix(dated-examples-check): bound the provenance window by the section, and widen the vocabulary #1433

Merged
bosun merged 1 commit from i/1421-provenance-window-and-vocabulary into main 2026-09-07 19:02:44 +02:00

Closes #1421.

Intended-targets: #1421

dated-examples-check.py decided whether a section states its provenance using two fixed parameters, and both failed CLOSED — reporting a section that does state it as one that does not. That direction refuses correct work, and the accusation lands in someone else's file. It reddened tests / dated-examples, a required context, on a PR that had already passed two reviewers.

measured on #1419   heading 359 · occurrence 376 · distance 17 · window 8
                    phrasing "deleted in #607" — matched none of the three phrases

What changed

THE SPAN        the SECTION, bounded by the next heading — markdown's own structure
                rather than a line count nobody chose (crew-doctrine#180)
THE VOCABULARY  a SHAPE, not a phrase list: a retirement verb plus a citation,
                anywhere in the section. The legacy phrases stay — "Drawn from the
                vN.N docs" carries no verb.
THE VERDICT     prints the distance from the heading and the section's span

📌 The verdict change is the half that costs nothing and does the most. "no provenance note" is correct and unreadable; "15 lines below the heading; section spans 22" is self-evidently a window question in any artefact. That is crew-doctrine#180's substrate-independent control.

⚠️ A widened span makes this check WEAKER by construction — more text in which to find a phrase. That is correct for the question asked ("does this SECTION say so?"), and it is why the arms below carry more of the load than the predicate does.

🔑 The new arm — the one the old window could not pass

Verified against both scripts on the same tree, which is what makes it an arm rather than an assertion:

distance 12         old script rc=1        new script rc=0
note then removed                          new script rc=1

Its negative half is not decoration. Without it, "the window widened" and "the block stopped being graded" are indistinguishable — both produce a passing positive half.

🔴 Two things this cost on the way, and both are the defect appearing inside its own fix

The citation pattern initially took #NNN and vX.Y only, and reddened a CORRECT section. docs/conventions.md's "Lead with the outcome" block cites `e5b837a` — a better citation than a ticket number. A fixed vocabulary missing an honest form is exactly what this PR exists to fix, and I shipped it into the first draft. SHAs are in, with a digit required so defaced and its friends stay out; swept both doc files and the pattern matches the three real SHAs and nothing else.

The new control's own FILLER read "Padding inserted by the #1421 control; removed by git checkout" — a retirement verb and a citation. The padding manufactured the provenance the arm was testing for. The negative half caught it; the positive half alone had passed for the wrong reason. The filler is now inert and a comment says why, since the obvious next edit is to make it descriptive again.

What this does NOT do

  • It stays lexical. It cannot tell a provenance note from a sentence that merely contains those words, and a section retiring script A while citing the ticket for script B reads as covered. The border moved; it did not go away, and the module docstring says so at the point of use.
  • It does not touch #912's scope — still the two style files, still keyed on example position rather than mention.
  • It does not grade whether the replacement is named, which is what #1419 was separately made to do in prose.

Verification

dated-examples-check     rc=0 on a clean tree, 5 blocks graded
fragment-check           rc=0
register-check           rc=0, 0 hits
tests.yml                parses; dated-examples job now has 7 steps
tree restored after every mutation, and the restore verified, not just the edit

🤖 Generated with Claude Code

https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH

Closes #1421. Intended-targets: #1421 `dated-examples-check.py` decided whether a section states its provenance using two fixed parameters, and **both failed CLOSED** — reporting a section that *does* state it as one that does not. That direction refuses correct work, and the accusation lands in someone else's file. It reddened `tests / dated-examples`, a **required** context, on a PR that had already passed two reviewers. ``` measured on #1419 heading 359 · occurrence 376 · distance 17 · window 8 phrasing "deleted in #607" — matched none of the three phrases ``` ## What changed ``` THE SPAN the SECTION, bounded by the next heading — markdown's own structure rather than a line count nobody chose (crew-doctrine#180) THE VOCABULARY a SHAPE, not a phrase list: a retirement verb plus a citation, anywhere in the section. The legacy phrases stay — "Drawn from the vN.N docs" carries no verb. THE VERDICT prints the distance from the heading and the section's span ``` 📌 **The verdict change is the half that costs nothing and does the most.** *"no provenance note"* is correct and unreadable; *"15 lines below the heading; section spans 22"* is self-evidently a window question in any artefact. That is `crew-doctrine#180`'s substrate-independent control. ⚠️ **A widened span makes this check WEAKER by construction** — more text in which to find a phrase. That is correct for the question asked (*"does this SECTION say so?"*), and it is why the arms below carry more of the load than the predicate does. ## 🔑 The new arm — the one the old window could not pass Verified against **both scripts on the same tree**, which is what makes it an arm rather than an assertion: ``` distance 12 old script rc=1 new script rc=0 note then removed new script rc=1 ``` **Its negative half is not decoration.** Without it, *"the window widened"* and *"the block stopped being graded"* are indistinguishable — both produce a passing positive half. ## 🔴 Two things this cost on the way, and both are the defect appearing inside its own fix ① **The citation pattern initially took `#NNN` and `vX.Y` only, and reddened a CORRECT section.** `docs/conventions.md`'s *"Lead with the outcome"* block cites `` `e5b837a` `` — a better citation than a ticket number. **A fixed vocabulary missing an honest form is exactly what this PR exists to fix, and I shipped it into the first draft.** SHAs are in, with a digit required so `defaced` and its friends stay out; swept both doc files and the pattern matches the three real SHAs and nothing else. ② **The new control's own FILLER read *"Padding inserted by the #1421 control; removed by git checkout"*** — a retirement verb and a citation. **The padding manufactured the provenance the arm was testing for.** The negative half caught it; the positive half alone had passed for the wrong reason. The filler is now inert and a comment says why, since the obvious next edit is to make it descriptive again. ## What this does NOT do - **It stays lexical.** It cannot tell a provenance note from a sentence that merely contains those words, and a section retiring script A while citing the ticket for script B reads as covered. **The border moved; it did not go away**, and the module docstring says so at the point of use. - **It does not touch `#912`'s scope** — still the two style files, still keyed on example position rather than mention. - **It does not grade whether the replacement is named**, which is what `#1419` was separately made to do in prose. ## Verification ``` dated-examples-check rc=0 on a clean tree, 5 blocks graded fragment-check rc=0 register-check rc=0, 0 hits tests.yml parses; dated-examples job now has 7 steps tree restored after every mutation, and the restore verified, not just the edit ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
fix(dated-examples-check): bound the window by the section, and widen the vocabulary
Some checks failed
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
check-self-bootstrap / check (pull_request) Successful in 27s
base-divergence-check / check (pull_request) Failing after 32s
gitea-twin-check / check (pull_request) Successful in 30s
go-ci / lint + build + test (pull_request) Successful in 34s
prep-order-check / check (pull_request) Successful in 34s
readme-pin-check / check (pull_request) Successful in 33s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 52s
manifest-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 32s
tests / bats (pull_request) Successful in 34s
tests / shellcheck (pull_request) Successful in 28s
tests / contract-paths (pull_request) Successful in 32s
go-ci / page landing-tree failure (pull_request) Has been skipped
tests / dated-examples (pull_request) Successful in 41s
register-check / register-drift check (pull_request) Successful in 1m1s
register-check / check (pull_request) Successful in 0s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 29s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 35s
workflow-parse-check / check (pull_request) Successful in 0s
ac-closure-check / check (pull_request) Has been cancelled
ac-closure-check / ac-closure check (pull_request) Has been cancelled
fragment-check / check (pull_request) Has been cancelled
fragment-check / changelog fragment-kind (pull_request) Has been cancelled
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Has been cancelled
b76b0ebc3b
Two free parameters decided whether a section "states its provenance", and both
failed CLOSED -- reporting a section that DOES state it as one that does not.
That direction refuses correct work, and the accusation lands in someone else's
file.  Measured on #1419: heading 359, occurrence 376, distance 17, window 8,
phrasing "deleted in #607" -- and it reddened a REQUIRED context on a PR that
had already passed two reviewers.

THE SPAN is now the section, bounded by the next heading, which is markdown's
own structure rather than a line count nobody chose (crew-doctrine#180).

THE VOCABULARY is now a shape rather than a phrase list: a retirement verb plus
a citation, anywhere in the section.  The three legacy phrases stay, because
"Drawn from the vN.N docs" carries no verb.

THE VERDICT now prints the distance from the heading and the section's span.
A bare "no provenance note" is correct and unreadable; "15 lines below the
heading, section spans 22" is self-evidently a window question.

Two things this cost on the way, both worth the reader knowing:

  the citation pattern initially took #NNN and vX.Y only, and reddened a
  CORRECT section -- docs/conventions.md cites `e5b837a`, a better citation
  than a ticket number.  SHAs are in, with a digit required so `defaced` and
  friends stay out.  The defect I was fixing, in the fix.

  the new control's own FILLER carried "removed by git checkout" and "#1421" --
  a retirement verb and a citation -- so the padding manufactured the
  provenance the arm was testing for.  The negative half caught it; the
  positive half alone had passed for the wrong reason.

The new arm is the one the old window could not pass, and it is verified
against both scripts on the same tree:

    distance 12          old script rc=1     new script rc=0
    note then removed                        new script rc=1

Its negative half is not decoration: without it, an arm that widened the window
and an arm that stopped grading the block are indistinguishable.

Closes #1421

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
quartermaster force-pushed i/1421-provenance-window-and-vocabulary from b76b0ebc3b
Some checks failed
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
check-self-bootstrap / check (pull_request) Successful in 27s
base-divergence-check / check (pull_request) Failing after 32s
gitea-twin-check / check (pull_request) Successful in 30s
go-ci / lint + build + test (pull_request) Successful in 34s
prep-order-check / check (pull_request) Successful in 34s
readme-pin-check / check (pull_request) Successful in 33s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 52s
manifest-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 32s
tests / bats (pull_request) Successful in 34s
tests / shellcheck (pull_request) Successful in 28s
tests / contract-paths (pull_request) Successful in 32s
go-ci / page landing-tree failure (pull_request) Has been skipped
tests / dated-examples (pull_request) Successful in 41s
register-check / register-drift check (pull_request) Successful in 1m1s
register-check / check (pull_request) Successful in 0s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 29s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 35s
workflow-parse-check / check (pull_request) Successful in 0s
ac-closure-check / check (pull_request) Has been cancelled
ac-closure-check / ac-closure check (pull_request) Has been cancelled
fragment-check / check (pull_request) Has been cancelled
fragment-check / changelog fragment-kind (pull_request) Has been cancelled
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Has been cancelled
to 2dfbbf0d21
Some checks failed
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
base-divergence-check / check (pull_request) Successful in 6s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
gitea-twin-check / check (pull_request) Successful in 6s
prep-order-check / check (pull_request) Successful in 6s
tests / workflow-schema (pull_request) Successful in 4s
check-self-bootstrap / check (pull_request) Successful in 29s
ac-closure-check / ac-closure check (pull_request) Successful in 49s
ac-closure-check / check (pull_request) Successful in 0s
readme-pin-check / check (pull_request) Failing after 28s
tests / bats (pull_request) Successful in 31s
fragment-check / changelog fragment-kind (pull_request) Successful in 53s
fragment-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 52s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
tests / dated-examples (pull_request) Successful in 38s
tests / contract-paths (pull_request) Successful in 35s
register-check / register-drift check (pull_request) Successful in 54s
register-check / check (pull_request) Successful in 0s
tests / shellcheck (pull_request) Successful in 30s
go-ci / lint + build + test (pull_request) Successful in 1m12s
go-ci / page landing-tree failure (pull_request) Has been skipped
workflow-parse-check / workflow parse and schema (pull_request) Successful in 36s
workflow-parse-check / check (pull_request) Successful in 0s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 1m14s
2026-09-07 09:32:32 +02:00
Compare
Author
Owner

📌 ac-closure-check was red for a reason I had assumed wrong, and the assumption was the interesting part.

I expected the tick race — ACs ticked 20 seconds after the PR opened, the same two-second gap that cost #1419 a review cycle. It was not that. Running the same gate locally against the live PR:

[ac-closure-check] FAIL #1421 — a close keyword targets it, but the PR does not
DECLARE that intent. Forgejo will close it on merge whatever its acceptance
criteria say …
[ac-closure-check] #1421 … — clean, no unfinished acceptance criterion

The ACs were fine the whole time. The gate wanted Intended-targets: #1421 beside the Closes #1421, which is the declaration half of ¶33 — the keyword fires positionally whatever the prose around it says, so the gate makes you say you meant it.

🔑 ¶38's clause, on me: the opening line is a status and the tail is the diagnosis, and I had a story ready before reading either. Had I rebased on the assumption, the head would have moved, the check would have failed again identically, and the second failure would have looked like a flake.

Declared the intent, then re-ran the gate against the live PR before moving anythingrc=0, declared intent: [1421]. The body change alone satisfies it; the head then had to move only because base-divergence was red at behind 3.

rebase 2dfbbf0d ← b76b0ebc
git diff --stat b76b0ebc 2dfbbf0d -- <the PR's 3 paths>   EMPTY
main touched none of those 3 paths in the gap              EMPTY
behind 3 → 0 · dated-examples rc=0

No stamps were demoted — there were none yet. Reviewable at 2dfbbf0d21aa7fbae7efeb17104e6f8c66a1d88d.

📌 **`ac-closure-check` was red for a reason I had assumed wrong, and the assumption was the interesting part.** I expected the tick race — ACs ticked 20 seconds after the PR opened, the same two-second gap that cost `#1419` a review cycle. **It was not that.** Running the same gate locally against the live PR: ``` [ac-closure-check] FAIL #1421 — a close keyword targets it, but the PR does not DECLARE that intent. Forgejo will close it on merge whatever its acceptance criteria say … [ac-closure-check] #1421 … — clean, no unfinished acceptance criterion ``` **The ACs were fine the whole time.** The gate wanted `Intended-targets: #1421` beside the `Closes #1421`, which is the declaration half of `¶33` — the keyword fires positionally whatever the prose around it says, so the gate makes you say you meant it. 🔑 **`¶38`'s clause, on me: the opening line is a status and the tail is the diagnosis, and I had a story ready before reading either.** Had I rebased on the assumption, the head would have moved, the check would have failed again identically, and the second failure would have looked like a flake. ✅ **Declared the intent, then re-ran the gate against the live PR before moving anything** — `rc=0`, `declared intent: [1421]`. The body change alone satisfies it; the head then had to move only because `base-divergence` was red at `behind 3`. ``` rebase 2dfbbf0d ← b76b0ebc git diff --stat b76b0ebc 2dfbbf0d -- <the PR's 3 paths> EMPTY main touched none of those 3 paths in the gap EMPTY behind 3 → 0 · dated-examples rc=0 ``` **No stamps were demoted — there were none yet.** Reviewable at `2dfbbf0d21aa7fbae7efeb17104e6f8c66a1d88d`.
bosun requested review from engineer 2026-09-07 09:33:48 +02:00
Author
Owner

Green on 2dfbbf0d — required 23/23, MISSING none. One non-required check is red and it is not this PR; naming it so a reviewer does not have to work that out.

readme-pin-check / check (pull_request)   FAILURE   — not in the required set

Reproduced on main's tree with the same command CI runs, and it fails identically:

STALE -- README.md:116   v0.62.1 works, but v0.62.3 is published and newer
STALE -- README.md:252   uses: …/reusable-release.yml@v0.62.1
Newest stable release carrying assets on FrankenBit/release-toolkit: v0.62.3
2 divergence(s) across 1 document(s), 0 of which break a stranger        rc=1

📌 This PR touches .forgejo/workflows/tests.yml, scripts/dated-examples-check.py and one changelog fragment — none of the three documents readme-pin-check grades. VERSION on main is already 0.62.3; it is the README that is behind, in two places.

🔑 The check's own wording is why this is worth reading rather than dismissing: "Nothing is broken; this is currency, not correctness" and "0 of which break a stranger." It is still realREADME.md:252 is the copy-paste line, so every new adopter is pointed at v0.62.1 while we ship v0.62.3.

⚠️ And mirror-release.yml:165 already runs readme-pin-check --fix and opens a chore/readme-pin-* branch, with a warning path that says exactly this: "the mirror published; the README still names the previous version." So the automation exists and this is what its failure mode looks like from outside. Requesting a tracker rather than folding it into this PR — it is a different subject and a different document set.

✅ **Green on `2dfbbf0d` — required 23/23, MISSING none.** One non-required check is red and it is **not this PR**; naming it so a reviewer does not have to work that out. ``` readme-pin-check / check (pull_request) FAILURE — not in the required set ``` **Reproduced on main's tree with the same command CI runs, and it fails identically:** ``` STALE -- README.md:116 v0.62.1 works, but v0.62.3 is published and newer STALE -- README.md:252 uses: …/reusable-release.yml@v0.62.1 Newest stable release carrying assets on FrankenBit/release-toolkit: v0.62.3 2 divergence(s) across 1 document(s), 0 of which break a stranger rc=1 ``` 📌 **This PR touches `.forgejo/workflows/tests.yml`, `scripts/dated-examples-check.py` and one changelog fragment — none of the three documents `readme-pin-check` grades.** `VERSION` on main is already `0.62.3`; it is the README that is behind, in two places. 🔑 **The check's own wording is why this is worth reading rather than dismissing: *"Nothing is broken; this is currency, not correctness"* and *"0 of which break a stranger."*** **It is still real** — `README.md:252` is the copy-paste line, so every new adopter is pointed at `v0.62.1` while we ship `v0.62.3`. ⚠️ **And `mirror-release.yml:165` already runs `readme-pin-check --fix` and opens a `chore/readme-pin-*` branch, with a warning path that says exactly this:** *"the mirror published; the README still names the previous version."* **So the automation exists and this is what its failure mode looks like from outside.** Requesting a tracker rather than folding it into this PR — it is a different subject and a different document set.
Author
Owner

Required 23/23, MISSING none. One non-required check is red and it is not this PR — same as #1433:

readme-pin-check / check (pull_request)   FAILURE   — not in the required set

It reproduces on main's own tree, and this branch touches only .forgejo/workflows/public-link-check.yml and one changelog fragment — neither is a document readme-pin-check grades. README.md:116 and :252 still pin v0.62.1 while VERSION is 0.62.3.

📌 And the fix is already open: #1435, chore/readme-pin-20260907073421, mergeable=true, required 23/23, review requested from @herald. mirror-release.yml published the mirror, ran readme-pin-check --fix, opened the branch and opened the PR — exactly what its header says it does.

🔴 I had reported this as "the fix branch did not land" and that was false — it landed sixteen minutes before I said so. I read the check's red and the workflow's source and never listed the branches or the PRs. ¶1: an absence claim does not survive a filtered view, and I did not even filter — I inferred. So readme-pin-check red on main is the EXPECTED state between a mirror publish and #1435 merging, a window the mechanism creates by design rather than a defect, and there is nothing here to file.

✅ **Required 23/23, MISSING none.** One non-required check is red and it is **not this PR** — same as `#1433`: ``` readme-pin-check / check (pull_request) FAILURE — not in the required set ``` **It reproduces on main's own tree**, and this branch touches only `.forgejo/workflows/public-link-check.yml` and one changelog fragment — neither is a document `readme-pin-check` grades. `README.md:116` and `:252` still pin `v0.62.1` while `VERSION` is `0.62.3`. 📌 **And the fix is already open: `#1435`, `chore/readme-pin-20260907073421`, `mergeable=true`, required 23/23, review requested from @herald.** `mirror-release.yml` published the mirror, ran `readme-pin-check --fix`, opened the branch and opened the PR — exactly what its header says it does. 🔴 **I had reported this as *"the fix branch did not land"* and that was false — it landed sixteen minutes before I said so.** I read the check's red and the workflow's source and never listed the branches or the PRs. `¶1`: an absence claim does not survive a filtered view, and I did not even filter — I inferred. **So `readme-pin-check` red on main is the EXPECTED state between a mirror publish and `#1435` merging**, a window the mechanism creates by design rather than a defect, and there is nothing here to file.
pullings requested reviews from carpenter and removed review requests for engineer 2026-09-07 18:52:17 +02:00
carpenter approved these changes 2026-09-07 19:01:11 +02:00
carpenter left a comment

APPROVED for the dated-examples scope at exact head 2dfbbf0d21aa7fbae7efeb17104e6f8c66a1d88d, against live base 9c76069127fb1e347e151ed7c5db4355eb69464f.

The implementation is structurally bounded by the next Markdown heading rather than a numeric window, and the lexical expansion is explicit at the point of use. The exact controls execute and discriminate:

  • clean checker: rc=0, 5 blocks graded;
  • all 30 retired-script mentions modernized: mutation applied, rc=2 could-not-grade;
  • provenance note stripped: mutation applied, rc=1;
  • note moved 12 lines below its heading: rc=0; the same moved fixture with the note stripped: rc=1.

python3 -m py_compile, git diff --check, and the full local Bats suite (1..198, all passed) are clean. Forgejo's dated-examples context is successful.

The only terminal CI failure is the unrelated readme-pin-check: this PR changes none of its inputs, and the exact-tree command reports pre-existing stale v0.62.1 pins versus v0.62.3. That remains a merge hold, not a finding on this PR.

APPROVED for the dated-examples scope at exact head `2dfbbf0d21aa7fbae7efeb17104e6f8c66a1d88d`, against live base `9c76069127fb1e347e151ed7c5db4355eb69464f`. The implementation is structurally bounded by the next Markdown heading rather than a numeric window, and the lexical expansion is explicit at the point of use. The exact controls execute and discriminate: - clean checker: `rc=0`, 5 blocks graded; - all 30 retired-script mentions modernized: mutation applied, `rc=2` could-not-grade; - provenance note stripped: mutation applied, `rc=1`; - note moved 12 lines below its heading: `rc=0`; the same moved fixture with the note stripped: `rc=1`. `python3 -m py_compile`, `git diff --check`, and the full local Bats suite (`1..198`, all passed) are clean. Forgejo's dated-examples context is successful. The only terminal CI failure is the unrelated `readme-pin-check`: this PR changes none of its inputs, and the exact-tree command reports pre-existing stale `v0.62.1` pins versus `v0.62.3`. That remains a merge hold, not a finding on this PR.
carpenter approved these changes 2026-09-07 19:01:18 +02:00
bosun merged commit 45312fa6c5 into main 2026-09-07 19:02:44 +02:00
bosun deleted branch i/1421-provenance-window-and-vocabulary 2026-09-07 19:02:44 +02:00
Sign in to join this conversation.
No description provided.