Skip to content

fix(gate): the claims manifest must name LIVE surfaces, and pin every one of them - #258

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/governance-claims-repoint
Oct 27, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
fix/governance-claims-repoint

Conversation

@aitrawog

@aitrawog aitrawog commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Closes #250
Closes #251
Closes #257

Summary

The org archived layer-charly-internals, its skills moved into the umbrella's own .agents/skills/… trees, and this repo's governance surfaces still pointed at the retired carrier. Three filed findings, one theme, one gate:

  • R1: 19 of the 81 governance-claims rows name owner repos the org no longer carries — the archived layer-charly-internals, whose skills moved to the umbrella, so those claims are unasserted org-wide #257 — the 19 stale pointers. scripts/governance-claims.tsv named 19 of its 81 rows at layer-charly-internals/… (17 rows) and layer-charly-internals-extra/… (2 rows). The org no longer carries those repos, so the checker skipped every one of them: a listed surface that cannot be reached was indistinguishable from one that was never checked. The rules those rows claim — the takeover window, the sign-off account gate, the successor protocol, the read-in-full rule, the RCA-on-validation-failure rule, the footer order, the re-run manual, the auto-close threshold, fix-or-file and the coordination vocabulary — were unasserted org-wide. The rows are repointed at the home that now holds the prose: the umbrella's own .agents/skills/… trees. The prose is unchanged; only the pointers move.
  • R1: one manifest surface is named only by a forbid row — nothing pins that the path IS the surface #250 — the unanchored surface. One path was named by a forbid row only, and a forbid row asserts that a phrase is absent — vacuously true of any file at that path, including a file that holds no rule at all. So the table could not say "this path IS the surface", and the row could never go red. The surface is now pinned by a require row, and the class is made structural: the checker now FAILS a manifest that names a path no require row pins. The verdict is about the table, not about the checkout, so an unanchored path fails in every root — including one that does not carry the surface at all.
  • R1: scripts/strip-config-version.sh cites a byte figure that is no longer true — layer-charly-internals/charly.yml is 6,692 bytes, not 1,038,418 #251 — the dead byte figure. scripts/strip-config-version.sh justified its > 1 MB fallback with a figure that no longer exists: it cited layer-charly-internals/charly.yml as 1,038,418 bytes, and that repo is archived. The fallback is kept — it is a forward guard, and the branch is not dead code: scripts/strip-config-version_test.sh exercises it with a > 1 MB fixture on every run of this repo's gate — and the comment now states the threshold, what triggers it, and a live measurement (the org's largest charly.yml is charly/charly.yml at 316,230 bytes, measured 2026-10-27).

The prose that names the retired carrier is swept in the same phase (R5): the manifest's header, the checker's three path examples, and the two comments in strip-config-version.sh.

file what it changes
scripts/governance-claims.tsv 19 rows repointed at the umbrella's own .agents/skills/… trees; 1 require row added to pin the cutover-policy surface; the header states the anchor rule and the live path forms
scripts/governance-reconcile.sh the anchor control: a path no require row pins FAILS, in every root; its self-test coverage (out10–out12) and the fixtures it needs; the retired carrier's name swept from the path examples
scripts/strip-config-version.sh the two comments that cited the retired carrier's dead byte figure — the threshold, its trigger, and a live measurement

Change classification

