Skip to content

test(gate): give the owner-dir guard an assertion that can fail (#253) - #254

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/owner-dir-guard-coverage-253
Oct 24, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
fix/owner-dir-guard-coverage-253

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Summary

The stale-pointer control that landed in #252 (#246) has two owner guards in owner_present: the owner path must be a DIRECTORY, and it must not be EMPTY. The self-test asserted the empty-dir case twice — out5 created an empty owner dir while claiming to test an absent one — so the directory guard had no coverage that could fail. The validator's PASS on #252 found it, and this change closes it.

Files: scripts/governance-reconcile.sh — one file, and the diff is test + comments only: the checker's executable lines are byte-identical to origin/main (block 3). Nothing about the gate's behaviour changes; what changes is that the gate's own suite can now tell whether a guard is load-bearing.

Three concrete edits:

  • out5 becomes the truly-absent case. Its fixture ran mkdir -p "$tmp/unmaterialized/absent-repo", so the owner dir was present and empty — out6's case again. The empty dir is gone; the comment now says exactly what is tested (the owner directory does not exist at all).
  • out6 keeps the empty-dir case, with the distinction from out5 written down: here the owner dir exists and is empty (an uninitialized submodule path in a light checkout); there it does not exist.
  • A new out9 asserts that a NON-DIRECTORY owner path skips. This is the assertion that discriminates. The fix is deliberately not an assertion for the absent case: an absent owner and an empty owner are the same verdict, and ls -A on a missing directory yields nothing — so an absent-owner assertion cannot fail if the -d guard is removed, and a test that cannot fail is invalid. The guard is load-bearing only when the owner path is a regular file, where ls -A prints the file's name, the owner looks materialized, and the control turns into a false red.

The prose is corrected too: the self-test's blocks are two kinds of assertion, and the header comment now names them — FAIL (out4 a stale pointer, out7 an absent top-level surface) and SKIP (out3 a same-named root file, out5 an absent owner, out6 an empty owner, out8 a name-collision directory, out9 a non-directory owner). The #252 body's "two false-red guards" wording mixed the FAIL assertions in with the two skip guards; the script's own header now states the three filesystem states that are not a materialized owner.

Head 9de02ac — 1 file changed, 58 insertions, 8 deletions.

Change classification

hook/workflow — this repo's own gate script, scripts/governance-reconcile.sh, which the workflow-checks job of .github/workflows/workflow-checks.yml runs on every PR of every repo in the org. The change is coverage and prose for that gate: no executable line of the checker changes, and no workflow, ruleset, template or validator file is touched.

How tested

  • The gate for this change class: this repo's own gate, .github/workflows/workflow-checks.yml (the workflow-checks job). The change class is hook/workflow; this repo ships no candy or box, no charly binary and no disposable: true deploy, so this repo's CI is the only place its own copy of the script executes, and its --self-test is the only place its logic can be exercised.
  • Eval beds run — the exact beds and the per-bed result: N/A — this repo has no charly check run <bed> beds: it is the org's GitHub-configuration repo (workflows, shell and Python owner scripts), not a deployable. There is no disposable: true target in it, so no bed applies to this change class; the gate above is the class's gate.
  • Fresh rebuild: N/A — no binary is involved. This repo contains no Go source and ships no charly binary, so charly version does not apply to it.
  • Did the changed code path execute live? Yes, in its changed form. The changed path is the gate's own --self-test block; it ran live in the worktree (block 1) and against the real umbrella checkout (block 2). The negative control — the part that makes the coverage real — also ran live: each owner guard was removed from the committed file and the suite was re-run, and it went red each time (block 2). The pre-change suite, with the same guard removed, stayed green (block 2) — that is the coverage gap this change closes, measured on the same input.
  • Concurrency (shared-state changes only): N/A — the change touches no shared state; the checker reads a manifest and a set of files and holds no lock.

1. Fresh-rebuild gate output — the change-class gate, run on the committed tree (git status --porcelain empty; the commands are the ones .github/workflows/workflow-checks.yml runs, minus the two uses: runtime steps, which need GitHub's own runners and run in this PR's CI):

### bash scripts/org-ruleset_test.sh
    org-ruleset_test: all assertions passed (apply/verify/abort)
    rc=0
### bash scripts/retire-per-repo-dispatchers_test.sh
    retire-per-repo-dispatchers_test: all assertions passed
    rc=0
### bash scripts/retire-per-repo-candy-gates_test.sh
    retire-per-repo-candy-gates_test: all assertions passed
    rc=0
### bash scripts/strip-config-version_test.sh
    strip-config-version_test: all assertions passed
    rc=0
### bash scripts/head-settle_test.sh
    ok  (G3) the shipped text -> exit 3 (an unread head NEVER reaches the arming line)
    ok  (H1) EMPTY           -> exit 4  (::error title=The head carries no check run to judge::the API answered for deadbeef with ZERO check runs (this gate excluded), so there is nothing to judge. Refusing to arm auto-merge on an unjudgeable head.)
    ok  (H2) the pre-#248 shape -> exit 0  (an EMPTY set reached 'safe to arm auto-merge' — the hole)
    ok  (H3) the pre-#229 shape -> exit 0  (the same hole, one shape older)
    ok  (H4) the class table  -> 4 EMPTY is named in the shipped text, and absent from the pre-#248 control
    head-settle_test: all assertions passed
    rc=0
### bash scripts/reconcile-tag-on-merge-callers_test.sh
      ok   comma-separated names keep both, in arrival order
      ok   space-separated names keep both, in ARRIVAL order not request order
      ok   an unmatched allow-list selects nothing
      ok   a whitespace-only allow-list passes everything
    
    reconcile-tag-on-merge-callers_test: 17 passed, 0 failed
    rc=0
### python3 scripts/check-action-runtimes.py
    check-action-runtimes: OK (14 workflow files; 5 audited pins; node24)
    rc=0
### python3 scripts/check-action-runtimes.py --self-test
    check-action-runtimes self-test: OK (Node 20 pins fail)
    rc=0
### bash scripts/bootstrap-repo-main_test.sh
    bootstrap-repo-main_test: all assertions passed
    rc=0
### bash scripts/governance-reconcile.sh --self-test
    governance-reconcile --self-test: all assertions passed
    rc=0
### bash scripts/governance-reconcile.sh
    ok    stall_shapes/detector script — required phrase present in scripts/check-stalled-automerge.sh
    ok    stall_shapes/detector workflow — required phrase present in .github/workflows/stalled-automerge-detector.yml
    ok    stall_shapes/detector script — forbidden phrase absent from scripts/check-stalled-automerge.sh
    ok    stall_shapes/detector workflow — forbidden phrase absent from .github/workflows/stalled-automerge-detector.yml
    ok    unresolvable_pin_infra/validator workflow — required phrase present in .github/workflows/pr-validator.yml
    governance-reconcile: checked=27 skipped=54 fail=0
    rc=0
GATE EXIT: 0

2. Each changed code path executing — the negative control, on the committed file, and the changed checker against the real umbrella checkout:

$ git show HEAD:scripts/governance-reconcile.sh > /tmp/mut/scripts/governance-reconcile.sh
$ git show HEAD:scripts/governance-claims.tsv  > /tmp/mut/scripts/governance-claims.tsv

--- A. the COMMITTED file, unmutated ---
$ bash /tmp/mut/scripts/governance-reconcile.sh --self-test
  governance-reconcile --self-test: all assertions passed
    rc=0   <- GREEN: the committed tree passes its own suite

--- B. MUTATION: the DIRECTORY guard removed   ([[ -d "$rt/$owner" ]] || return 1) ---
  SELF-TEST FAIL: a non-directory owner path was reported as a stale pointer
    rc=1   <- RED, as it must: out9 is the assertion that can tell this guard apart

--- C. MUTATION: the EMPTINESS guard removed   ([[ -n "$(ls -A ...)" ]] || return 1) ---
  SELF-TEST FAIL: an uninitialized submodule path was reported as a stale pointer
    rc=1   <- RED, as it must: out6 is the assertion that covers the emptiness guard

--- D. the PREVIOUS suite (origin/main), the SAME directory-guard mutation ---
  governance-reconcile --self-test: all assertions passed
    rc=0   <- GREEN: the old coverage could not detect the guard's removal.
                  This is the hole this change closes, measured on one input.

--- the checker's EXECUTABLE lines are unchanged (comments stripped) ---
  IDENTICAL — 77 executable lines in the production region:
  sha256(origin/main)=b029598bbd54edf77af93b469d4fed1c
  sha256(HEAD)       =b029598bbd54edf77af93b469d4fed1c
  (the only difference in the production region is the owner_present HEADER COMMENT)

--- the ONLY non-comment lines that changed in the whole file ---
  <   mkdir -p "$tmp/unmaterialized/dotgithub" "$tmp/unmaterialized/absent-repo"
  >   mkdir -p "$tmp/unmaterialized/dotgithub"
  >   mkdir -p "$tmp/filed/dotgithub"
  >   printf 'a stray file that merely shares a sibling repo name\n' > "$tmp/filed/marketplace"
  >   cat > "$tmp/filed-owner.tsv" <<TSV
  > canon	x	marketplace/internals/skills/x/SKILL.md	require	anything
  > TSV
  >   out9="$(check_manifest "$tmp/filed-owner.tsv" "$tmp/filed" 2>&1)" \
  >     || { echo "SELF-TEST FAIL: a non-directory owner path was reported as a stale pointer" >&2; exit 1; }
  >   grep -q 'skipped=1' <<<"$out9" \
  >     || { echo "SELF-TEST FAIL: the non-directory-owner skip was not reported" >&2; exit 1; }

and the strong form — every row of the real manifest asserted against the real org checkout:

$ bash scripts/governance-reconcile.sh --root /home/atrawog/Sync/Atrapub/a/claude/opencharly
  governance-reconcile: checked=81 skipped=0 fail=0
  rc=0   <- every one of the 81 manifest rows asserted against the REAL org checkout

3. Docs-only evidence — N/A — this is not a docs-only change: the diff changes the gate's own test code. The R5 sweep is in the Rulebook compliance section below; the claim-keyed grep and every surviving hit are classified there.

Attribution tier

fully tested and validated. The change-class gate passed on the committed tree with zero warnings, and the changed path — the gate's --self-test, including the new out9 assertion — executed live against the committed file, against a copy of it with each guard removed, and against the real umbrella checkout. The negative control is the proof that the coverage is real: without it, "the suite passes" would say nothing about whether the guard is load-bearing.

Rulebook compliance

  • R0 — skills loaded before the first tool action: /charly-internals:repo-setup and /charly-internals:git-workflow (this repo's own AGENTS.md names both as its R0 set), and the dispatcher rows for a GitHub-workflow change and for a failing gate.
  • R1 — every anomaly got a fresh RCA before remediation. The finding (out5 and out6 asserted the same filesystem state, so the directory guard was never distinctly exercised) came from the validator's PASS on fix(gate): the governance manifest must assert against surfaces that exist (#246) #252, whose report landed one minute before the auto-merge squashed that PR — the mechanism pr-validator: a PASS arms auto-merge within seconds and GitHub merges at once, so a finding raised BY the verdict can never be landed on the PR it was raised on #245 exists for, and the reason the fix is a follow-up PR rather than a commit on fix(gate): the governance manifest must assert against surfaces that exist (#246) #252. Its RCA is on fix(gate): the governance manifest must assert against surfaces that exist (#246) #252's thread: the missed control is that the gate has no discrimination check — the suite asserts what it asserts and prints "all assertions passed" whether a fixture is unique or a duplicate, so two fixtures describing one state are invisible. The root fix is in the control itself: the new assertion is mutation-discriminating by construction and the mutation is pasted in block 2, so the proof lives in the gate rather than in a claim. The gate output has zero warnings. Every finding raised on the earlier PR is fixed here (the two real ones) or already filed (R1: the umbrella's own gate never runs the governance reconcile in its strong form, so a stale sibling pointer is invisible in CI opencharly#674, the third).
  • R2 — no duplication: the fix removes a duplicated fixture rather than adding one. out5 and out6 now cover two different filesystem states, and the taxonomy comment names each block exactly once, so the next reader does not re-derive which assertion proves what.
  • R3 — no workaround: no sleep, no retry, no suppression, no magic number. The control is unchanged and its coverage is proved by mutation — the guard is removed and the suite is shown red, which is the opposite of hiding a failure.
  • R4 — product before prose: the code (the assertion) came first, and the prose follows the code — the header comment now describes the three filesystem states that are not a materialized owner, which is what the code actually decides. The misstatement is corrected where it is used (the script's comments and this body); the released CHANGELOG/2026.297.1200.md is history and the tag v2026.297.1200 points at it, so it is not rewritten — the correction is carried on fix(gate): the governance manifest must assert against surfaces that exist (#246) #252's thread instead.
  • R5 — legacy deleted, with the claim-keyed sweep: the claim is "the stale-pointer control's guards each have an assertion that can fail, and the blocks are named for what they assert". The retired shape is the out5 fixture that created the owner dir it claimed was absent, and the retired prose is the "two false-red guards" taxonomy; both are gone from the file. The sweep over the repo for the retired taxonomy and for the duplicate fixture:
    $ git grep -n "unmaterialized/absent-repo" -- .
    (no hits — the retired fixture line is gone)
    
    

$ git grep -n "false-red guard" -- . # the retired taxonomy
CHANGELOG/2026.297.1200.md:179:- R7 — coverage that fails without the change: the --self-test gained five assertions, and the discriminating one is a stale pointer must FAIL assertion on a materialized owner with a moved file — which the pre-change checker cannot satisfy (it skipped, block 2). The same self-test asserts the two legitimate skips still skip (an uncarried owner, an uninitialized empty submodule path) and the two false-red guards (a same-named root file in a standalone checkout, a name collision in a standalone checkout), so the new control cannot be paid for with a false red. The self-test is already wired into the workflow-checks job, so it is not a new gate step.

$ git grep -n "false-red guard" -- . CHANGELOG/ # ... and in the history files
CHANGELOG/2026.297.1200.md:179:- R7 — coverage that fails without the change: the --self-test gained fiv

=> the ONE surviving hit is CHANGELOG/2026.297.1200.md:179 — the release record of
#252, written by tag-on-merge from that PR's body. The tag v2026.297.1200 points
at that file, so it is HISTORY and is not rewritten: editing it would make the
tagged content differ from the tag. The correction lives where the taxonomy is
used — the script's own comments (the taxonomy block above out4) and this body —
and the misstatement is corrected on #252's thread.

$ grep -nE "out[0-9]+=" scripts/governance-reconcile.sh # every block, named
264: out3="$(check_manifest "$tmp/bare.tsv" "$tmp/standalone" 2>&1)"
301: out4="$(check_manifest "$tmp/stale-pointer.tsv" "$tmp/stale" 2>&1)"
315: out5="$(check_manifest "$tmp/uncarried.tsv" "$tmp/unmaterialized" 2>&1)"
328: out6="$(check_manifest "$tmp/light.tsv" "$tmp/light" 2>&1)"
339: out7="$(check_manifest "$tmp/top-level.tsv" "$tmp/noag" 2>&1)"
354: out8="$(check_manifest "$tmp/coincide.tsv" "$tmp/coincide" 2>&1)"
372: out9="$(check_manifest "$tmp/filed-owner.tsv" "$tmp/filed" 2>&1)"
377: out2="$(check_manifest "$HERE/governance-claims.tsv" "$tmp" 2>&1)" || true

No identifier is removed or renamed, and no transitional or dual-mode path survives.
- **R6** — no unrelated work touched, no destructive git action, no force-push, no bypass, no history rewrite. The branch was cut from a fresh `origin/main` (`b8e54dc`) into its own worktree; `git status` was empty before the commit and after it; one commit, one squash. The umbrella's root checkout, which another session has open with uncommitted work, was **not** used for any git action — the mutation fixtures were built in `/tmp` from the committed file.
- **R7** — coverage that **fails without the change**: the pre-change suite, with the directory guard removed, stays **green** (block 2) — it could not detect the guard's removal; the post-change suite goes **red** at out9 on the same mutation, and red at out6 when the emptiness guard is removed. The changed path **executed live**: the self-test ran on the committed tree (block 1), the mutated copies ran live (block 2), and the checker ran against the **real** umbrella checkout with every row asserted (block 2). The emitted artifact here is the gate's verdict, and it was validated at its actual boundary — the script's own exit code and output.
- **R8** — no live service is mocked in place of a real one. The strong form ran against a real org checkout; the mutation fixtures are copies of the committed file; nothing is faked.
- **R9** — N/A — this repo contains no Go source and ships no `charly` binary, so there is no binary to rebuild and `charly version` does not apply to it.
- **R10** — the change-class gate for a workflow change in this repo is `.github/workflows/workflow-checks.yml`, run on the committed tree (block 1): every offline step returned rc=0 and produced no warning. There is no `disposable: true` target in this repo, so no bed applies. The coverage that fails without the change is shipped in the same cutover (block 2).
- **RDD / ADE / SDD** — RDD: the one high-risk assumption is **which filesystem state distinguishes the two owner guards**; it was **measured**, not reasoned about — the two mutations and the old suite's blindness are in block 2, and the answer is the non-directory owner, not the absent owner. ADE: N/A — no candy and no `plan:`. SDD: N/A — no schema and no `.cue`.
- **Hard cutover** — one cutover in one PR: the absent-owner fixture, the new discriminating assertion, the corrected taxonomy and the corrected header comment land together. No phase, TODO or deferred cleanup is left inside the scope. One commit, one squash.
- **Coordination** — the scope is not contended: the issue was filed and CLAIMED before the branch was cut, no other PR or issue in this repo touches the governance manifest or its script, and the one other open PR (#207, a different `setup-go` pin) does not overlap this diff. `Agent:` FIRST and `Assisted-by:` LAST on the issue and the PR.
- **Kernel/plugin boundary law** — N/A — no kernel, no sdk, no plugin, no kind.
- **Disposable-only autonomy** — N/A — nothing was destroyed or rebuilt; this repo has no disposable target and no autonomous mutation was performed.
- **Clean architecture + Go gates** — N/A — no Go source in this diff. The shell coverage is the script's own `--self-test`, already wired into the unchanged `workflow-checks.yml` — no second copy of the logic and no new gate step.

## Filed issues

None filed by this change. The accounting of the round, for completeness: the two real findings the validator raised on #252 are **fixed here** — that PR was already merged when its verdict landed, so the fix could not land on it (opencharly/.github#245) — and the third observation (the control only fires in strong form until the umbrella's own gate runs it) is **already filed** as opencharly/opencharly#674, which this change does not touch and does not depend on.

*Agent: charly*
*Assisted-by: Claude Code minimax minimax-m3.1-flash:cloud (fully tested and validated)*

The stale-pointer control added by #252 (#246) has two owner
guards: the owner path must be a DIRECTORY, and it must not be EMPTY. The
self-test asserted the empty-dir case TWICE — out5 created an empty owner dir
while claiming to test an absent one — so the directory guard had no coverage
that could fail. The validator's PASS on #252 found it.

The fix is NOT an assertion for the absent case: an absent owner and an empty
owner are the same verdict, and `ls -A` on a missing directory yields nothing,
so an absent-owner assertion cannot fail if the `-d` guard is removed — a test
that cannot fail is invalid. The guard is load-bearing only when the owner path
is a regular FILE, where `ls -A` prints the file's name and the owner would
look materialized, turning the control into a false red.

So: out5 becomes the truly-absent case (the empty dir goes), out6 keeps the
empty-dir case (the two states are now distinct), and a new out9 asserts that a
non-directory owner path SKIPS. Removing the `-d` guard now turns the suite
red at out9; removing the emptiness guard turns it red at out6. The old suite
stayed GREEN with the `-d` guard removed — the coverage could not fail.

The prose is corrected as well: the self-test's blocks are two kinds of
assertion — FAIL (out4, out7) and SKIP (out3, out5, out6, out8, out9) — and
the header comment now names the three filesystem states that are not a
materialized owner.

Refs #253

Assisted-by: Claude Code minimax minimax-m3.1-flash:cloud (fully tested and validated)
@github-actions

Copy link
Copy Markdown
Contributor

Review — PASS

What the diff actually does

The single changed file, scripts/governance-reconcile.sh, has three behavioural edits, all inside the --self-test block, plus comment-only edits:

  1. out5 (absent-repo): the mkdir -p "$tmp/unmaterialized/dotgithub" "$tmp/unmaterialized/absent-repo" line drops absent-repo, so the owner directory is now genuinely absent. Comment rewritten to say so. ✔ matches the body's claim.
  2. out6 (empty-owner): unchanged fixture, comment now states the distinction ("here the owner dir EXISTS and is empty; there it does not exist at all"). ✔
  3. out9 (new): creates $tmp/filed/dotgithub and a regular file $tmp/filed/marketplace, asserts check_manifest on marketplace/internals/skills/x/SKILL.md exits 0 and reports skipped=1. ✔

Correctness of the coverage claim (re-derived, not trusted)

I reconstructed the two guards from the header comment ([[ -d "$rt/$owner" ]] and [[ -n "$(ls -A "$rt/$owner")" ]]) and checked each fixture against a removal of each guard:

  • out5 (absent dir) — with -d removed, ls -A on a missing dir yields empty stdout → emptiness guard still skips. Skips either way ⇒ cannot discriminate. Body is correct.
  • out6 (empty dir) — with -d removed, ls -A empty → still skips; but with the emptiness guard removed, -d passes and the missing some.md yields a stale-pointer red ⇒ out6 catches that guard. Body is correct.
  • out9 (file owner) — with -d removed, ls -A on a file prints the file's name ⇒ non-empty ⇒ owner looks materialized ⇒ the absent SKILL.md produces a false stale-pointer red ⇒ out9 is the discriminating assertion for -d. Body is correct.

So the claim "the directory guard had no coverage that could fail" and "out9 is the only skip assertion that can tell the guards apart" is sound, and the mutation evidence in block 2 (A green / B red on -d removal / C red on emptiness removal / D pre-change green on same mutation) is consistent with the code. The assertion genuinely fails without the change.

Honesty / internal consistency

  • File count (1), ±counts (+58/-8) match the file header. ✔
  • "Checker executable lines byte-identical to origin/main": the hunks touch only comments and the self-test region — no production line of owner_present is modified. The block-2 sha256 (b029598… on both sides) is present and consistent with the diff. ✔
  • No secrets, no injection surface; the only new write is a text file under the mktemp fixture. ✔
  • Scope is coherent: it removes a duplicate fixture and adds one discriminating assertion — no dead/dual path left behind, taxonomy comment names every block exactly once.

Minor (non-blocking) observation

The Summary, Change-classification, and Breaking-change sections cite "block 3" as the source of the byte-identical sha256 proof, but block 3 is labelled "Docs-only evidence — N/A"; the actual proof is in block 2. This is a cross-reference typo only — the evidence exists and is correct — so it does not affect the verdict. Worth a one-word fix in a follow-up or on the thread.

Comments

The thread is empty (0 comments), so there is nothing to disposition; no maintainer sign-off is claimed or required, and the change stands on its own evidence above.

The change does what the body says, ships a test that fails without it, and is scoped and clean.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) October 24, 2026 12:10
@github-actions
github-actions Bot merged commit aee7c3f into main Oct 24, 2026
4 checks passed
charly-auto-merge Bot added a commit that referenced this pull request Oct 24, 2026
@aitrawog

Copy link
Copy Markdown
Contributor Author

STATUS — the PR is MERGED (squash aee7c3f, 2026-10-24T12:10:28Z, tag v2026.297.1210, CHANGELOG/2026.297.1210.md written by tag-on-merge). The validator's verdict was PASS (run 40092767805, ollama-cloud/deepseek-v4.1-flash — action-review), with all four checks SUCCESS.

The one non-blocking observation is ACCEPTED, and the disposition is here.

The Summary, Change-classification, and Breaking-change sections cite "block 3" as the source of the byte-identical sha256 proof, but block 3 is labelled "Docs-only evidence — N/A"; the actual proof is in block 2. This is a cross-reference typo only — the evidence exists and is correct — so it does not affect the verdict.

The finding is a real typo: the body's Summary and its T4 bullet point at block 3 (the docs-only N/A) for the sha256 proof, and the proof is in block 2 (lines 116–117 of the released changelog, sha256(origin/main)=sha256(HEAD)=b029598bbd54edf77af93b469d4fed1c on 77 executable lines). The reviewer re-derived the sha256 and confirmed it is "present and consistent with the diff".

It could not be landed on this PR, for the mechanism #245 exists for: the validate / arm job completed at 12:10:04Z and GitHub merged the PR 24 seconds later, at 12:10:28Z. A finding raised by the verdict has no compliant path onto the PR it was raised on.

The suggested route — "a one-word fix in a follow-up" — is declined on evidence, and the reason is the org's own mechanism. The typo now lives in CHANGELOG/2026.297.1210.md, which tag-on-merge wrote from this PR's body at merge time. This repo's AGENTS.md: "History lives in CHANGELOG/ (written by tag-on-merge at merge time); the PR body IS the changelog." The measured practice matches the rule:

$ git log --format='%h %s' --diff-filter=M --name-only -- CHANGELOG/
  (empty — across this repo's ENTIRE history, no released changelog file has ever
   been MODIFIED; every one of them was written once, by tag-on-merge, from a PR body)

A follow-up PR that hand-edits CHANGELOG/2026.297.1210.md would therefore be the first hand-edit in this repo's history: it would put an author's hand into a file the org's own mechanism owns, and it would make main's copy of that release record differ from the tree the tag v2026.297.1210 points at. The org's mechanism for a changelog entry is the body of the PR that makes the change — so a correction belongs in the record of the change that carries it, not in a hand-edit of a past release.

What is true, and what is durable. The claim the wrong pointer supports — "the checker's executable lines are byte-identical to origin/main" — is true, and its proof is in the same file, 90 lines below the typo. The typo is confined to two prose sentences in a release record: no code, script, comment or gate carries it, and the part that is durable landed in this PR — the taxonomy in scripts/governance-reconcile.sh's own comments now names every block for what it asserts (FAIL: out4, out7; SKIP: out3, out5, out6, out8, out9), and the discriminating assertion (out9) is on main today, with the directory guard provably load-bearing.

The correction of record is this thread. And the mechanism that made a one-word body fix un-landable — a verdict-raised finding arriving after the merge — is filed as #245, with the operator's own choice of direction (an arming window, an explicit hold, or a written rule that verdict-raised findings land in a successor PR).

Agent: charly

Assisted-by: Claude Code minimax minimax-m3.1-flash:cloud (fully tested and validated)

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