Skip to content

fix(review-code): the glossary gate says so when it cannot see (#4299)#4331

Merged
usirin merged 2 commits into
mainfrom
umut-sirin/glossary-gate-detector-blind-8ADD3AC7
Jul 27, 2026
Merged

fix(review-code): the glossary gate says so when it cannot see (#4299)#4331
usirin merged 2 commits into
mainfrom
umut-sirin/glossary-gate-detector-blind-8ADD3AC7

Conversation

@usirin

@usirin usirin commented Jul 26, 2026

Copy link
Copy Markdown
Member

The reviewer's glossary-freshness check used to say the same thing whether a pull request genuinely added no new surface or the checker simply had no idea what a surface looks like in this repository. Those are very different facts, and only one of them is good news. This change makes the checker prove it can see something before it reports "nothing to check" — and when it cannot see anything at all, it now says so out loud and blocks, instead of waving the pull request through.

Fixes #4299

What changed

One file, one step: claude-plugins/kampus-pipeline/skills/review-code/SKILL.md, Step 3c.

  • New positive-evidence probe. After the three surface detectors run, the step counts how many surfaces the detectors' own patterns can express on the freshly-fetched base (DETECTOR_SURFACES_N): directory entries under apps/web/worker/features/, plus packages/<pkg>/package.json files. Read-only against origin/$BASE_REF, same posture as every other probe in the step (formats §RO).
  • The empty-scan branch splits in two.
    • DETECTOR_SURFACES_N > 0 — the detector demonstrably works here, so an empty NEW_SURFACE is a fact about the pull request: the existing not-applicable skip, now also naming the candidate count.
    • DETECTOR_SURFACES_N == 0 (with .glossary/TERMS.md present on base) — the emptiness is a fact about the detector. The step emits a distinct detector blind line and folds an [UNVERIFIABLE] row into the conjunctive table. One UNVERIFIABLE fails the gate, so the vacuous case now stops the line rather than reading green.
  • The graceful-absence probe leads the chain as an executed guard. git cat-file -e "origin/$BASE_REF:.glossary/TERMS.md" is the verdict chain's first if, so a repo that never adopted the glossary takes its honest skip and the new detector blind branch is structurally unreachable for it — the ordering is enforced by the block, not asserted by the prose around it. The defect surface is exactly the intersection triage identified: glossary present, layout absent.

Which fork: the fail-closed one. ADR 0092's bias — "default FAIL, pass on positive evidence of scope" — makes that the floor. Configurable surface detection stays unbuilt; the new line names it as one of the three remedies a blind repo can take.

Replay — the required confirmation (AC 1)

Triage required replaying the Step 3c detection block against a real pull request's file list with the apps/web/worker/features/ and packages/ prefixes altered, before building. Run against PR #2258 (which added packages/design-capture/), with the base probes resolved at that pull request's own base.sha — the tree the reviewer would have seen at the time:

Run NEW_SURFACE Emitted verdict line
Real prefixes (apps/web/worker/features/, packages/) packages/design-capture glossary-freshness: scanned new surfaces => packages/design-captureFAIL
Altered prefixes (src/modules/, libs/) (empty) glossary-freshness: not applicable — no new feature folder / public package / export in this PR

Same pull request, same file list, same diff. Only the layout prefixes changed, and the gate went from a correct FAIL to a clean skip. Prediction confirmed, not falsified — the vacuous pass is real, and its emitted line is byte-identical to a legitimate skip.

The new probe was then exercised on both sides:

  • phoenix layout on origin/mainDETECTOR_SURFACES_N=49 → not-applicable skip branch (AC 4: no new false FAIL here).
  • simulated foreign layout (src/modules, libs) → DETECTOR_SURFACES_N=0UNVERIFIABLE detector-blind branch.

Repair round 1 — the ordering is now enforced, not asserted

The review-skill FAIL was right: the graceful-absence probe existed only as a parenthetical inside the prose blockquote that sits after the verdict block, so the else was reachable with the glossary absent and emitted a blocking UNVERIFIABLE asserting .glossary/TERMS.md is on origin/$BASE_REF — a fact it never tested. That was inert on base (the else was a benign skip) and load-bearing here (the paths now diverge).

The chain now leads with the executed probe. Replayed both directions against real trees:

Base tree DETECTOR_SURFACES_N Emitted line
origin/main (glossary present) 0 (simulated) UNVERIFIABLE — detector blind — the new branch is still reachable when the glossary is on base
026dbdf6^ (real pre-glossary tree) 0 not applicable — no .glossary/TERMS.md on base — the graceful-absence skip, detector blind unreachable

Nothing else moved: the DETECTOR_SURFACES_N probe and its numbers, the [UNVERIFIABLE] table row, the count > 0 skip, and the no-fixture decision are all untouched. The diff of this round is 10 insertions / 4 deletions in one file, both hunks inside Step 3c.

Verification

  • pipeline-cli gh-phoenix lint-skills over the full corpus (the CI invocation shape): clean. Single-file invocation exits 3 on zero gh-call scope both before and after this change, so the corpus-wide run is the meaningful one.
  • pnpm lint:worktree: no biome-handled changed files — clean skip on a markdown-only diff.
  • cp-classify classify: control-plane, exit 0 (path-match on claude-plugins/**). This is a §CP pull request: blocking, human merge.

Deviations

Class: narrowed a suggested scope.

  • Said: triage's AC 1 prose adds that "the same fixture is the regression test the fix ships with."
  • Did: recorded the replay in this description, as the checkbox itself requires, but shipped no committed test fixture.
  • Why: the scope fence limits the change to Step 3c of that one file, and there is no harness in this repo that executes a skill's bash block, so a fixture would have been a new untested surface in a directory the fence excludes.
  • Disposition: for the reviewer to judge; say the word and I will file a follow-up for an executable-block test harness rather than widen this pull request.

Class: chose between two forks the issue left open.

  • Said: triage left the fail-closed fork and the configurable-detection fork both in scope for the implementer.
  • Did: built only the fail-closed fork.
  • Why: triage names it the floor under ADR 0092, and configurable detection is a design surface (a declaration format, its own graceful absence, its own vacuity) that would not fit the scope fence.
  • Disposition: no action needed; the emitted line names configurable detection as an available remedy without presuming it.

Class: did not run the full acceptance test.

  • Said: the report's strongest confirmation is a live review-code run in a repository without the phoenix layout.
  • Did: simulated the foreign layout by altering the detector's prefixes against real base trees and a real pull request file list.
  • Why: triage explicitly marked the live foreign-repo run not required and priced it as the expensive part of No issue expresses the Pipeline Anywhere acceptance test: foreign repo, second operator, end to end #4268.
  • Disposition: no action needed; called out so nobody reads the table above as a foreign-repo run.

Step 3c's empty-scan branch had two causes wearing one line: "this PR added
no new surface" and "this repo has no surface of the kind I know how to look
for". Both emitted the not-applicable skip, so a repo that adopted
.glossary/TERMS.md while keeping its own layout got a green gate on every PR
forever.

Add a positive-evidence probe (ADR 0092's "pass on positive evidence of
scope"): count the surfaces the detector's own patterns can express on fresh
base. With at least one, an empty scan is a fact about the PR and keeps the
existing skip. With zero, it is a fact about the detector, and the gate now
emits a distinct detector-blind line and folds an [UNVERIFIABLE] row into the
conjunctive table — a blocking soft fail, not an omitted row.

The other three outcomes are untouched: glossary absent on base still takes
the graceful-absence skip (evaluated first, so it short-circuits the new
branch), new surface + glossary touched still PASSes, new surface + glossary
untouched still FAILs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@usirin
usirin requested a review from a team as a code owner July 26, 2026 22:25
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

No preview deploy

  • No preview deploy for this PR — its diff touches no deploy-relevant path, so no preview stack was minted and e2e is not applicable. (632fe91)
  • web — Stage pr-4331 torn down.

@usirin

usirin commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

claim: fcd74bd3-8872-4023-b100-a81ac870eb43 · 2026-07-26T22:27:24.627Z · presence 09f7c847e681/23523

@usirin

usirin commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

review-skill: FAIL @ f841260 — changes-requested

Reviewed-head: @ f841260

§CP blocking-set PR — the diff touches claude-plugins/kampus-pipeline/skills/review-code/ (gate-critical skill). Live cp-classify classify, re-resolved from origin/main: control-plane [path-match], state word control-plane. This verdict authorizes nothing either way; it is the repair seam for write-code.

Verified PR #4331 against #4299's acceptance criteria + the skill-rigor checklist. The substance of this fix is sound and independently corroborated — one defect blocks it, and its remedy is a single restructure inside the existing scope fence.

Acceptance criteria

  • [PASS] AC 1 — replay recorded in the PR description. Recorded, and independently re-run rather than accepted. Against PR feat(design-capture): Playwright-capture + gh user-attachments upload helper for review-design (#2247) #2258's own base.sha 1192ae98: its 15 added files all sit under packages/design-capture/; packages/design-capture/package.json is absent on that base (existence probe → non-zero), so with real prefixes NEW_SURFACE = packages/design-capture, and .glossary/TERMS.md is not in that PR's 16-file list ⇒ the FAIL branch. With src/modules/ + libs/, both sed patterns match none of the 15 paths ⇒ NEW_SURFACE empty ⇒ the base else emits the byte-identical not-applicable line. The DETECTOR_SURFACES_N figures reproduce exactly too: origin/main gives 25 feature entries + 24 packages/*/package.json = 49, the coder's reported number; the altered prefixes give 0 and 0 ⇒ N=0. Replay confirmed on both sides — the fix does not rest on an unverified claim.
  • [PASS] AC 2 — glossary present + no expressible surface ⇒ a distinct self-asserting outcome. SKILL.md:1128–1131 adds the else branch emitting the detector blind line, and SKILL.md:1108–1115 / 1140 fold the [UNVERIFIABLE] row. The line names the detector's inability explicitly and is not confusable with the skip.
  • [FAIL] AC 3 — "the three legitimate outcomes are unchanged: glossary absent on base ⇒ the existing graceful-absence skip." Unchanged in prose; regressed in the executable block. Detail below.
  • [PASS] AC 4 — no new false FAIL on the phoenix layout. DETECTOR_SURFACES_N = 49 on origin/main (verified above) ⇒ the elif [ "$DETECTOR_SURFACES_N" -gt 0 ] branch at SKILL.md:1126 ⇒ the not-applicable skip. Phoenix PRs that add no surface still skip.
  • [PASS] AC 5 — a verdict reader can distinguish the two cases without reading the skill. The two emitted lines are lexically distinct; the skip now carries the candidate count, the blind case a full explanation (SKILL.md:1127 vs 1129) plus a table row (1140).
  • [PASS] AC 6 — the change stays inside Step 3c; no other gate modified. One file, four hunks, all inside Step 3c's span (the step runs 1015–1166; hunks land at 1068–1166). No sibling gate touched.

The AC 3 defect — the ordering is asserted, never enforced

The PR body states the graceful-absence probe "runs first and short-circuits the whole step." I verified that claim in the actual text, as asked. What is true: SKILL.md:1161–1164 adds a sentence asserting the ordering, and SKILL.md:1108–1109 conditions the UNVERIFIABLE bullet on the glossary being present. What is not true: nothing enforces it.

  • The graceful-absence probe appears only inside the prose blockquote at SKILL.md:1155, as a parenthetical description. It is in no bash block anywhere in Step 3c — grepping every .glossary/TERMS.md occurrence in the head file confirms the sole executable use is GLOSSARY_TOUCHED at 1070, which tests the PR's file list, not the base tree.
  • The blockquote sits at 1153–1166 — physically after the verdict block at 1117–1131. The only thing making it "first" is the new sentence saying so.
  • Consequently the else at SKILL.md:1128–1129 is reachable with the glossary absent, and it emits a line asserting a fact it never tested: ".glossary/TERMS.md is on origin/$BASE_REF".

Why this is newly load-bearing rather than pre-existing noise. The same textual ordering existed on base — but there the else branch was benign: not applicable …, no row, no failure, i.e. semantically identical to the graceful-absence skip. Skipping the probe cost exactly nothing, which is why it never mattered. This PR makes the two paths diverge: graceful-absence ⇒ skip / no row; the elseUNVERIFIABLE ⇒ conjunctive gate FAIL (Step 3, SKILL.md:876 — "One FAIL or UNVERIFIABLE → the PR fails the gate", which I confirmed, so the new row is genuinely blocking and not theatre). A previously-inert ambiguity now separates a benign outcome from a blocking one.

The failure mode lands on exactly the population this campaign targets: a fresh adopter with no glossary at all — the most common foreign-repo state, and the one triage explicitly carved out as correct-by-design — now risks a hard gate FAIL accompanied by a false explanation. That is worse than the vacuous pass being fixed: a false blocking claim beats a silent green for damage. And it reintroduces this issue's own sin one branch over — an emitted self-assertion stating something the gate never established.

Remedy — one restructure, entirely inside the existing scope fence. Make the ordering structural instead of asserted by leading the same if-chain at SKILL.md:1117–1131 with the probe the blockquote already describes:

if ! git cat-file -e "origin/$BASE_REF:.glossary/TERMS.md" 2>/dev/null; then
  echo "glossary-freshness: not applicable — no .glossary/TERMS.md on base"   # graceful absence, §ZS #3 skip
elif [ -n "$(printf '%s' "$NEW_SURFACE" | tr -d ' ')" ]; then
  ...

Nothing else in the PR needs to move; the blockquote's assertion then describes a property the block actually has. (Hoisting the blockquote above the block would also work, but the guard-in-the-chain is the version that cannot drift.)

Skill rigor

  • [FAIL] Behavioral correctness. Traced as an executing agent would run it: the block at 1034–1082 computes no glossary-presence variable, so the guard cannot fire from the block; an agent running the verdict block in a glossary-less repo reaches 1129 and emits a factually false, blocking line. Same site and remedy as AC 3.
  • [PASS] Trigger / description quality. Frontmatter untouched; the diff is confined to Step 3c's body. No trigger-surface change, so no over- or under-trigger admitted.
  • [PASS] Cross-skill conflict / shadowing. The emitted skip line's text changed (a (detector expressible here: N …) suffix), so I checked for consumers: no skill and no packages/** tool parses glossary-freshness output — the only two occurrences of that line are the two in this file. plan-epic/SKILL.md:96 and gh-issue-intake-formats.md:2066 reference Step 3c only descriptively ("catches structural surfaces", "owns the glossary-freshness contract"); both remain accurate. No broken seam.
  • [PASS] Gate-invariant preservation. Nothing removed or softened. The §ZS / ADR 0092 posture is strengthened: an empty scan that was an unconditional skip now requires positive evidence of scope to earn that skip, and the UNVERIFIABLE row is confirmed blocking against Step 3's conjunctive rule (SKILL.md:876). Config-pin, marker namespaces, SHA-binding, matcher, and "never merge" are untouched, and the three legitimate outcomes' prose definitions are intact. (The AC 3 defect is a correctness gap in the new branch, not a weakened guardrail — recorded there rather than double-counted here.)
  • [PASS] deviation-disclosure (§DEV). The body carries a real ## Deviations section with three disclosed classes — the missing fixture, the fork choice, and the not-run live foreign-repo test — each with said / did / why / disposition. Nothing this gate could see went undisclosed.

On the disclosed fixture gap (judged, not deferred)

Judging the AC and the honesty of the disclosure as separate questions, as asked: the AC is met, and the reasoning holds on its own. AC 1's checkbox requires only that the replay's NEW_SURFACE and emitted verdict line "are recorded in the PR description" — done, and corroborated above. "The same fixture is the regression test the fix ships with" is triage's surrounding prose, not the checkbox, and it collides with triage's own scope fence in the same issue: "In scope: Step 3c of review-code/SKILL.md … and nothing else in that file." A committed fixture is necessarily a new file outside that fence, and nothing in this repo executes a skill's bash block, so it would ship as untested surface. Not blocking. If an executable-block harness is wanted, that is a separate issue, not a widening of this one.

Sanity-checks on the two coder observations

  • lint-skills exit 3 on a single file. Consistent with the reported scanned 0 file(s) — a zero-scope fail-closed on a single-file invocation, reproduced by the coder against an unmodified skill. The corpus-wide run is the meaningful invocation shape and is reported clean. Nothing in this verdict rests on it either way.
  • The interrupted push. Resolved: the current head is exactly what the PR carries. The REST head SHA and the remote refs/pull/4331/head both return f841260652851d3b6ed08a2ec51978c509c7db16, and the per-run ref I reviewed from asserted equal to that SHA before any file was read. No duplicate or partial push.

Read the PR head (§HEAD): all skill text under review was sourced from f841260652851d3b6ed08a2ec51978c509c7db16 via a per-run ref, never the launched checkout's working copy. No working tree was switched (§RO).

One failing item above must be addressed before this PR can merge. The PR stays open and unmerged; #4299 stays open and assigned. review-skill does not merge. Re-request review once the if-chain leads with the graceful-absence probe.

Verdict-written: 2026-07-26T22:37:16Z

…not prose (#4299)

Step 3c's verdict chain assumed, but never tested, that a repo without
.glossary/TERMS.md on base short-circuits before the new detector-blind
branch. The probe existed only as a parenthetical inside a prose blockquote
that sits physically after the chain, so the final `else` was reachable with
the glossary absent and emitted a blocking UNVERIFIABLE asserting
".glossary/TERMS.md is on origin/$BASE_REF" — a fact it never checked.

On base that ambiguity was inert (the else was a benign not-applicable skip);
this branch made the two paths diverge, so a glossary-less fresh adopter — the
most common foreign-repo state — risked a hard FAIL carrying a false
explanation.

Lead the chain with the executed `git cat-file -e
"origin/$BASE_REF:.glossary/TERMS.md"` probe, making the detector-blind branch
structurally unreachable when the glossary is absent, and reword the
blockquote to describe the guard instead of asserting an ordering.

Replayed both directions: with the glossary on base and DETECTOR_SURFACES_N=0
the chain still reaches UNVERIFIABLE; against a real pre-glossary base tree
(026dbdf^) it emits the graceful-absence skip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@usirin

usirin commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

review-skill: advisory — blocking-set PR (manual merge)

PR #4331 touches the control plane (claude-plugins/kampus-pipeline/skills/review-code/ — a gate-critical skill, §CP). Live cp-classify classify --repo kamp-us/phoenix, re-resolved from origin/main: state word control-plane [path-match], exit 0. My verdict is advisory only — it authorizes no merge. Under ADR 0135 a @kamp-us/control-plane member must approve at this head and ship-it then enqueues (ADR 0048 single merge authority). There is no such approval at head right nowpulls/4331/reviews is empty. Head CI: ci-required success, CodeQL success, all guard jobs green.

Reviewed-head: @ 632fe91

Round-2 re-review of the repair (f841260632fe919, fast-forward, origin/main already an ancestor). Round 1's single blocking item is cleared; nothing regressed. Verified against #4299's acceptance criteria + the skill-rigor checklist — all checks pass.

The blocking item — cleared, verified structurally AND by execution

Round 1 failed this PR because the graceful-absence probe was asserted in a prose blockquote at line 1155, physically after the verdict block, and never executed — so the else was reachable with the glossary absent and emitted UNVERIFIABLE — detector blind: .glossary/TERMS.md is on origin/$BASE_REF…, asserting a fact it had not tested.

Structural check. At head the chain leads with the executed probe (SKILL.md:1117–1136):

1121  if ! git cat-file -e "origin/$BASE_REF:.glossary/TERMS.md" 2>/dev/null; then
1122    echo "glossary-freshness: not applicable — no .glossary/TERMS.md on base"
1123  elif [ -n "$(printf '%s' "$NEW_SURFACE" | tr -d ' ')" ]; then
...
1131  elif [ "$DETECTOR_SURFACES_N" -gt 0 ]; then
1133  else   # UNVERIFIABLE — detector blind

The detector blind branch is the terminal else of a single if/elif chain whose first arm is the glossary-absence test. Glossary absent ⇒ arm 1 taken ⇒ the chain terminates. The branch is therefore structurally unreachable when .glossary/TERMS.md is absent on base. No &&/|| shortcut, no nesting, no fallthrough — one flat chain.

Execution check. I extracted SKILL.md:1118–1136 verbatim from the head worktree and ran it, rather than reasoning about it. Five outcomes, all five reached:

# BASE_REF NEW_SURFACE GLOSSARY_TOUCHED DETECTOR_SURFACES_N Emitted
A main (glossary present) (empty) 0 UNVERIFIABLE — detector blind: … is on origin/main …
B main (empty) 49 not applicable — no new feature folder / public package / export in this PR (detector expressible here: 49 candidate surface(s) on base)
C 026dbdf6^, the real pre-glossary tree (empty) 0 not applicable — no .glossary/TERMS.md on base
D main packages/foo set 49 scanned new surfaces ⇒ packages/foo + PASS
E main packages/foo (empty) 49 scanned new surfaces ⇒ packages/foo + FAIL

Run A is the corroboration you asked for, and it is the one that matters most: the fix did not disarm the branch it was added to enforce. Glossary on base, detector blind ⇒ the UNVERIFIABLE line still fires, verbatim. Run C is the other direction against a real tree, not a simulated ref: 026dbdf6 is docs(glossary): add .glossary/TERMS.md domain vocabulary seed (#898); I confirmed TERMS.md is present at 026dbdf6 and absent at 026dbdf6^, materialized that parent as a throwaway per-run ref, ran the chain against it, and got the honest graceful-absence skip. The ref was deleted after the run; no worktree was switched and none was touched.

Runs A and C together are the whole point of the repair: the two states now have separate lines and neither can wear the other's clothes.

Round-2 delta — scope confirmed

git diff --stat f841260 632fe9191 file changed, 10 insertions(+), 4 deletions(-), exactly as claimed. Two hunks, both inside Step 3c (span 1015–1173 at head; Step 3d begins 1174):

  • Hunk 1 (~1117) — 3 comment lines + the if ! probe + its echo prepended; the former first if became elif on the same condition, byte-for-byte otherwise. I diffed the chain arm by arm: 1123–1130 (scanned/PASS/FAIL), 1131–1132 (count > 0 skip) and 1133–1136 (UNVERIFIABLE) are unchanged — the round-2 diff shows them as context, not as +/-.
  • Hunk 2 (~1166) — the blockquote's assertion rewritten from "This probe runs FIRST and short-circuits the step" to "This probe is the executed guard that LEADS the verdict chain above — it is the chain's first if". That is now a description of the block rather than a claim about an ordering the block did not have. Correct fix, correct place.

Nothing outside Step 3c moved in either round: all five hunks of the full PR diff land at 10681170.

Acceptance criteria

  • [PASS] AC1 — detection block replayed against a real PR file list with altered prefixes, NEW_SURFACE + emitted line recorded in the description — PR body ## Replay table (PR feat(design-capture): Playwright-capture + gh user-attachments upload helper for review-design (#2247) #2258 at its own base.sha; real prefixes ⇒ packages/design-capture ⇒ FAIL, altered prefixes ⇒ empty ⇒ the not-applicable line). The prediction was confirmed, not falsified, so the "stop and re-report" clause did not fire.
  • [PASS] AC2 — glossary present on base + no expressible surface no longer emits the plain skip — SKILL.md:1133–1136 emits the distinct UNVERIFIABLE — detector blind line naming the detector's inability; reproduced live (run A).
  • [PASS] AC3 — the three legitimate outcomes unchanged — glossary absent ⇒ graceful-absence skip (run C, SKILL.md:1121–1122); new surface + touched ⇒ PASS (run D, 1125–1126); new surface + untouched ⇒ FAIL (run E, 1128).
  • [PASS] AC4 — no new false FAIL on the phoenix layout — run B takes the not-applicable skip. I recounted DETECTOR_SURFACES_N independently off origin/main: 25 dirs under apps/web/worker/features + 24 packages/*/package.json = 49, matching the body.
  • [PASS] AC5 — a verdict reader can tell the two apart without opening the skill — the [UNVERIFIABLE] example row at SKILL.md:1145 spells out "this repo contains 0 surfaces the detector's patterns can express, so the empty scan says nothing about this PR", and 1148–1156 states the distinction in the reviewer's own artifact terms.
  • [PASS] AC6 — change stays inside Step 3c, no other gate modified — one file, all hunks within 1015–1173.

Skill rigor

  • [PASS] Behavioral correctness — the chain is one flat if/elif/elif/else; all five outcomes were reached by executing the head's own block, not inferred. Fail-closed on a degenerate count too: an unset/empty DETECTOR_SURFACES_N makes [ "" -gt 0 ] error non-zero and fall through to the UNVERIFIABLE else, which is the safe direction under ADR 0092.
  • [PASS] Trigger / description quality — frontmatter untouched by the diff; review-code's trigger surface is unchanged.
  • [PASS] Cross-skill conflict / shadowing — the emitted line has no machine consumer. The only cross-references to this gate are descriptive and still accurate: plan-epic/SKILL.md:96 ("only catches structural surfaces") and gh-issue-intake-formats.md:2066 ("review-code Step 3c owns the glossary-freshness contract"). No marker namespace, §CP set, or matcher is touched.
  • [PASS] Gate-invariant preservation — §ZS / ADR 0092 is strengthened: an empty scan that used to read as a sanctioned skip now folds a blocking [UNVERIFIABLE] row into the conjunctive table. One observation, deliberately not a finding: the new lead arm also short-circuits the new-surface FAIL branch when the glossary is absent on base — a real behavior change beyond pure ordering. It is not a weakening. The pre-existing blockquote already declared that case a not-applicable skip ("there is no vocabulary file to require a touch of"), AC3 requires exactly that, and requiring an adopter's PR to "touch" a file its base does not have would be incoherent. phoenix keeps its glossary, so no in-repo enforcement is lost. This aligns the bash to a contract the prose already stated.
  • [PASS] deviation-disclosure (§DEV) — the body carries ## Deviations with three disclosed entries, and this round correctly added none. I checked the round-2 delta against all seven classes and found no new departure: the hunks implement reviewer guidance and AC3 rather than departing from anything. The three standing entries are all still accurate at this head (no committed fixture; fail-closed fork only; simulated rather than live foreign-repo run). The corrected "Ordering made explicit" bullet is now a true statement about the block, and the new ## Repair round 1 section discloses the change beyond what §DEV requires.

Specialist fan-out (ADR 0079) — no findings

unreachable-step: none — every branch was reached by execution. contradictory-instruction: none — the bullet list (1100–1115), the table rows (1143–1145), the omit-the-row prose (1148–1156) and the blockquote (1158–1172) all now describe the same five outcomes, and the bullet's remedy "drop .glossary/TERMS.md (which routes to the honest graceful-absence skip below)" is true of the block for the first time. uncovered-procedure-path: none — the glossary-absent-plus-new-surface path is specified (skip, no row) by the blockquote. Nothing to append to #4299, nothing to route to report.

Verdict

All 6 acceptance criteria and all 5 rigor checks pass. Advisory only — this authorizes no merge. §CP requires a @kamp-us/control-plane approval at 632fe9193d28acfb640c5f80a9bbcad5bb889130 before ship-it will enqueue, and there is none yet. Do not rebase or amend — that would move the head and un-bind this verdict.

Read the PR head (§HEAD): all skill text under review sourced from 632fe9193d28acfb640c5f80a9bbcad5bb889130 via an isolated throwaway worktree with the instruction denylist removed and asserted absent; no working tree was switched (§RO).

Verdict-written: 2026-07-26T23:20:05Z

@usirin

usirin commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

Handoff — banked, awaiting a control-plane approval

Engine instance 98177c03 (session fcd74bd3-…) is winding down. This lane is complete on the engine side; nothing is in flight against it.

State: PASS at head 632fe9193d28acfb640c5f80a9bbcad5bb889130 — all 6 ACs and all 5 rigor checks. §CP control-plane (exit 0, path-match on claude-plugins/**). Assigned to the control-plane approver with a team review requested.

Next step, and the only one: a @kamp-us/control-plane approval at head 632fe919, then the approval-aware shipper enqueues. No code change is owed.

Do not rebase or push to this branch. Any head movement un-binds the verdict (ADR 0058) and would cost a re-review. Three human approvals were destroyed that way today.

What it fixes, for whoever picks it up: the glossary-freshness gate computed NEW_SURFACE from hard-coded layout prefixes, so on a repo with a different layout it scanned nothing and emitted a line byte-identical to a legitimate skip — a vacuous pass indistinguishable from a real one. The fix makes the unevaluable case fail loudly (detector blind + an [UNVERIFIABLE] row that stops the gate) rather than narrowing it, and the graceful-absence path is now established by an executed git cat-file -e probe rather than by prose. The reviewer verified both directions by execution, including against a real pre-glossary base tree (026dbdf6^).

Marker note: this PR carries verdict markers at more than one head. That is the documented contract — verdict post keys on (gate, run, head), and head-first resolution drops non-head markers. Do not hand-edit or delete any of them; that is the one action that could actually break head-first resolution.

@usirin
usirin added this pull request to the merge queue Jul 27, 2026
Merged via the queue into main with commit 1046e94 Jul 27, 2026
45 checks passed
@usirin
usirin deleted the umut-sirin/glossary-gate-detector-blind-8ADD3AC7 branch July 27, 2026 02:34
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.

review-code's glossary-freshness gate passes vacuously wherever the phoenix layout is absent

2 participants