hook/workflow — this repo's own gate script, scripts/governance-reconcile.sh, and the claims table it asserts. The workflow-checks job of .github/workflows/workflow-checks.yml runs it on every PR of this repo, and the umbrella's own verify task now runs it in its strong form (opencharly/opencharly#674). The change strengthens the gate: it adds a failure condition where the old suite could only skip.

Closes / relates to: Closes #250, #251, #257. Relates to #246 and #252 (the stale-pointer control these complete), #253 (the coverage that can fail), and the consumer leg that made the strong form run at all: opencharly/opencharly#674.

Breaking change + rollback: none — no public surface is removed or renamed, and no consumer reads this script's exit code. The manifest's row paths change, but every claim still resolves and every phrase is still asserted (block 2: checked=82 skipped=0 fail=0, against the shipped manifest's checked=62 skipped=19 fail=0). The new control can only turn a defective table red — the shipped table, before this change, is one it names (block 2, control B). If this PR proves wrong it is reverted by a follow-up PR; no tag, publication or consumer artifact depends on it.

T4: not triggered. T4 fires on a change touching the T3 "weakened guardrails" list. This diff touches three files, and it is on none of that list: the pre-push gate (the clone-level hook, installed from the charly repo — 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's AGENTS.md and the umbrella's, both untouched). The other T3 bullets are answered explicitly: scope match — the table above accounts for every line of the diff, and the body above describes the change's real content (a new failure condition, 19 repointed rows plus one anchor row, two comments); secrets & exfiltration — none (the only new write is a text file inside a mktemp -d fixture); weakened guardrails — no gate is disabled or loosened: the diff adds a failure condition and asserts a larger set of surfaces (skipped=19 → skipped=0); supply chain — no new dependency, no version pin, no @github ref change, and the CI steps that run these scripts are unchanged. The precedent in this repo is exact: #218, #242, #252 and #254 all changed this repo's own gate and merged on that ground.

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, against real org checkouts. The changed path is the checker's own executable logic. It ran live (a) against the real umbrella checkout — every row asserted, none skipped; (b) in the standalone .github checkout — the CI form the job runs; (c) in the self-test, whose three new assertions can fail; and (d) as the negative control, on one input, both ways: the old checker accepts an unanchored manifest (exit 0) where the new one rejects it (exit 1), and the new checker against the old manifest names the one unanchored path the shipped table actually had. The strip-config-version.sh branch is exercised by this repo's own test, with its > 1 MB fixture, on the same run.
  • 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):

$ git status --porcelain
$ git rev-parse HEAD
64a77de9441004e95538eb731071b543c941652a

### 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
head-settle_test: all assertions passed
rc=0
### bash scripts/reconcile-tag-on-merge-callers_test.sh
reconcile-tag-on-merge-callers_test: 17 passed, 0 failed
rc=0
### bash scripts/bootstrap-repo-main_test.sh
bootstrap-repo-main_test: all assertions passed
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/governance-reconcile.sh --self-test
governance-reconcile --self-test: all assertions passed
rc=0
### bash scripts/governance-reconcile.sh
governance-reconcile: checked=27 skipped=55 fail=0
rc=0

Zero warnings, zero failures: grep -cE "^\[fail\]|^FAIL" over the gate log → 0, and grep -ciE "warn" → 0.

2. Each changed code path executing — the strong form against the real umbrella checkout (every surface this checkout carries is asserted; the 19 repointed skill-source rows are ok, never skip):

$ bash scripts/governance-reconcile.sh --root <the umbrella checkout>
governance-reconcile: root=/home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/govrec/opencharly manifest=/home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/govrec/dotgithub/scripts/governance-claims.tsv
ok    footer_order/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    footer_order/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
ok    footer_order/git-workflow skill source — required phrase present in .agents/skills/git-workflow/SKILL.md
ok    footer_order/git-workflow skill source — forbidden phrase absent from .agents/skills/git-workflow/SKILL.md
ok    footer_order/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/SKILL.md
ok    footer_order/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/SKILL.md
ok    takeover_window/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    takeover_window/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
ok    takeover_window/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    takeover_window/git-workflow skill source — forbidden phrase absent from .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    takeover_window/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    takeover_window/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    comment_first_takeover/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    comment_first_takeover/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    comment_first_takeover/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    successor_protocol/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    successor_protocol/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    successor_protocol/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    signoff_account_gate/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
ok    signoff_account_gate/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/git-workflow skill source — forbidden phrase absent from .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    rerun_manual/git-workflow skill source — required phrase present in .agents/skills/git-workflow/SKILL.md
ok    rerun_manual/git-workflow skill source — forbidden phrase absent from .agents/skills/git-workflow/SKILL.md
ok    rerun_manual/git-workflow skill source — forbidden phrase absent from .agents/skills/git-workflow/SKILL.md
ok    rerun_manual/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/SKILL.md
ok    rerun_manual/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/SKILL.md
ok    rerun_manual/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    rerun_manual/dotgithub README — required phrase present in dotgithub/README.md
ok    rerun_manual/dotgithub README — forbidden phrase absent from dotgithub/README.md
ok    rerun_manual/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    auto_close_threshold/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_infra_excluded/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_no_one_shot/validator workflow — forbidden phrase absent from dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_threshold/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_infra_excluded/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    candy_gate_single_pin/org-wide candy validate reusable — required phrase present in dotgithub/.github/workflows/candy-validate.yml
ok    candy_gate_no_hand_pin/org-wide candy validate reusable — forbidden phrase absent from dotgithub/.github/workflows/candy-validate.yml
ok    auto_close_threshold/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    auto_close_threshold/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    ci_gate_mechanical/org-wide required workflow — required phrase present in dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — forbidden phrase absent from dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator rulebook — required phrase present in action-review/charly.yml
ok    ci_gate_mechanical/validator rulebook — required phrase present in action-review/charly.yml
ok    ci_gate_mechanical/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    same_repo_gate/org-wide required workflow — required phrase present in dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    same_repo_gate/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    fix_or_file/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    fix_or_file/strict-policy skill source — required phrase present in .agents/skills/strict-policy/SKILL.md
ok    fix_or_file/validator rulebook — required phrase present in action-review/charly.yml
ok    fix_or_file/cutover-policy skill source — required phrase present in .agents/skills/cutover-policy/SKILL.md
ok    fix_or_file/cutover-policy skill source — forbidden phrase absent from .agents/skills/cutover-policy/SKILL.md
ok    fix_or_file/strict-policy skill source — forbidden phrase absent from .agents/skills/strict-policy/SKILL.md
ok    fix_or_file/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    filed_issues_section/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    filed_issues_section/PR template — required phrase present in dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    filed_issues_section/PR template — required phrase present in dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    read_in_full/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    read_in_full/git-workflow skill source — required phrase present in .agents/skills/git-workflow/SKILL.md
ok    validation_failure_rca/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    validation_failure_rca/git-workflow skill source — required phrase present in .agents/skills/git-workflow/SKILL.md
ok    validation_failure_rca/validator rulebook — required phrase present in action-review/charly.yml
ok    inform_sessions/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    inform_sessions/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    inform_sessions/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    inform_sessions/git-workflow skill source — required phrase present in .agents/skills/git-workflow/references/multi-repo-coordination.md
ok    validator_enforces/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    validator_enforces/validator rulebook — required phrase present in action-review/charly.yml
ok    validator_enforces/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    rule_numbering/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
ok    rule_numbering/PR template — forbidden phrase absent from dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    stall_shapes/detector script — required phrase present in dotgithub/scripts/check-stalled-automerge.sh
ok    stall_shapes/detector workflow — required phrase present in dotgithub/.github/workflows/stalled-automerge-detector.yml
ok    stall_shapes/detector script — forbidden phrase absent from dotgithub/scripts/check-stalled-automerge.sh
ok    stall_shapes/detector workflow — forbidden phrase absent from dotgithub/.github/workflows/stalled-automerge-detector.yml
ok    unresolvable_pin_infra/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
governance-reconcile: checked=82 skipped=0 fail=0

Control A — the coverage must fail without the change (R7). The old checker (from origin/main) against an unanchored manifest, and the new checker against the same manifest, on one input:

$ bash <origin/main's governance-reconcile.sh> --root $F --manifest $F/unanchored.tsv
governance-reconcile: root=/tmp/pt299/anchor-fixture manifest=/tmp/pt299/anchor-fixture/unanchored.tsv
ok    canon/a — required phrase present in dotgithub/one.txt
ok    legacy/b — forbidden phrase absent from sibling/two.txt
governance-reconcile: checked=2 skipped=0 fail=0

$ bash scripts/governance-reconcile.sh --root $F --manifest $F/unanchored.tsv
governance-reconcile: root=/tmp/pt299/anchor-fixture manifest=/tmp/pt299/anchor-fixture/unanchored.tsv
FAIL  legacy/b — UNANCHORED surface: sibling/two.txt is named by a 'forbid' row only, so nothing pins that the path IS the surface (opencharly/.github#250)
ok    canon/a — required phrase present in dotgithub/one.txt
ok    legacy/b — forbidden phrase absent from sibling/two.txt
governance-reconcile: checked=2 skipped=0 fail=1

Control B — the shipped table was defective, and the fix is load-bearing. The new checker against the old manifest, on the real umbrella checkout: it names the one unanchored path the shipped table had, and the old manifest is the one that skips:

$ bash scripts/governance-reconcile.sh --root <the umbrella checkout> --manifest <origin/main's governance-claims.tsv>
governance-reconcile: root=/home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/govrec/opencharly manifest=/tmp/pt299/old-claims.tsv
FAIL  fix_or_file/cutover-policy skill source — UNANCHORED surface: layer-charly-internals/.agents/skills/cutover-policy/SKILL.md is named by a 'forbid' row only, so nothing pins that the path IS the surface (opencharly/.github#250)
ok    footer_order/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    footer_order/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
skip  footer_order/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
skip  footer_order/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
ok    footer_order/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/SKILL.md
ok    footer_order/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/SKILL.md
ok    takeover_window/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    takeover_window/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
skip  takeover_window/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
skip  takeover_window/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    takeover_window/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    takeover_window/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    comment_first_takeover/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  comment_first_takeover/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    comment_first_takeover/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    successor_protocol/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  successor_protocol/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    successor_protocol/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    signoff_account_gate/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
skip  signoff_account_gate/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
skip  signoff_account_gate/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    signoff_account_gate/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
ok    signoff_account_gate/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/references/multi-repo-coordination.md
skip  rerun_manual/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
skip  rerun_manual/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
skip  rerun_manual/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
ok    rerun_manual/marketplace projection — required phrase present in marketplace/internals/skills/git-workflow/SKILL.md
ok    rerun_manual/marketplace projection — forbidden phrase absent from marketplace/internals/skills/git-workflow/SKILL.md
ok    rerun_manual/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    rerun_manual/dotgithub README — required phrase present in dotgithub/README.md
ok    rerun_manual/dotgithub README — forbidden phrase absent from dotgithub/README.md
ok    rerun_manual/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    auto_close_threshold/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_infra_excluded/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_no_one_shot/validator workflow — forbidden phrase absent from dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_threshold/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    auto_close_infra_excluded/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    candy_gate_single_pin/org-wide candy validate reusable — required phrase present in dotgithub/.github/workflows/candy-validate.yml
ok    candy_gate_no_hand_pin/org-wide candy validate reusable — forbidden phrase absent from dotgithub/.github/workflows/candy-validate.yml
ok    auto_close_threshold/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  auto_close_threshold/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    ci_gate_mechanical/org-wide required workflow — required phrase present in dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator workflow — forbidden phrase absent from dotgithub/.github/workflows/pr-validator.yml
ok    ci_gate_mechanical/validator rulebook — required phrase present in action-review/charly.yml
ok    ci_gate_mechanical/validator rulebook — required phrase present in action-review/charly.yml
ok    ci_gate_mechanical/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    same_repo_gate/org-wide required workflow — required phrase present in dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    same_repo_gate/org-wide required workflow — forbidden phrase absent from dotgithub/.github/workflows/org-wide-pr-validator-required.yml
ok    fix_or_file/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  fix_or_file/strict-policy skill source — layer-charly-internals-extra/.agents/skills/strict-policy/SKILL.md not in this checkout
ok    fix_or_file/validator rulebook — required phrase present in action-review/charly.yml
skip  fix_or_file/cutover-policy skill source — layer-charly-internals/.agents/skills/cutover-policy/SKILL.md not in this checkout
skip  fix_or_file/strict-policy skill source — layer-charly-internals-extra/.agents/skills/strict-policy/SKILL.md not in this checkout
ok    fix_or_file/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    filed_issues_section/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    filed_issues_section/PR template — required phrase present in dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    filed_issues_section/PR template — required phrase present in dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    read_in_full/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  read_in_full/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
ok    validation_failure_rca/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  validation_failure_rca/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/SKILL.md not in this checkout
ok    validation_failure_rca/validator rulebook — required phrase present in action-review/charly.yml
ok    inform_sessions/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    inform_sessions/umbrella AGENTS.md — required phrase present in AGENTS.md
skip  inform_sessions/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
skip  inform_sessions/git-workflow skill source — layer-charly-internals/.agents/skills/git-workflow/references/multi-repo-coordination.md not in this checkout
ok    validator_enforces/umbrella AGENTS.md — required phrase present in AGENTS.md
ok    validator_enforces/validator rulebook — required phrase present in action-review/charly.yml
ok    validator_enforces/validator rulebook — forbidden phrase absent from action-review/charly.yml
ok    rule_numbering/umbrella AGENTS.md — forbidden phrase absent from AGENTS.md
ok    rule_numbering/PR template — forbidden phrase absent from dotgithub/.github/PULL_REQUEST_TEMPLATE.md
ok    stall_shapes/detector script — required phrase present in dotgithub/scripts/check-stalled-automerge.sh
ok    stall_shapes/detector workflow — required phrase present in dotgithub/.github/workflows/stalled-automerge-detector.yml
ok    stall_shapes/detector script — forbidden phrase absent from dotgithub/scripts/check-stalled-automerge.sh
ok    stall_shapes/detector workflow — forbidden phrase absent from dotgithub/.github/workflows/stalled-automerge-detector.yml
ok    unresolvable_pin_infra/validator workflow — required phrase present in dotgithub/.github/workflows/pr-validator.yml
governance-reconcile: checked=62 skipped=19 fail=1

Control C — the > 1 MB fallback branch is live, not dead code. This repo's own test drives it with a > 1 MB fixture on every run of the gate (scripts/strip-config-version_test.sh:361-365):

# ---- > 1 MB fallback: Contents API omits content; the git blob API supplies it ---
out="$(FORCE_BIGFILE=1 strip bigfile 2>&1)" || { echo "FAIL: the large-file fallback must classify, not fail" >&2; exit 1; }
grep -q 'wrote bigfile:charly.yml' "$STATE/transcript" || { echo "FAIL: a > 1 MB charly.yml must still be stripped via the blob fallback" >&2; exit 1; }
### bash scripts/strip-config-version_test.sh
strip-config-version_test: all assertions passed
rc=0

3. Docs-only evidence — the R5 sweep for the retired carrier. The claim is "no live surface names the retired carrier", and the grep is claim-keyed over the whole tree:

$ git grep -n "layer-charly-internals" -- . ':!CHANGELOG'
README.md:121:  anything (opencharly/layer-charly-internals#71). The re-run is a new attempt on the SAME

One survivor, classified. It is a provenance citation of a live operational rule, not a pointer to a surface that must exist: README.md documents the REST re-run for a body-only fix and cites the issue that measured gh's behaviour. That issue is still readable (closed, opencharly/layer-charly-internals#71, "gh run rerun cannot re-run the org-required validator"), and the claim it supports is explicitly version-scoped ("measured on 2.100.0"; the local gh is 2.103.0), so it is neither a dead pointer nor a false claim. Every other hit was CHANGELOG history. The three files this diff opens named the retired carrier as a place a rule lives — and all three now name the live home.

Attribution tier

fully tested and validated. The change-class gate passed on the committed tree with zero warnings, and the changed path — the checker's executable logic — executed live against the real umbrella checkout, in the standalone .github checkout, in the self-test, and as the negative control on one input in both directions (block 2). The coverage is real: without the change, the control's own fixture is accepted (exit 0); with it, it is rejected (exit 1).

Rulebook compliance

  • R0 skills: this repo's AGENTS.md names /charly-internals:repo-setup and /charly-internals:git-workflow for R0; both were read before this leg's first tool action, and the umbrella's dispatcher added /charly-internals:root-cause-analyzer (the RCA of each finding), /charly-internals:strict-policy (the R1 classification, the mutation/coverage discipline) and /charly-internals:cutover-policy (the sizing law that put three filed findings into one batched cutover instead of three solo ceremonies). The .github repo's own rulebook was read before acting here.
  • R1 RCA + zero warnings + fix it or file it: three findings, each root-caused and each fixed in this change — R1: 19 of the 81 governance-claims rows name owner repos the org no longer carries — the archived layer-charly-internals, whose skills moved to the umbrella, so those claims are unasserted org-wide #257 (the org archived the carrier, so the rows' owners are not carried: the checker skips them and a skip is indistinguishable from "never checked"), R1: one manifest surface is named only by a forbid row — nothing pins that the path IS the surface #250 (a forbid-only row is vacuously true of any file, so nothing pins the path as the surface), R1: scripts/strip-config-version.sh cites a byte figure that is no longer true — layer-charly-internals/charly.yml is 6,692 bytes, not 1,038,418 #251 (the cited byte figure died with the archived repo). The gate's output carries zero warnings (grep -ciE "warn" → 0) and zero failures. Nothing raised on this PR is filed away: this PR files no new issue, because it closes the three that were filed. Every finding the work surfaced is either fixed here or was already filed, with its RCA on the issue.
  • R2 no duplication: one canonical implementation — the checker, the claims table and the stale-pointer control stay in this repo, and the umbrella invokes them (it copies none of that logic); the new anchor control is one function, unanchored_surfaces, called once.
  • R3 no workaround: no sleep, no retry, no suppressed finding, no ad-hoc service command. The new control is a real assertion with a real exit status, and its negative control is measured, not asserted.
  • R4 product before prose: the product was fixed first — the manifest and the checker — and the prose (the header, the path examples, the two comments) was corrected to match what the code now does. The corrected figure in strip-config-version.sh is a live measurement (316,230 bytes, measured 2026-10-27), not a remembered one.
  • R5 hard cutover + grep: the retired carrier's name is gone from every file this diff opens, and the claim-keyed sweep over the whole tree is pasted in block 3, with its one survivor classified. No transitional path survives: the 19 old paths are gone from the table, not aliased, and the manifest is a hard cutover to the live home.
  • R6 git safety: the work ran in its own session worktree (.worktrees/govrec/dotgithub), branched off fresh origin/main; git status --porcelain was empty before the commit; no history was rewritten, no force-push, no hook bypass, and nothing was pushed to main.
  • R7 prove behavior + emitted artifacts: the coverage fails without the change, measured on one input in both directions (block 2, control A), and the fix is load-bearing against the shipped table (control B). The new control's three assertions (out10–out12) are in the self-test, so a future change that breaks them turns the gate red.
  • R8 live or skip: nothing is mocked — the checker ran against the real umbrella checkout, the real standalone .github checkout, and the real shipped manifest; the strip-config-version branch ran against this repo's own mock-gh fixture, which is the fixture the repo's own gate uses for that script.
  • R9 binary == source + deps: N/A — this repo ships no binary and adds no runtime dependency.
  • R10 disposable + coverage: this repo has no disposable: true target and no charly check run <bed> bed; the change class's gate is this repo's own CI job, run on the committed tree with zero warnings, and the coverage that would fail without the change ships with it (the self-test's out10–out12).
  • RDD / ADE / SDD: N/A — no charly.yml, no box, no .cue schema. The high-risk unknown here (does the repointed table still assert every claim?) was proven on a live org checkout before the change was written into the table: checked=82 skipped=0 fail=0.
  • Hard cutover: ONE cutover, ONE PR, ONE commit on this repo — the three filed findings of one theme (the retired carrier's pointers) land together, because the batch law says a cutover is the largest coherent scope one gate can honestly prove. No "Phase 2", no deferred row, no TODO left in scope. The consumer leg (the umbrella's dotgithub pin bump) follows this merge, producer-first.
  • Coordination: the scope is claimed — R1: 19 of the 81 governance-claims rows name owner repos the org no longer carries — the archived layer-charly-internals, whose skills moved to the umbrella, so those claims are unasserted org-wide #257 is assigned to this session's account, and R1: one manifest surface is named only by a forbid row — nothing pins that the path IS the surface #250 and R1: scripts/strip-config-version.sh cites a byte figure that is no longer true — layer-charly-internals/charly.yml is 6,692 bytes, not 1,038,418 #251 are unclaimed and are closed by this PR. Agent: and Assisted-by: carry the two-line footer in the canonical order.
  • Kernel/plugin boundary law: N/A — no core, sdk or plugin change.
  • Disposable-only autonomy: N/A — no autonomous destroy or rebuild; no target was touched beyond reading the org checkouts.
  • Clean architecture + Go gates: N/A — no Go source in this diff (gofmt/golangci-lint/go vet/go test do not apply). The cleanliness half does apply: the new control is one function, the retired name is swept, and no dead code is left behind.

Agent: charly

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

… one of them

Three filed findings in this repo, one theme, one gate: the org archived
`layer-charly-internals`, its skills moved into the UMBRELLA's own
`.agents/skills/…` trees, and this repo's governance surfaces still pointed
at the retired carrier.

#257 — the 19 stale pointers. `scripts/governance-claims.tsv` named 19 of its
81 rows at `layer-charly-internals/…` and `layer-charly-internals-extra/…`.
The org no longer carries those repos, so the checker SKIPPED every one of
them: a listed surface that cannot be reached was indistinguishable from one
that was never checked, and the rules they claim (the takeover window, the
sign-off account gate, the successor protocol, the read-in-full rule, the
RCA-on-validation-failure rule, the footer order, the re-run manual, the
auto-close threshold, fix-or-file, the coordination vocabulary) were
unasserted org-wide. Measured against the real umbrella checkout, the shipped
manifest read `checked=62 skipped=19 fail=0` — a green line that hid a fifth
of the table. The rows are repointed at the home that now holds the prose,
the umbrella's own `.agents/skills/…` trees: `checked=82 skipped=0 fail=0`.
The prose is unchanged; only the pointers move.

#250 — the unanchored surface. One path was named by a 'forbid' row only, and
a forbid row asserts that a phrase is ABSENT, which is vacuously true of ANY
file at that path — including a file that holds no rule at all. So the table
could not say "this path IS the surface", and the row could never go red. The
surface is now pinned by a 'require' row, AND the class is structural: the
checker now FAILS a manifest that names a path no 'require' row pins. The
verdict is about the TABLE, not about the checkout, so an unanchored path
fails in every root — including one that does not carry the surface at all.

#251 — the dead byte figure. `scripts/strip-config-version.sh` justified its
> 1 MB fallback with a figure that no longer exists: it cited
`layer-charly-internals/charly.yml` as 1,038,418 bytes, and that repo is
archived. The fallback is KEPT — it is a forward guard, and the branch is not
dead code: `scripts/strip-config-version_test.sh` exercises it with a > 1 MB
fixture on every run of this repo's gate — and the comment now states the
threshold, what triggers it, and a live measurement (the org's largest
`charly.yml` is `charly/charly.yml` at 316,230 bytes, measured 2026-10-27).

The prose that names the retired carrier is swept in the same phase (R5): the
manifest's header, the checker's three path examples, and the two comments in
`strip-config-version.sh`. `git grep layer-charly-internals` over the tree now
returns ONE survivor outside CHANGELOG/ — README.md's citation of the gh
`run rerun` measurement. That one is a PROVENANCE citation of a live
operational rule, not a pointer to a surface that must exist: the issue it
names is still readable, and the claim it supports is version-scoped
("measured on 2.100.0"), so it is not a dead pointer and it is not touched
here.

The coverage can fail, and it was measured both ways: the OLD checker accepts
an unanchored manifest (exit 0), the NEW one rejects it (exit 1); and the NEW
checker against the OLD manifest names the one unanchored path the shipped
table actually had.

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

Copy link
Copy Markdown
Contributor Author

STATUS — #258

Head: 64a77de — fix(gate): the claims manifest must name LIVE surfaces, and pin every one of them (3 files, +120/−39; worktree clean at push).

Done. The producer leg of #257 is open, carrying three filed findings of one theme in one cutover, per the batch law:

The coverage can fail, and it was measured both ways (pasted in the body): the OLD checker accepts an unanchored manifest (exit 0) where the NEW one rejects it (exit 1), and the NEW checker against the OLD manifest names the one unanchored path the shipped table actually had.

Gate. This repo's own workflow-checks job, run on the committed tree: every offline gate rc=0, governance-reconcile: checked=27 skipped=55 fail=0 for the CI form, zero fail lines and zero warn lines. The two uses: runtime steps need GitHub's own runners and run in this PR's CI.

Next. Read every new comment and the validator's full report; fix every finding in ONE commit per round. Then, on landing: the umbrella's dotgithub pin bump in its own PR (producer-first, charly task sync — never a hand-pin), and #250/#251/#257 closed BY HAND with a RESOLVED record (the auto-merge App never auto-closes).

Next update due: when the validator's verdict on this head lands, or in 24 h, whichever comes first.

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

@github-actions

Copy link
Copy Markdown
Contributor

Review — PASS

What I checked

Diff ↔ body consistency. The three-file diff (+120/−39) matches the body and the status comment exactly. The manifest diff removes 19 rows pointing at layer-charly-internals/… / layer-charly-internals-extra/… and adds 20 (the same 19 repointed at .agents/skills/…, plus one new require row pinning fix_or_file/cutover-policy skill source), so the row count goes 81 → 82. That reconciles the body's "19 of its 81 rows", "1 require row added", checked=62 skipped=19 (old) and checked=82 skipped=0 / checked=27 skipped=55 (new CI form: 27+55 = 82). The header-comment hunk (+7/−4) plus data rows (+20/−19) sums to the stated +27/−23 for the TSV. No contradiction found.

The anchor control (unanchored_surfaces + check_manifest). The awk is correct: it skips # lines, short rows, and the claim header; records the first claim/surface per path; marks a path anchored if any row for it is require; and prints the rest. The bash consumer skips an empty path field, so a malformed row cannot inject a phantom finding. Because the verdict is derived only from the table, it is root-independent — which is exactly what the body claims and what self-test out11 (a root that does not carry the surface) asserts. The control sets fail=1 before the row loop, so it forces a non-zero exit; the pasted Control A/B runs (old checker rc=0 / new rc=1) are consistent with the code.

Tests that fail without the change. out10/out11 are real: the old checker accepts the unanchored fixture, so the && { fail } branch would trip without the checker change. out12 pins the shipped manifest's shape. pass.tsv, fail-forbid.tsv, and skip.tsv gained an anchoring require row so the pre-existing forbid/skip tests are not themselves tripped by the new control, and the skip assertion was correctly bumped skipped=1 → skipped=2 for the second sibling row. The strip-config-version.sh edit is comment-only; the > 1 MB branch it describes is exercised by the repo's own test (block 2, Control C).

Security / scope. No secrets, no new dependency, no argv-injection surface; the only new write is inside the mktemp -d self-test fixture. The change is coherent (one theme, three files) and sweeps the retired carrier from every file it opens; the single tree-wide survivor (README.md:121) is a closed-issue provenance citation for a version-scoped rule, a defensible classification rather than a live pointer.

Comments. The one comment (@aitrawog, author) is a STATUS update, not a maintainer sign-off — no designated maintainers are configured, so I treat it as non-authoritative. Its claims (3 files, +120/−39, checked=27 skipped=55 fail=0, the old/new rc split) all match the current diff and body; nothing in it is stale.

Caveats (non-blocking)

  • The .agents/skills/… rows — in particular the new fix_or_file/cutover-policy require pattern fix it or file it (lowercase, unlike the capitalized strict-policy pattern) — resolve only against the umbrella checkout, which this repo's CI does not run. The body's pasted checked=82 skipped=0 fail=0 is the only evidence, and it is consistent with the diff; I simply cannot independently re-derive the external file contents from the supplied state.
  • Minor cross-reference slip: the body's "the shipped table, before this change, is one it names (block 2, control C)" points at the wrong control — the old-manifest run is Control B (Control C is the > 1 MB test). Cosmetic.
  • The T4 section says "20 repointed rows" where 19 were repointed and 1 was added; the row-count math itself is sound.

None of these rises to a correctness or honesty defect, and the change demonstrably strengthens the gate (adds a failure condition, skipped=19 → skipped=0) with coverage that fails without it.

Verdict: PASS


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

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) October 27, 2026 04:51
@github-actions
github-actions Bot merged commit b7e2a2c into main Oct 27, 2026
4 checks passed
charly-auto-merge Bot added a commit that referenced this pull request Oct 27, 2026
github-actions Bot pushed a commit to opencharly/opencharly that referenced this pull request Oct 27, 2026
…728)

The wave's scope is DERIVED, never hand-listed: it is the set of paths the
product's own currency report names as not current on the wave's base, and the
report's own count is asserted against the derived scope, so a class silently
dropped from the derivation fails the run rather than quietly narrowing the wave.

The scope is ONE path, a leaf whose intent is `upstream:main` (implicit: the
`branch = main` its .gitmodules entry declares): dotgithub (716c551dc3 ->
d256175fa6).

The producer is opencharly/.github#258, merged as b7e2a2c and tagged
v2026.300.0451. It repointed the governance claims manifest's 19 rows off the
org's ARCHIVED layer-charly-internals carrier (17) and layer-charly-internals-extra
(2) at the umbrella's own .agents/skills trees, added a require row for the
cutover-policy surface, and made the checker FAIL a manifest that names a path
no require row pins.

Advancing the pin is the consumer leg, and it is what the umbrella's own gate
reads: before the bump the strong form reported `checked=62 skipped=19 fail=0`,
and a skipped row is SILENT -- a claim the org no longer carried was
indistinguishable from a claim that was never checked. After it, the same strong
form over the same checkout reports `checked=82 skipped=0 fail=0`: every claim
is asserted, and none is skipped.

Every pin moved through the pin contract's own verb (charly task sync), and the
paths that verb moved that are NOT in the wave's scope were restored to their
base gitlinks -- this is one scope, not a drift-everything bump. Here the verb
advanced 398 pins in the index and exactly one path's value changed, and it is
the derived scope's path, so nothing was restored.

The order is sync -> init -> verify: the sync writes the pins, the init
materializes the checkouts AT those pins, and the verify judges them.

The umbrella's own charly pin does not move in this wave, so the worktree-local
binary is already the pinned source's build (2026.300.0236, from charly
5a83e03) and this wave owes no rebuild of it.

Refs #674

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