Repository navigation
test(gate): give the owner-dir guard an assertion that can fail (#253) - #254
Conversation
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)
Review — PASSWhat the diff actually doesThe single changed file,
Correctness of the coverage claim (re-derived, not trusted)I reconstructed the two guards from the header comment (
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 Honesty / internal consistency
Minor (non-blocking) observationThe 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. CommentsThe 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. |
|
STATUS — the PR is MERGED (squash The one non-blocking observation is ACCEPTED, and the disposition is here.
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, It could not be landed on this PR, for the mechanism #245 exists for: the 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 A follow-up PR that hand-edits What is true, and what is durable. The claim the wrong pointer supports — "the checker's executable lines are byte-identical to 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) |
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 toorigin/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:
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).ls -Aon a missing directory yields nothing — so an absent-owner assertion cannot fail if the-dguard is removed, and a test that cannot fail is invalid. The guard is load-bearing only when the owner path is a regular file, wherels -Aprints 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 theworkflow-checksjob of.github/workflows/workflow-checks.ymlruns 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.charlyrepo — this repo carries no hook script, verified: no.githooks/here), the pre-commit gate (same), the branch-protection config (scripts/org-ruleset.sh, untouched), this validator's spec (.github/workflows/pr-validator.yml, untouched), and the project rulebook's landing / attribution / no-force-push / no-direct-push rules (this repo'sAGENTS.mdand the umbrella's, both untouched). The other T3 bullets are answered explicitly: scope match — the body above accounts for every line of the diff; secrets & exfiltration — none (the only new write is a text file inside amktemp -dfixture); weakened guardrails — no gate is disabled or loosened, and the guard set inowner_presentis unchanged (proved byte-for-byte in block 3); supply chain — no new dependency, no version pin, no@githubref, and the CI step that runs this script is unchanged (no new gate step is introduced; the--self-teststep already existed). The change strengthens the gate: it adds an assertion that can fail where the old suite had only one that could not.How tested
.github/workflows/workflow-checks.yml(theworkflow-checksjob). The change class is hook/workflow; this repo ships no candy or box, nocharlybinary and nodisposable: truedeploy, so this repo's CI is the only place its own copy of the script executes, and its--self-testis the only place its logic can be exercised.charly check run <bed>beds: it is the org's GitHub-configuration repo (workflows, shell and Python owner scripts), not a deployable. There is nodisposable: truetarget in it, so no bed applies to this change class; the gate above is the class's gate.charlybinary, socharly versiondoes not apply to it.--self-testblock; 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.1. Fresh-rebuild gate output — the change-class gate, run on the committed tree (
git status --porcelainempty; the commands are the ones.github/workflows/workflow-checks.ymlruns, minus the twouses:runtime steps, which need GitHub's own runners and run in this PR's CI):2. Each changed code path executing — the negative control, on the committed file, and the changed checker against the real umbrella checkout:
and the strong form — every row of the real manifest 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
/charly-internals:repo-setupand/charly-internals:git-workflow(this repo's ownAGENTS.mdnames both as its R0 set), and the dispatcher rows for a GitHub-workflow change and for a failing gate.CHANGELOG/2026.297.1200.mdis history and the tagv2026.297.1200points 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.$ git grep -n "false-red guard" -- . # the retired taxonomy
CHANGELOG/2026.297.1200.md:179:- R7 — coverage that fails without the change: the
--self-testgained 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 theworkflow-checksjob, 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-testgained 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