Skip to content

feat(meta-orchestrator-stage-4): CLI override flags --mode-* (6 flags) + parse helper + paired-negative - #244

Merged
artyhoo merged 1 commit into
stagingfrom
feat/meta-orchestrator-stage-4-cli-overrides
May 26, 2026
Merged

feat(meta-orchestrator-stage-4): CLI override flags --mode-* (6 flags) + parse helper + paired-negative#244
artyhoo merged 1 commit into
stagingfrom
feat/meta-orchestrator-stage-4-cli-overrides

Conversation

@artyhoo

@artyhoo artyhoo commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

Stage 4 of meta-orchestrator-mode-triage-and-planner umbrella (optional per design §13). Wires the 6 CLI override flags that SKILL.md §2.5 Step 5 predicates already reference (lines 192-209) but never parse. Pure-bash parser; OVERRIDE_MODE+REASON emission; routing-tree short-circuit specified. Substrate-only ship — SKILL.md wiring deferred (see Fallback below).

Closes design §13 Stage 4 — partial: A/B/C ship (infrastructure); D.1-D.3 (SKILL.md wiring) deferred to a follow-up post-cap-bump PR.

What ships

  1. .claude/skills/meta-orchestrator/references/mode-overrides.md (171 LOC) — Class C reference doc mirroring references/plan-cache.md:1-10 blockquote header pattern. Sections: §1 flag set, §2 reason requirement (≥20 chars; MO_OVERRIDE_REASON_MIN seam), §3 emission contract (OVERRIDE=user-flag, reason=<reason>), §4 anti-patterns (#override-as-default, #bypass-with-empty-reason, #multi-flag-collision, #override-used-when-classify-would-agree), §5 anti-pattern guard mechanism — DOCUMENTATION-ONLY / DEFERRED (override_log[] schema field absent from _master-backlog-delta.json today; spec preserved for Stage 5 wiring), §6 §1.7 self-reflexive, §7 see also.
  2. .claude/skills/meta-orchestrator/helpers/parse-override-flags.sh (128 LOC) — deterministic pure-bash parser. Single-string $1 input (the umbrella string from CC slash-command); tokenises internally with bash 3.2-compatible glob matching (no declare -A). 6-flag mutually-exclusive validation; --reason requirement (default ≥20 chars). Carries @dual-pair: meta-orchestrator-mode-overrides (line 37) + @cc-only-rationale (line 38) + MO_OVERRIDE_REASON_MIN seam (line 42).
  3. packages/core/hooks/parse-override-flags.test.ts (137 LOC, capability commit) — 9 paired-negative cases / 14 it.each tests including the label-mutation falsifier ALL-SIX-FLAGS-ROUND-TRIP (swap any mapping → per-flag assertion fails). spawnSync invocation shape mirrors delta-diff.test.ts:68 + update-delta.test.ts:32.

Binding spec compliance

  • design §10 — override-flags spec (6 flags, reason ≥20 chars, emission contract, #override-as-default anti-pattern). Honoured.
  • R-phase §3.5 Area C verdict — BUILD flags + REFERENCE aif-handoff AGENT_USE_SUBAGENTS pattern; SSOT row fix(hooks): force visible "🟢 Простыми словами" block in end-of-turn reminder #81. Honoured.
  • DN-3 (SKILL.md body single-source) — classify-work.sh UNCHANGED (verified via diff on helper). Honoured.
  • references/plan-cache.md REUSE-template — blockquote header + ## §N sections + companion paired-negative pattern mirrored.
  • helpers/update-delta.sh:1-40 REUSE-template — header block + seams convention (MO_OVERRIDE_REASON_MIN parallel to MO_DELTA_FILE / MO_GIT_HEAD / MO_TIMESTAMP).

§1.7 Forward-check applied

  • build-first-reuse-default.md:50 — REUSE plan-cache.md:3-4 / update-delta.sh:1-40 header + seams pattern for new helper + ref doc; BUILD parse-override-flags.sh per R-phase Area C verdict (no upstream bash CLI override parser with 6-flag mutually-exclusive semantics — confirmed via R-phase §3.2 DeepWiki sweep of Superpowers / AIF / aif-handoff / oh-my-openagent).
  • no-paid-llm-in-ci.md:23 — deterministic bash + TypeScript test only; zero API-billed calls; no claude -p in CI.
  • doc-authority-hierarchy.md:71references/mode-overrides.md:3-6 carries blockquote > **Authoritative for:** header mirroring plan-cache.md:3-4 format (NOT ## Authoritative for:); Class C declared at line 3.
  • dual-implementation-discipline.md:73helpers/parse-override-flags.sh:37 carries @dual-pair: meta-orchestrator-mode-overrides + @cc-only-rationale:38; ref doc line 27 carries matching HTML-comment anchor.
  • ai-laziness-traps.md:115 — T3 / T7 / T11 / T13 / T15 / T16 / T19 / T20 enumerated; T-MO-design-A «alias inline emit attractor» (from design §0) honoured (parse-override-flags.sh emits OVERRIDE_MODE= only, not ALIAS — ALIAS lives in SKILL.md §2.5 Step 6); T-Stage4-A «umbrella-vs-ARGUMENTS uncertainty» surfaced — calling convention documented as quoted single-arg invocation /meta-orchestrator "umbrella --mode-X --reason=..." pending empirical CC probe (recorded in REPORT DECISIONS LOG feat: audit fixes Phase 1+2+3 (R2/R11 drift, manifest SSOT, depcruise integration) #1).
  • reviewer-discipline.md:24 — Phase -1 cold-review (1× Opus reviewer Agent) caught 2 BLOCKER + 4 MAJOR + 5 MINOR before dispatch; all resolved or explicitly deferred; re-review verdict GO. Cap-blocker MAJOR-3 correctly handled via fallback path (A/B/C only, D.1-D.3 deferred).
  • parallel-subwave-isolation.md:25 — Mode A inline Worker dispatched with isolation: "worktree"; verified branch feat/meta-orchestrator-stage-4-cli-overrides from origin/staging HEAD c4b63ac (Stage 3 merged PR feat(meta-orchestrator): Stage 3 — delta-diff helper + reconciliation rule + paired-negative #243). Known incident: Worker's commit transiently landed on local staging ref in main workdir during dispatch (recovered via direct SHA push ea82de8:refs/heads/feat/... fast-forward to remote feat branch — no force-push, no destructive op); remote feat/ correctly carries ea82de8 per git ls-remote verification.
  • phase-research-coverage.md:50 — this section is the §1.7 forward-check.
  • phase-research-coverage.md:107 §1.11 — base verified empirically: gh pr view 243 --json state,mergedAt confirmed merge before branching; git ls-remote origin refs/heads/staging confirmed c4b63ac as base.
  • phase-research-coverage.md:118 §1.12 — cap-blocker path committed (A/B/C ship; D.1-D.3 deferred); not option-dumped to maintainer.
  • recommendation-laziness-discipline.md:33 — every Stage 4 design recommendation backed by tool-call evidence (file reads, R-phase §3.5 / design §10 file:line cites, gh pr view, git ls-remote); no inline verdicts without tools.

§1.7 Backward-check applied

  • .claude/skills/meta-orchestrator/references/mode-overrides.md:1NEW 171 LOC Class C reference doc with blockquote header at lines 3-6; §1-§7 sections; @dual-pair anchor line 27.
  • .claude/skills/meta-orchestrator/helpers/parse-override-flags.sh:1NEW 128 LOC pure-bash helper; bash 3.2 compatible (no declare -A — uses KNOWN_FLAGS list + glob + case statement); @dual-pair line 37 + @cc-only-rationale line 38 + MO_OVERRIDE_REASON_MIN seam line 42; executable -rwxr-xr-x.
  • packages/core/hooks/parse-override-flags.test.ts:1NEW 137 LOC paired-negative; 9 cases / 14 tests via it.each; capability commit (≥80 LOC under packages/) with two Prior-art: trailers in commit body.
  • .claude/skills/meta-orchestrator/SKILL.md:1UNCHANGED (deferred pending cap-bump follow-up; D.1-D.3 edits would push the file from 600 → ~607-612 LOC, blocked by .husky/pre-commit:63 600-line cap which is classifier-protected).
  • .claude/skills/meta-orchestrator/helpers/update-delta.sh:1UNCHANGED (NOT-scope per kickoff; schema NOT extended; override_log[] field documented in references/mode-overrides.md §5 as DEFERRED design-only spec).
  • .claude/skills/meta-orchestrator/helpers/classify-work.sh:1UNCHANGED (DN-3 binding).
  • .claude/skills/meta-orchestrator/helpers/update-cache.sh:1UNCHANGED.
  • No .claude/rules/*.md modified.
  • No .husky/* modified (classifier-protected; cap-bump = separate maintainer PR per feat(meta-orchestrator): Stage 3 — delta-diff helper + reconciliation rule + paired-negative #243 follow-up note).
  • No allowed-tools frontmatter expansion (classifier-protected).
  • No existing packages/core/principles/*.test.ts modified (principle 18/19 verified green via pnpm exec vitest run principles/ — 134/134 pass).
  • docs/meta-factory/prior-art-evaluations.md:1 UNCHANGED — SSOT row fix(hooks): force visible "🟢 Простыми словами" block in end-of-turn reminder #81 shipped in Stage 3 R-phase patch; not touched here per feedback_no_drive_by_prs.
  • File scope exactly 3 NEW + 0 modified; verified via git diff origin/staging --name-only.

Pre-merge verification — owner per item

  • helper executable (-rwxr-xr-x) — verified: Worker ls -la per REPORT row 1 + remote checkout
  • helper smoke green (OVERRIDE_MODE=SOLO\nOVERRIDE_REASON=...; exit 0) — verified: Worker REPORT row 2
  • helper rejects no-reason (exit 2; stderr --reason required) — verified: Worker REPORT row 3
  • helper rejects short-reason (exit 2) — verified: Worker REPORT row 4
  • helper rejects multi-flag (exit 2; stderr multi-flag collision) — verified: Worker REPORT row 5
  • helper no-flag = exit 1 — verified: Worker REPORT row 6
  • paired-negative test 14/14 green — verified: Worker REPORT row 7 (Tests 14 passed (14))
  • all 19 principle tests still green (134 passed / 4 skipped) — verified: Worker REPORT row 9
  • markdownlint clean on both ref doc + SKILL.md — verified: Worker REPORT row 10 (0 errors)
  • SKILL.md ≤ 600 LOC (= cap) — verified: Worker REPORT row 11 + orchestrator wc -l post-push
  • ref doc carries blockquote > **Authoritative for:** header — verified: Worker REPORT row 12 (line ≤10)
  • helper carries @dual-pair + @cc-only-rationaleverified: Worker REPORT rows 13-14
  • husky pre-commit clean on staged set — verified: Worker REPORT row 15
  • commit body carries 2× Prior-art: trailers + full §1.7 trailer with file:line citations — verified: orchestrator git log -1 ea82de8 --format=%B post-push
  • branch pushed to origin/feat/meta-orchestrator-stage-4-cli-overridesverified: orchestrator git ls-remote origin shows ea82de8
  • CI ci-success green on this PR — owner: GitHub Actions (post-push)
  • Auto-merge into staging on ci-success green — owner: GitHub auto-merge (post-CI)

Acceptance criteria

  • 6-flag set parsed (DIRECT/SOLO/BUNDLE/PAIR/DECOMPOSE/RESEARCH) — verified: ALL-SIX-FLAGS-ROUND-TRIP test case (label-mutation falsifier)
  • --reason=<text> with ≥MO_OVERRIDE_REASON_MIN chars (default 20) required — verified: REASON-MISSING + REASON-TOO-SHORT + REASON-MIN-OVERRIDE-VIA-SEAM test cases
  • ≥2 flags = collision (exit 2) — verified: MULTI-FLAG-COLLISION test case
  • Zero flags = exit 1 silent (routing tree proceeds) — verified: NO-FLAG + EMPTY-INPUT test cases
  • MO_OVERRIDE_REASON_MIN seam works — verified: REASON-MIN-OVERRIDE-VIA-SEAM test case
  • OVERRIDE_REASON preserves embedded spaces — verified: REASON-WITH-EMBEDDED-SPACES test case
  • Helper output schema = OVERRIDE_MODE=<NAME>\nOVERRIDE_REASON=<reason>verified: SINGLE-VALID test case
  • classify-work.sh UNCHANGED — verified: git diff origin/staging empty for that file
  • update-delta.sh UNCHANGED — verified: same
  • SKILL.md UNCHANGED (D.1-D.3 deferred per fallback) — verified: git diff origin/staging empty for SKILL.md

Known residuals

  1. pre-push.test.ts:59 failure on staging — pre-existing on staging (confirmed by Worker via stash-test before any Stage 4 edits); unrelated to this PR. 18/19 hook tests pass (the failing one tries to invoke tsx validator on a fake-SHA case in a context where the dep isn't resolved).
  2. T-Stage4-A probe deferred — CC slash-command arg-passing behaviour for ${umbrella} with embedded whitespace-tokens was NOT empirically verified in this session (quota budget). Calling convention documented as quoted-single-arg invocation: /meta-orchestrator "my-task --mode-solo --reason=because...". Follow-up R-phase probe recommended before user-facing UX is finalised.
  3. Worker isolation incident — Mode A inline Worker's commit transiently landed on local staging ref in main workdir despite isolation: "worktree". Recovered via direct SHA push to remote feat branch (fast-forward, non-destructive, work fully preserved on remote feat branch). Local staging ref in maintainer's workdir now sits at ea82de8 while remote staging correctly remains at c4b63ac — maintainer may want to manually resync local staging to origin/staging when convenient (the orchestrator's destructive-ops hook required user confirmation; left for maintainer to run). This is a parallel-subwave-isolation.md §3 #shared-workdir-parallel incident class observation; tracker counter +1.

Follow-ups (separate atomic PRs per feedback_no_drive_by_prs)

  1. SKILL.md wiring (D.1-D.3) — once 600-line cap is bumped via separate maintainer PR (per feat(meta-orchestrator): Stage 3 — delta-diff helper + reconciliation rule + paired-negative #243 follow-up note), wire:
    • §0 Step 0 preamble: ${CLAUDE_SKILL_DIR}/helpers/parse-override-flags.sh "${umbrella:-}" invocation + OVERRIDE_MODE detection + emission contract
    • §2.5 Step 5 short-circuit: if OVERRIDE_MODE is set: Mode = OVERRIDE_MODE at top of routing tree pseudo-code
    • ## See also bullet pointing to references/mode-overrides.md
  2. override_log[] schema extension — extend _master-backlog-delta.json schema (NOT writer helper, but body-populated array per Stage 2C precedent) + wire active rate-check per references/mode-overrides.md §5. Currently DEFERRED.
  3. T-Stage4-A empirical probe — verify CC's actual ${umbrella} arg-passing behaviour (single-token vs full-post-command-string); confirm or revise the quoted-single-arg invocation convention.
  4. Stage 5 (dogfood) — first /meta-orchestrator invocation on a real backlog item using the new pipeline (with override flags exercised at least once via D.1-D.3 wired path).

Auto-merge

This PR targets staging (trunk per project_automerge_staging_plan — staging=GitHub default + trunk since 2026-05-22). Auto-merge will fire on ci-success green via the repo's branch-protection configuration.

…) + parse helper + paired-negative

Stage 4 of meta-orchestrator-mode-triage-and-planner umbrella (optional per design §13).
Wires the 6 override flags referenced in SKILL.md §2.5 Step 5 predicates but never parsed.
Pure-bash parser; OVERRIDE_MODE+REASON emission; routing tree short-circuit. Anti-pattern
guard mechanism documented in references/mode-overrides.md §5 (active rate-check deferred
to follow-up — override_log[] schema field absent from current _master-backlog-delta.json
per Stage 2B/2C contract; mechanism wired only when schema extension lands).

SKILL.md wiring (Step 0 preamble + §2.5 short-circuit) deferred to follow-up after
maintainer cap-bump PR (per #243 follow-up note). Stage 4 ships A/B/C infrastructure
only; helper is fully testable in isolation. Cap blocker: SKILL.md at 600 LOC; D.1-D.3
add ~11-12 LOC; achievable compression ~8-9 LOC max (Phase -1 reviewer MAJOR-3 finding).

Prior-art: docs/meta-factory/prior-art-evaluations.md#81 (oh-my-openagent alias dispatch REFERENCE; R-phase Area C verdict 2026-05-26 BUILD for flags + REFERENCE for aif-handoff AGENT_USE_SUBAGENTS shape).
Prior-art: docs/meta-factory/research-patches/2026-05-25-meta-orchestrator-mode-triage-and-planner-design.md §10 (override-flags spec — 6 flags + reason ≥20 chars + emission contract + anti-pattern).

§1.7: forward-check applied — .claude/rules/build-first-reuse-default.md:50 (REUSE plan-cache.md:3-4 / update-delta.sh:1-40 header + seams pattern for new helper + ref doc; BUILD parse-override-flags.sh per R-phase Area C verdict — no upstream bash CLI override parser with 6-flag mutually-exclusive semantics; confirmed R-phase §3 evidence); .claude/rules/no-paid-llm-in-ci.md:23 (deterministic bash, zero API calls); .claude/rules/doc-authority-hierarchy.md:71 (references/mode-overrides.md:3-6 carries blockquote Authoritative-for header mirroring plan-cache.md:3-4 format; Class C declared line 3); .claude/rules/dual-implementation-discipline.md:73 (helpers/parse-override-flags.sh:37 carries @dual-pair: meta-orchestrator-mode-overrides + @cc-only-rationale:38; ref doc line 27 carries matching HTML comment anchor); .claude/rules/ai-laziness-traps.md:115 (T3/T7/T11/T13/T15/T16/T19/T20 + T-MO-design-A + T-Stage4-A domain-specific traps active; T19 own cold-QA: all 17 VERIFY rows run; T20 all recommendations backed by tool-call evidence); .claude/rules/reviewer-discipline.md:24 (Phase -1 cold-review 2 BLOCKER + 4 MAJOR + 5 MINOR findings resolved before dispatch; cap-blocker finding MAJOR-3 correctly handled via fallback path); .claude/rules/parallel-subwave-isolation.md:25 (CC isolation:worktree verified — worktree branch feat/meta-orchestrator-stage-4-cli-overrides from origin/staging HEAD c4b63ac); .claude/rules/phase-research-coverage.md:50 (this trailer = §1.7 check); .claude/rules/phase-research-coverage.md:107 §1.11 (base = origin/staging c4b63ac = Stage 3 merged PR #243 verified via git log). backward-check sweep — .claude/skills/meta-orchestrator/references/mode-overrides.md:1 (NEW 171 LOC Class C ref doc; blockquote header; §1-§7 sections; @dual-pair anchor line 27); .claude/skills/meta-orchestrator/helpers/parse-override-flags.sh:1 (NEW 128 LOC pure-bash helper; @dual-pair line 37; @cc-only-rationale line 38; MO_OVERRIDE_REASON_MIN seam line 42; bash 3.2 compatible — no associative arrays; case statement alias map lines ~79-87); packages/core/hooks/parse-override-flags.test.ts:1 (NEW 137 LOC paired-negative — 9 cases / 14 tests via it.each; capability commit ≥80 LOC under packages/; spawnSync shape mirrors delta-diff.test.ts:68 + update-delta.test.ts:32); .claude/skills/meta-orchestrator/SKILL.md:1 UNCHANGED (deferred pending cap-bump follow-up; D.1-D.3 edits deferred; SKILL.md stays at 600 LOC = pre-commit cap limit); .claude/skills/meta-orchestrator/helpers/update-delta.sh:1 UNCHANGED (schema unchanged; override_log[] is design-only spec in references/mode-overrides.md §5); .claude/skills/meta-orchestrator/helpers/classify-work.sh:1 UNCHANGED (DN-3 binding); .claude/skills/meta-orchestrator/helpers/update-cache.sh:1 UNCHANGED; .claude/rules/*.md UNCHANGED; .husky/* UNCHANGED (classifier-protected); allowed-tools frontmatter UNCHANGED; existing packages/core/principles/*.test.ts UNCHANGED; docs/meta-factory/prior-art-evaluations.md:1 UNCHANGED (SSOT row #81 shipped Stage 3; not touched per feedback_no_drive_by_prs).
@artyhoo
artyhoo merged commit fbe1bf0 into staging May 26, 2026
22 checks passed
artyhoo added a commit that referenced this pull request Aug 10, 2026
… cannot drift again (#1362)

`.github/workflows/audit-self.yml` defines 31 top-level jobs; `ci-success` needed 28.
Excluding itself, exactly two were missing — `shipped-prettier` (:99) and
`framework-fresh-install-validate-multistack` (:1094). Both trigger on `pull_request`
and neither carries an `if:` guard, so both run on every PR. Branch protection on
staging requires only `ci-success`, `fidelity-verdict-in-pr-body` and
`stale-revert-in-pr-diff`, so neither was separately required: either could go RED
while `ci-success` was green and the PR merged.

`git log -S` on each needs-entry returns empty — neither was EVER wired, from the day
the jobs shipped (#540, #815). Both were green on the latest staging run
(31371037396), re-confirmed before pushing, so wiring them in does not deadlock merges.

The two lines are not the deliverable. `scripts/ci-success-gate.sh:6-7` asserted in its
own header that audit-self.yml «`needs:` every audit-self PR job» — false, and detected
by nothing: the script only judges the RESULTS of jobs already listed (it consumes
`join(needs.*.result, ' ')`). The gaps are the predicted output of a check resting on
someone reading the workflow, which attention-is-not-a-mechanism.md §1 rejects as a
detection layer. `launch-preannounce-track/kickoff.md:46` had already ordered this exact
fix once — «immediately, one line, before anything else» — and it never happened, then
drifted further. A fix without a mechanism regressed once already.

So the mechanism ships with it: `packages/core/principles/36-ci-needs-completeness.test.ts`
asserts every job defined in audit-self.yml appears in `ci-success.needs`, with an
explicit allowlist carrying a per-entry justification. The allowlist has exactly one
member — `ci-success` itself, which cannot depend on itself. No job is push-only: the
sole `if:`-guarded job (`pr-commit-trailers`) is wired in, and `skipped` already counts
as passing at ci-success-gate.sh:30, so an `if:` guard is not grounds for exclusion.

Channel (rule-enforcement-channel-selection.md §3): mechanically detectable → gate, not
reminder. A principle test is the earliest channel that actually fires — the principles
suite runs at pre-push (pre-push.ts:1267) AND in CI (audit-self.yml:210), so CI stays
the backstop rather than the only line. Deliberately NOT placed inside ci-success-gate.sh:
that script is the body of the very job under test, would run in CI only, and would
conflate judging job results with asserting the job graph.

Paired-negative, both directions, exercised against the real file — not a synthetic
fixture (destination-environment-verification.md §4 `#contract-that-cannot-fail`):
arm (c) deletes a real `needs:` entry from the real text and expects RED; arm (d) seeds
a new job definition and expects RED; arm (f) renames the aggregate and proves the
parser returns null instead of passing vacuously on an empty list. Confirmed live by
seeding each mutation on disk and running the gate: RED naming the exact job both times,
GREEN after restore. Suite 7/7; full principles suite 38 files / 360 tests green.

The ci-success-gate.sh header now states its real scope — it judges results, never the
wiring — and names principle 36 as what keeps the aggregate claim true.

§1.7: Forward-check — deterministic node/vitest, zero API-billed calls
(no-paid-llm-in-ci.md §1); BFR consult recorded as SSOT #244 with a BUILD verdict;
capability commit carries this trailer plus `Prior-art:` below; touches no
maintainer-owned artefact (.claude/rules/** and .husky/** unedited per the CLAUDE.md
Artifact Ownership Contract). Backward-check — delegated cold to
agents/backward-sweep-auditor.md on the change's class only (registry completeness
assumed rather than asserted), never the diff; two sibling surfaces verified by hand and
judged SEPARATE, with evidence, in the PR body: packages/core/hooks/pre-push.ts has zero
consumer-matrix sections against the claim at
.claude/orchestrator-prompts/launch-preannounce-track/kickoff.md:46, and
tests/consumer-matrix/python-unfamiliar-stack-cell.sh:39 claims it runs under
`make consumer-matrix` while Makefile:34-38 invokes only pnpm-monorepo-cell.sh and
npm-tarball-cell.sh.

Prior-art: prior-art-evaluations.md#244 (actionlint RuleJobNeeds / zizmor / marketplace aggregate actions, verdict BUILD — upstream validates the `needs:` edges that ARE written, or aggregates results at runtime; none asserts statically that the list mirrors the job population. DeepWiki on rhysd/actionlint: «no built-in rule to enforce that every job defined in the workflow must be depended on by another job»).

Co-authored-by: Test <test@example.com>
artyhoo added a commit that referenced this pull request Aug 10, 2026
…and gate the two lists against it (#1368)

`.github/workflows/workflow-integrity.yml:75` claimed its `required_contexts` list was
«the single place recording which contexts must be registered». It was neither.

NOT SINGLE: `scripts/run-local-ci-sweep.sh:15-17` independently stated the same set, derived
neither from it nor from the API. NOT CURRENT: live protection on staging carries three
contexts (`gh api repos/artyhoo/getff/branches/staging/protection` with an admin token,
2026-08-10) — `ci-success`, `fidelity-verdict-in-pr-body`, `stale-revert-in-pr-diff` — while
workflow-integrity.yml listed two. Two hand-maintained lists, one population, nothing asserting
either was complete: the `#warning-nobody-reads` shape attention-is-not-a-mechanism.md §1
rejects as a detection layer. Same class as #1362, one level up.

Making one list the source of truth would fix their disagreement and leave the real hole open —
both could agree and both still omit a job. So the declaration moves to the population itself:
every job in a `pull_request`-triggered workflow now carries a `# required-context: yes|no —
<rationale>` marker, and packages/core/principles/37-required-context-completeness.test.ts
asserts both lists equal the declared set exactly. audit-self.yml is carved out except for
`ci-success` — its other 30 jobs route through the aggregate, which principle 36 asserts — and
arm (h) asserts that sibling still exists, so the carve-out cannot silently become a hole.

The substantive question — should the two unregistered discipline gates be required? — resolves
differently for each, and the difference is structural, not a matter of importance:

  * `capability PR carries Prior-art line in PR body (squash-survival)` (pr-body-prior-art.yml)
    — YES. No `paths:` filter, so it reports on every PR and registering it cannot deadlock.
    It is in its own workflow file, so cross-file `needs:` cannot reach it and registration is
    its only fail-closed transport. Declared required; NOT yet registered — a live hole.
  * `§1.7 forward+backward sections present in PR description` (discipline-self-check.yml:32)
    — NO, not in its current shape. The workflow is `paths:`-filtered at the trigger, and a
    workflow skipped by path filtering leaves its checks Pending, blocking the PR from merging
    (GitHub Docs, «Troubleshooting required status checks»). Registering it would deadlock
    every PR touching no discipline-bearing path. Making it required needs the filter moved
    from `on.pull_request.paths` into the job body first — a separate change, recorded at the
    marker rather than left implicit.

A backward sweep over the same class — «job in its own workflow file, unreachable by
`ci-success.needs`» — found a third instance the report did not name:
`framework-self-template-render.yml` runs the P1/P4/P6 probes on every PR, is aggregated by
nothing, and is registered by nothing. Declared required, and its suite added to the local
sweep (hermetic, ~5s); the alternative closure, relocating the job into audit-self.yml, is
noted at the marker and deliberately not bundled.

So five contexts are declared required and three are registered. Closing that delta is an
operator action: CI cannot verify registration at all (workflow-integrity.yml:32-42 —
GITHUB_TOKEN cannot read branch protection, measured on PR #1102), which is why that job stays
best-effort and this test gates the tractable half instead. Also recorded at the list: renaming
a declared-required job keeps this gate green while protection still points at the old string,
so any such rename must be paired with a re-registration.

Verified: principle 37 red before the markers (7/8 arms), green after; live-fired twice by
mutating the real tree — deleting a marker names the exact job, and reproducing the historical
one-list drift reports `declared but missing from the list: ["stale-revert-in-pr-diff"]`.
Full principles suite 368 passed, actionlint clean, format:check clean, typecheck clean,
run-local-ci-sweep.test.sh and meta-all-wired.test.sh both pass.

Prior-art: prior-art-evaluations.md#245 (safe-settings/Terraform are registrars that apply a
list without validating it against the job population — DeepWiki 2026-08-10; actionlint knows
branch protection only as a webhook event; all 40 zizmor audits are security-focused. Verdict
BUILD, one file, zero dependencies. Sibling of #244.)

Co-authored-by: Test <test@example.com>
artyhoo pushed a commit that referenced this pull request Aug 10, 2026
Third merge-forward on this branch; staging is moving fast today. #1368 appended its
own row 245 while this branch held that number. Resolution is the same shape as the
244 → 245 one: the row already ON staging keeps its ID (its commit trailer cites it),
and this branch's GNU-make-resolver row steps aside — now **246**.

Verified post-resolve: no conflict markers, no duplicate IDs across the whole table,
sequence ends 244 · 245 · 246, the blank line before the closing `---` is intact (its
loss during the first resolve turned the table into a setext heading — MD003), and
markdownlint reports 0 errors.

Trailer bookkeeping: commits 4e4231a and bf5ef84 cite `#244` and `#245`
respectively; after this merge the row is `#246`. Both citations are stale by
renumber, not by substance — no verdict, rationale or evidence changed, only the
row's position behind two upstream inserts. Principle 11 F1 resolves via the verbatim
SSOT path match (row 246's "Capability matched" cell names
packages/core/principles/37-make-target-claim-liveness.test.ts), which has been kept
correct across every renumber, and the squash body at merge time carries #246.

Prior-art: prior-art-evaluations.md#246 (GNU make `-n` as target-recipe resolver +
the absence of any tool verifying a script's prose claim about which build target
invokes it — HYBRID: ADOPT the resolver, BUILD only the claim-extraction slice).
artyhoo added a commit that referenced this pull request Sep 6, 2026
…quiring a resolvable referent (#1597 ledger L-1/B-3, K-5) (#1652)

## Summary

Two ways the capability gate disagreed with the definition it claims to mirror — ledger #1597 findings **L-1 / B-3** (the wide half, == C13 addendum B-3) and **K-5** (the real half). `CLAUDE.md:30` declares the prose definition a mirror of `packages/core/hooks/checks/prior-art.ts`; both halves of the disagreement are closed here, in the same commit as the prose.

## Changes

- **L-1 / B-3 — the LOC triggers taxed test material.** `packages/core/hooks/checks/prior-art.ts:231` and `:249` now skip test material in both LOC arms (`isExemptTestMaterial`, `packages/core/hooks/checks/prior-art.ts:221`): `*.test.*` / `*.spec.*` files and anything under `test(s)/`, `__tests__/` or `*fixtures/`. The carve-out cannot hide a capability — a commit that adds test material **alongside** a qualifying production file still trips on the production file — and it stops at `packages/core/principles/` (`ENFORCEMENT_FILE_RE`, `packages/core/hooks/checks/prior-art.ts:190`): a principle IS the enforcement capability, not a test for one.
- **K-5 — a positive trailer needed no referent.** `checkTrailerBody` accepted any ≥20-char payload that was not the `skipped` escape hatch, so `Prior-art: consulted — no entry applies` satisfied the gate on a capability commit. A positive line must now name something a reader can open (`REFERENT_RE`, `packages/core/hooks/checks/prior-art.ts:74`): an SSOT row (`prior-art-evaluations.md#N`), a concrete artefact path, or an issue/PR reference. Stacked lines keep working — a referent-free line is skipped so a later valid line can carry the commit.
- **Prose parity in the same commit.** `CLAUDE.md:36` now names three carve-outs on the LOC triggers instead of two, and `CLAUDE.md:50` enumerates the three referent forms. Three prose↔hook sync arms pin it (`packages/core/hooks/checks/prior-art.test.ts:1462`).
- **Diagnostics.** `packages/core/hooks/pre-push.ts:383` names the three accepted referent forms in the failure message.

## Measurement (why each side moved)

**L-1 / B-3 — the detector is the drifted side.** Over the last 250 first-parent commits on staging the ≥80-LOC arm fired on **27** commits; **18** of them added ONLY test files (9 under `packages/core/principles/`, 4 hook tests, 3 `packages/runtime-bridge/test/`, 2 skills, 1 synthesizer). The trailers it forced on the non-principle ones cited rows the commit never touched — `#242/#20/#16` on a hook-test pair (the C15 case), `#45 «unchanged by this»` on a runtime-bridge pair. The 9 principle files, by contrast, each carried a substantive on-topic citation (#244 actionlint, #245 safe-settings, #246 Vitest `test.include`, #251 markdownlint, #19 lychee, #229, #230, #248) — which is why the carve-out stops there.

The split matches the **other enforcement channel of the same invariant**, which has held this exact semantic since it shipped: principle 11 (SSOT #48) builds its capability set from non-test files only (`packages/core/principles/11-build-first-reuse-default.test.ts:192`) while singling principle tests out as needing «a dedicated SSOT entry with verbatim path OR a Prior-art trailer» (`packages/core/principles/11-build-first-reuse-default.test.ts:525`). This PR brings the pre-push channel into parity with the CI one.

**K-5 — error, not a transitional window, and a grammar wider than the strict reading.** Measured over the post-cutoff (`≥2026-05-12`) first-parent history: **145** capability commits carry a positive `Prior-art:` trailer.

| grammar | commits it would reject | rejects `consulted — no entry applies`? |
|---|---|---|
| strict `#ID` only (the literal documented form) | **23 / 145 (16%)** | yes |
| SSOT row \| artefact path \| issue/PR ref (shipped) | **2 / 145 (1.4%)** | yes |

The 23 the strict reading would reject are legitimate consults naming in-repo precedent or a research patch (`setup.d/lib.sh:359`, `research-patches/2026-05-23-guard-liveness-gate.md §2`, `PR #261`), so the strict reading was rejected on evidence. The 2 that still fail under the shipped grammar are both from before 2026-07-19. No transitional window: the pre-push arm only inspects unpushed commits, so merged history is never re-checked, and the recent rate is 1 in the last 250 first-parent commits.

## Review findings

- MAJOR: the ≥80-LOC / ≥50-LOC triggers classed new test files as capability commits, so two thirds of the gate's firings demanded a build-vs-reuse consult for work that adds no capability, and got rote citations back. Failure-scenario: a commit whose only new packages/ file is a 100-line `packages/core/hooks/hook-emit-prelude.test.ts` covering an existing prelude — `detectCapabilityReason` returned `"new file ≥80 LOC under packages/"` against the pre-fix module (probe output quoted under Test plan) and the real-git repro `tests/hooks/prior-art-trailer-hook.test.sh` sub-test 11 exited non-zero before the fix; both are clean after it, while sub-test 12 (a new principle file) stays non-zero on both sides.
- MAJOR: a positive `Prior-art:` trailer that named nothing satisfied the gate on a capability commit. Failure-scenario: a commit adding the explicit dependency `some-new-dep: ^1.0.0` with the body line `Prior-art: consulted — no entry applies` — `checkTrailerBody(...).code === 0` against the pre-fix module and the real-git repro sub-test 13 exited 0, so a genuinely new dependency merged with a trailer carrying no referent to any register row, artefact, or PR; the squash message then propagated it as the project's own precedent for what a consult looks like.

### §1.7 Forward-check applied

Both fixes land on the channels the gate already owns — the pre-push §7 arm (`packages/core/hooks/pre-push.ts:367` runs the same detector) and the PR-body arm (`packages/core/hooks/checks/pr-body-prior-art-bin.ts:23`) — with no new channel introduced, per the earliest-reachable-channel invariant. The prose↔hook obligation that `CLAUDE.md:30` asserts is itself mechanised rather than left to attention: `packages/core/hooks/checks/prior-art.test.ts:1462` reads `CLAUDE.md` from disk and fails if the capability definition stops naming the test-material carve-out or the `packages/core/principles/` exception, and if the trailer-syntax section stops naming all three referent forms — the counter to `#warning-nobody-reads` in `.claude/rules/attention-is-not-a-mechanism.md:29`. Each sync arm ships with a paired negative (`packages/core/hooks/checks/prior-art.test.ts:1476`).

### §1.7 Backward-check applied

Swept the sibling surfaces of both classes. The LOC arms had exactly two call sites, both converted (`packages/core/hooks/checks/prior-art.ts:231`, `packages/core/hooks/checks/prior-art.ts:249`); the third detector arm (`isNewDepAdded`) is path-independent and unaffected. The parallel enforcement channel for the same invariant was checked before choosing the carve-out shape and already excluded test files (`packages/core/principles/11-build-first-reuse-default.test.ts:192`), so this narrows a divergence rather than creating one. On the trailer side, the three existing tests that encoded the pre-K-5 grammar were found and updated in place rather than deleted — the two space-stripping mutation-killers keep their exact length arithmetic with `#1271` standing in for filler (`packages/core/hooks/checks/prior-art.test.ts:577`, `packages/core/hooks/checks/prior-art.test.ts:1051`), and the C1 free-form case now asserts the honest post-fix semantic: a non-SSOT referent passes the existence arm because it has nothing to resolve (`packages/core/hooks/checks/prior-art.test.ts:807`). The two sibling PR-body gates (`packages/core/hooks/checks/pr-body-fidelity.ts`, `packages/core/hooks/checks/pr-stale-revert.ts`) parse their own trailers and share no code with this grammar. No static import was added from this shipped check to an unshipped util — the change is two module-local regexes.

## Fidelity verdict

FIDELITY: skipped — no kickoff or spec substrate; this is a direct ledger-driven fix to the prior-art gate, verified by paired negatives that are RED against the pre-fix module and by a measurement over the repository's own first-parent history.

## Test plan

- `npx vitest run hooks/` (in `packages/core`) — **1399 passed / 73 files**, including the 26 new arms in `packages/core/hooks/checks/prior-art.test.ts` (test-material carve-out across 9 path shapes, four paired negatives, the referent grammar with its vacuity probes and stacked-line cases, three prose↔hook sync arms).
- `npx vitest run principles/ --no-file-parallelism` (in `packages/core`) — **477 passed / 2 skipped / 47 files**.
- `bash tests/hooks/prior-art-trailer-hook.test.sh` — **19 pass / 0 fail** after the fix (4 new real-git sub-tests). Before it: **17 pass / 2 fail**, the failures being exactly new sub-tests 11 and 13; sub-tests 12 and 14 (the paired negative and the paired positive) are green on both sides, so the carve-out is narrowed and the grammar widened only where intended.
- RED evidence against the pre-fix module (`git show HEAD:packages/core/hooks/checks/prior-art.ts` copy, run under tsx):
  - `detectCapabilityReason(test-only commit)` = `"new file ≥80 LOC under packages/"` → `null` after
  - `detectCapabilityReason(principles/99-new.test.ts)` = `"new file ≥80 LOC under packages/"` on **both** sides (control)
  - `detectCapabilityReason(hooks/checks/newcheck.ts)` = `"new file ≥80 LOC under packages/"` on **both** sides (control)
  - `checkTrailerBody('Prior-art: consulted — no entry applies').code` = `0` → `1` after
  - the SSOT-row, artefact-path and PR-reference trailers = `0` on **both** sides (controls)
- `npx tsc --noEmit -p packages/core` — clean.
- `bash scripts/build-getff-dist.sh --check` — «in sync with the repo root (1064 files)»; MANIFEST regenerated in the same commit as the files it pins, and re-verified after the merge-forward.
- `SNAPSHOT_MODE=capture bash tests/install-sh/snapshot.sh` — three cells move in each of the eight npm baselines (`packages/core/hooks/checks/prior-art.ts`, `packages/core/hooks/pre-push.ts`, `.ai-factory/refresh-baseline.json`); cargo / python / go untouched, no host-drift hunk to discard this time. Re-run after the merge-forward: no further change.
- Pre-push hook green on the pushed head.

## Prior-art consult

Prior-art: prior-art-evaluations.md#56 (§7 Prior-art trailer parser — capability-commit detection + trailer validation, verdict BUILD; this change is a semantics fix inside that row's own module, no new capability and no new dependency).

Prior-art: prior-art-evaluations.md#23 (commitlint, verdict ADOPT VOCABULARY — «custom trailer enforcement via regex on raw commit body»; the referent grammar stays inside that adopted vocabulary, adding no dependency, and the row's revisit trigger «§9 hand-roll grows beyond 2 substance arms» is unchanged: this is the §7 arm, not §9).

Not a capability change: every touched path is a modification — no new file under `packages/`, no new explicit dependency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant