You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
gate: extend ADR maintainer approval path predicate to .claude/skills/** — make Prime Directive #14 enforced (#9319 ruling) #9395
Implements the maintainer's #9319 decision-2 ruling (2026-08-17, 「同意」 batch): PR #9238 proved a .claude/skills/** change can reach main through the merge queue with zero human clicks, bypassing Prime Directive #14 (skill/ADR PRs are human-merge only). Prose already forbade it; only a gate makes it impossible.
The work
Extend the ADR merge-approval gate so PRs touching .claude/skills/** are held to the same contract as docs/adr/**:
scripts/check-adr-merge-approval.mjs — widen the path predicate that decides whether a PR is gate-scoped; keep the two surfaces' reason strings distinct in output (an ADR PR and a skills PR fail with wording naming their own rule).
.github/workflows/adr-merge-approval.yml — confirm the trigger set needs no change (it fires on all PRs and the script decides scope; verify rather than assume).
Self-tests: extend the gate's own test coverage for the new path class, including the armed-auto-merge case and a mixed-diff PR (skills + other files ⇒ gate-scoped).
Acceptance
A PR touching only .claude/skills/** shows ADR maintainer approval red until maintainer approval, and stays red while auto-merge is armed.
A PR touching neither surface is unaffected.
Reverse verification in the PR body: demonstrate the gate red on a synthetic skills-path diff before approval.
⛔ This PR itself touches gate scripts — it is NOT skill-surface, but treat sequencing carefully: it must not weaken any existing check while landing. No Settings change is needed (the check is already required).
Refs: #9319 (the finding and ruling) · #9238 (the bypass instance) · #8012 (armed-auto-merge clause) · Prime Directive #14.
Claim: PM seat dispatch
Session: session_01NYgmGheCzM6NrHZN436Cxf
Branch: claude/issue-9395-adr-gate-skills-paths
Scope: as the card specifies — scripts/check-adr-merge-approval.mjs path predicate + self-tests, workflow trigger verification, #8012 armed clause coverage for the new path class, reverse verification in the PR body. ⛔ No weakening of any existing check while landing; no Settings change needed.
Routing authority: maintainer ruling on #9319 decision 2, 2026-08-17 「同意」 batch.
Label pm:queue → pm:dispatched in the same stroke.
{
"issue": 9395,
"status": "done",
"branch": "claude/issue-9395-adr-gate-skills-paths",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9407",
"premise_still_valid": true,
"summary": "The ADR merge-approval gate's single ADR path prefix became a GOVERNED_SURFACES table holding docs/adr/ and .claude/skills/ under identical pass conditions (an APPROVED current standing plus no armed auto-merge) with distinct failure wording — an ADR red cites the 2026-08-12 approval ruling and #6741, a skills red cites Prime Directive #14 and the #9319 decision-2 ruling, and a mixed diff carries both blocks. One path hit governs the whole PR; proportion is never computed. The report renderer was made pure (renderVerdict) so the distinct-wording requirement is asserted on the text an operator reads, and PR #9238 joined the historical replay fixtures as a real API capture pinned RED. The workflow needed no trigger change: it carries no paths filter, and #9238 (a skills-only diff) already produced a completed 'ADR maintainer approval' check run — verified both structurally and empirically, stated in the workflow header and the PR body. The required job name is untouched. No existing check weakened: every prior assertion still runs.",
"tests": "All at cc1a065 (git rev-parse --short HEAD after the final commit, quoted from the union run). `pnpm check:adr-merge-approval` -> '✓ 96 assertions over the real decide() / review-state / PR-resolution paths' (origin/main baseline: 66; +30 new). Gate families re-derived with `node scripts/pm/dispatch-gates.mjs` against the actual changed paths, all exit 0: check:node-version ('OK, 28 setup-node step(s) across 25 workflow(s)'), check:required-contexts ('adr-merge-approval.yml:adr-merge-approval -> ADR maintainer approval'), check:shard-attestation ('2 aggregate gate(s) count 3 declared leg(s)'), check:workflow-status-functions ('OK, scanned 25 workflow file(s)'), plus check:nul-bytes ('OK, scanned 6108 text file(s), no raw ASCII control bytes') and `npx eslint scripts/check-adr-merge-approval.mjs --no-inline-config` (clean, exit 0). REVERSE VERIFICATION, predicted then observed, four offline-replay legs over the same skills+package.json mixed diff: [1] OLD gate (origin/main copy), zero reviews -> predicted GREEN (the hole), observed exit=0 'No files under docs/adr/'; [2] NEW gate, same inputs -> predicted RED, observed exit=1 citing '.claude/skills/**' and 'Prime Directive #14'; [3] NEW gate, APPROVED + disarmed -> predicted GREEN, observed exit=0; [4] NEW gate, APPROVED + ARMED -> predicted RED for the arming, observed exit=1 'AUTO-MERGE IS ARMED'. Rows 3/4 are the clause-(2) emptiness proof on the new class. A fifth leg ran gate mode over a real git diff in a throwaway detached worktree with a synthetic .claude/skills edit (never committed, never pushed, removed): OLD exit=0 green, NEW exit=1 after entering the gated path ('.claude/skills/** touched -- consulting reviews of PR #9379'); the local red is the fail-loud path (HTTP 401, no usable token in this container), not the approval verdict — reported as observed rather than as the template's presumption. No ablation and no dist involved: the gate script imports node builtins only. `pnpm install --frozen-lockfile` was still run because the derived workflow gates need `yaml`. skip-changeset label applied and READ BACK as present alongside the bot's ci/cd and size/l labels (scripts + workflow only; nothing published).",
"open_questions": [],
"out_of_scope_findings": [
"filed as #9403: AGENTS.md's Prime Directive #14 text names only docs/adr/**, so the directive the gate now cites in its skills-surface failure does not itself name that surface (unlabeled — governance prose, maintainer's call)",
"filed as #9404 (finding): the published skills/ catalog is human-merge by pm-dispatch convention but ungated — two of that fork's three prefixes are now enforced; deliberate here since the ruling names .claude/skills/** only"
]
}
PR #9407 is draft, not marked ready, no auto-merge armed. CI status at report time: in_progress (reported at draft-PR time per the 2026-08-10 rule; the convergence wait is the PM's).
Activity
os-zhuang commentedon Aug 17, 2026
Claim: PM seat dispatch
Session:
session_01NYgmGheCzM6NrHZN436CxfBranch:
claude/issue-9395-adr-gate-skills-pathsScope: as the card specifies —
scripts/check-adr-merge-approval.mjspath predicate + self-tests, workflow trigger verification, #8012 armed clause coverage for the new path class, reverse verification in the PR body. ⛔ No weakening of any existing check while landing; no Settings change needed.Routing authority: maintainer ruling on #9319 decision 2, 2026-08-17 「同意」 batch.
Label
pm:queue→pm:dispatchedin the same stroke.Generated by Claude Code
feat(devx): gate .claude/skills/** on the ADR merge-approval check (#…
os-zhuang commentedon Aug 17, 2026
{ "issue": 9395, "status": "done", "branch": "claude/issue-9395-adr-gate-skills-paths", "pr": "https://github.com/objectstack-ai/objectstack/pull/9407", "premise_still_valid": true, "summary": "The ADR merge-approval gate's single ADR path prefix became a GOVERNED_SURFACES table holding docs/adr/ and .claude/skills/ under identical pass conditions (an APPROVED current standing plus no armed auto-merge) with distinct failure wording — an ADR red cites the 2026-08-12 approval ruling and #6741, a skills red cites Prime Directive #14 and the #9319 decision-2 ruling, and a mixed diff carries both blocks. One path hit governs the whole PR; proportion is never computed. The report renderer was made pure (renderVerdict) so the distinct-wording requirement is asserted on the text an operator reads, and PR #9238 joined the historical replay fixtures as a real API capture pinned RED. The workflow needed no trigger change: it carries no paths filter, and #9238 (a skills-only diff) already produced a completed 'ADR maintainer approval' check run — verified both structurally and empirically, stated in the workflow header and the PR body. The required job name is untouched. No existing check weakened: every prior assertion still runs.", "tests": "All at cc1a065 (git rev-parse --short HEAD after the final commit, quoted from the union run). `pnpm check:adr-merge-approval` -> '✓ 96 assertions over the real decide() / review-state / PR-resolution paths' (origin/main baseline: 66; +30 new). Gate families re-derived with `node scripts/pm/dispatch-gates.mjs` against the actual changed paths, all exit 0: check:node-version ('OK, 28 setup-node step(s) across 25 workflow(s)'), check:required-contexts ('adr-merge-approval.yml:adr-merge-approval -> ADR maintainer approval'), check:shard-attestation ('2 aggregate gate(s) count 3 declared leg(s)'), check:workflow-status-functions ('OK, scanned 25 workflow file(s)'), plus check:nul-bytes ('OK, scanned 6108 text file(s), no raw ASCII control bytes') and `npx eslint scripts/check-adr-merge-approval.mjs --no-inline-config` (clean, exit 0). REVERSE VERIFICATION, predicted then observed, four offline-replay legs over the same skills+package.json mixed diff: [1] OLD gate (origin/main copy), zero reviews -> predicted GREEN (the hole), observed exit=0 'No files under docs/adr/'; [2] NEW gate, same inputs -> predicted RED, observed exit=1 citing '.claude/skills/**' and 'Prime Directive #14'; [3] NEW gate, APPROVED + disarmed -> predicted GREEN, observed exit=0; [4] NEW gate, APPROVED + ARMED -> predicted RED for the arming, observed exit=1 'AUTO-MERGE IS ARMED'. Rows 3/4 are the clause-(2) emptiness proof on the new class. A fifth leg ran gate mode over a real git diff in a throwaway detached worktree with a synthetic .claude/skills edit (never committed, never pushed, removed): OLD exit=0 green, NEW exit=1 after entering the gated path ('.claude/skills/** touched -- consulting reviews of PR #9379'); the local red is the fail-loud path (HTTP 401, no usable token in this container), not the approval verdict — reported as observed rather than as the template's presumption. No ablation and no dist involved: the gate script imports node builtins only. `pnpm install --frozen-lockfile` was still run because the derived workflow gates need `yaml`. skip-changeset label applied and READ BACK as present alongside the bot's ci/cd and size/l labels (scripts + workflow only; nothing published).", "open_questions": [], "out_of_scope_findings": [ "filed as #9403: AGENTS.md's Prime Directive #14 text names only docs/adr/**, so the directive the gate now cites in its skills-surface failure does not itself name that surface (unlabeled — governance prose, maintainer's call)", "filed as #9404 (finding): the published skills/ catalog is human-merge by pm-dispatch convention but ungated — two of that fork's three prefixes are now enforced; deliberate here since the ruling names .claude/skills/** only" ] }PR #9407 is draft, not marked ready, no auto-merge armed. CI status at report time:
in_progress(reported at draft-PR time per the 2026-08-10 rule; the convergence wait is the PM's).Generated by Claude Code
feat(devx): gate .claude/skills/** on the ADR merge-approval check (o…