Skip to content

gate: extend ADR maintainer approval path predicate to .claude/skills/** — make Prime Directive #14 enforced (#9319 ruling) #9395

Description

@os-zhuang

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/**:

Acceptance

  1. A PR touching only .claude/skills/** shows ADR maintainer approval red until maintainer approval, and stays red while auto-merge is armed.
  2. A PR touching neither surface is unaffected.
  3. 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.

Activity

  1. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    ContributorAuthor

    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.


    Generated by Claude Code

  2. self-assigned this
    on Aug 17, 2026
  3. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    ContributorAuthor
    {
      "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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions