feat(plugin): S8 — README per-harness install + fetch ref → main (umbrella finale) - #672
Merged
Conversation
README 'As a Claude Code plugin (per-harness)' section: the marketplace-add/install flow, skills auto-trigger, the honest soft/hard boundary (T16 — the plugin never wires git/CI; /rules-as-tests:install-enforcement does, opt-in), the OpenCode path + accepted degradation, and plugin-vs-./setup. §Why this exists untouched (read-only per the Artifact Ownership Contract). fetch-and-wire.sh: decouple RAT_INSTALL_REF from the plugin version → default 'main'. Evidence: the framework's latest release tag v0.3.0 is 2026-05-06, staging is 72 files / +17551 ahead in install.sh + payload (incl. the hardening fixes #531/#551/#635/#636 the S5 cold-review valued); origin/main (2026-06-18) carries the current installer (only 65 lines behind staging). Pinning to v<plugin-version> would ship a 6-week-stale install.sh. Plugin keeps its own 0.1.0 version line; a reproducible per-plugin release tag is a future maintainer action. Spec §6 updated. Verified: composite e2e on a throwaway consumer — SessionStart bootstrap fires, inject-matching-rule reads consumer rules, /install-enforcement seam dry-run writes nothing + --apply wires .husky pre-commit/pre-push + CI, manifest version-parity True; all 6 plugin bash tests + principle 21/24/09 (39) green; README markdownlint 0. Prior-art: skipped — docs + a config-default change under plugin/, no new dependency or capability.
CLAUDE.md Umbrella closure schema: Final PR #672, all S0–S8 merged to staging. Records the stage-PR map + 3 maintainer-owned residuals (live /plugin install unproven-from-automation, install.sh-vs-plugin agent divergence, reproducible-tag option). Prior-art: skipped — closure doc, no new dependency or capability.
artyhoo
added a commit
that referenced
this pull request
Jun 22, 2026
…g release) (#673) * fix(hooks): pre-push delivery completeness on pnpm monorepo (#635, #636) (#638) * fix(hooks): probe tsx runtime in pre-push dispatcher, degrade to bash fallback instead of crash (#636) * fix(install): refresh ships hooks/package.json type:module marker in do_refresh (#635) * fix(templates): shipped CI templates pass the framework's own zizmor gate + regression guard + honest brownfield hint (#637) (#639) * fix(templates): harden shipped CI workflows to pass zizmor (artipacked + unpinned-uses + template-injection) (#637) Both shipped CI templates failed the framework's own zizmor pre-push gate, blocking a fresh consumer on the framework's own scaffolding. Apply three hardening transforms to each: - artipacked: persist-credentials: false on every actions/checkout step - unpinned-uses: SHA-pin every action uses: (KEEP the # vX.Y.Z comment) - template-injection: use runner GITHUB_BASE_REF env in the Stryker run-block instead of interpolating ${{ github.base_ref }} into shell The actions/cache key: interpolation of github.base_ref is left unchanged (not a shell context — zizmor only flags the run:-block one). Prior-art: skipped — shipped-template security hardening (persist-credentials + SHA-pin + injection fix), no new capability * fix(pre-push): guard shipped CI templates against zizmor drift + honest brownfield fix hint (#637) Extend the pre-push zizmor gate (and the audit-self.yml CI backstop) to also scan the three shipped CI templates, so they cannot silently drift past the gate again after cab1a0e fixed them. The template guard uses an INVERTED existsSync direction vs sections 3c/3d: it fires in the maintainer repo (where the templates exist) and no-ops on consumers (who receive the rendered .github/workflows/ci.yml, not templates/). Also add an HONEST brownfield fix hint to every zizmor failure via a new optional requireTool(failHint) param. The hint spells out that `zizmor --fix=all` auto-fixes only artipacked + template-injection and that unpinned-uses is NOT auto-fixable (needs SHA-pinning) — a "just run --fix" hint would mislead the consumer. Prior-art: skipped — extend existing zizmor gate to shipped templates + brownfield fix hint, no new capability * fix(install): guarantee tsx resolves from workspace root for the pre-push TS hook (#636) (#640) GH #636 fix (a) — the install-time half, complementing #638's runtime degradation half. The pre-push dispatcher runs `node --import tsx/esm <root>/packages/core/hooks/pre-push.ts` from the repo ROOT, so tsx must resolve THERE. tsx is in CORE_DEVDEPS, but on a pnpm monorepo a tsx living in a sub-package is NOT hoisted to the root, so the TS hook silently degrades to the bash fallback (#638 made that degradation graceful instead of a crash). This closes the gap at install time. New install.sh step (§8b, after the §8 dep-install, before §6b-bis-L2 R2 wiring — so on --full the bulk install has already landed tsx via -w and the probe is a no-op, and tsx is ready before R2 wiring needs it). Reuses the #638 dispatcher probe expression and the existing detect_pm SSOT: tsx already resolvable from root -> nothing tsx missing, --full -> install at the workspace root, silently tsx missing, interactive (tty) -> offer [y/N]; on yes install, EVEN without --full tsx missing, refused / non-tty -> WARN: pre-push runs REDUCED (critical-only); print the exact PM-aware enabling command tsx ONLY (the hook runtime) — ts-morph / R2 untouched. Idempotent: the probe short-circuits when tsx already resolves. PM-aware command is built once as the SSOT for both the install and the warn (no #two-prompts-drift). No new dependency (tsx already in CORE_DEVDEPS). Test: tests/install-sh/gh-636-ensure-tsx-root.test.sh (fake PM on PATH, real node probe, python pty for the interactive arm; NO network). Covers --full silent install, interactive-yes (no --full), non-interactive WARN, PM/workspace command derivation, and a load-bearing paired-negative (tsx present -> no warn/install). Wired into audit-self.yml (meta-all-wired gate). Closes #636. * fix(tests): portable in-place sed in c1-wiring (BSD/GNU sed parity) (#641) GNU-style `sed -i 's/…/…/' file` errors on BSD sed (macOS): the substitute expression is consumed as the backup-suffix arg, the file path is parsed as a sed command, the command fails, and the file is never mutated. This made the C1-548-neg paired-negative arm VACUOUS on macOS (spurious FAIL=1) while Linux CI (GNU sed) stayed green and hid it. Replace with a POSIX temp-file + mv in-place edit — identical behaviour on both seds, no OS branching. Verified: macOS PASS=12 FAIL=0 (was 11/1). Test-only; install.sh and deps-hash-check.sh behaviour untouched. * fix(install): resolve ts-morph from consumer cwd in R2 Layer-2 wirer (#642) Resolve ts-morph from the consumer cwd (createRequire + pathToFileURL) in the R2 Layer-2 wirer; declare ts-morph@^24.0.0 as a packages/core devDep (matches setup.sh:336 / SSOT #131, fixes the latent undeclared audit-r4.ts:11 import); add fail-first Fixture X (in-pattern with f16/f17). Prior-art: prior-art-evaluations.md#131 (ts-morph REUSE — declaring an already-imported engine; SSOT pre-cleared, no new capability introduced) Closes #642 * docs(orchestrator): preserve #642 R2-wirer-fix kickoff (Phase -1 audit trail) (#645) Audit-trail for the #642 fix (shipped via #643, merged to staging as e1bb9c4): the orchestration kickoff capturing the 3-round Phase -1 cold-review reasoning (B->A CI-strategy flip away from a runtime-registry-install anti-pattern; the ts-morph ^24 vs npm-latest-28 version-drift catch) and the TDD cross-checkout fixture design. The fix itself already landed; this preserves the design record per the project's kickoff-tracking convention. Passes principle-12 citation (ai-laziness-traps + T-642-A/B/C domain traps). Prior-art: skipped — docs only (orchestration kickoff audit-trail under .claude/orchestrator-prompts/, not under packages/, no dependency), no new capability * docs(#646): multi-stack hybrid design + react-spa R-phase (#648) * docs(specs): multi-stack hybrid design (epic #646) Records the hybrid decision resolving the Stage-1 preset-vs-principle fork surfaced by the reviewer pass on #646: Stage 1 curated react/react-native presets now + Stage 2 generate-path as committed next umbrella, with a binding sequencing-commitment (phase-5 v2-trigger research-patch at Stage 1 close) so the bridge cannot strand 3 hand-maintained presets. Prior-art: skipped — design spec doc, no new capability (brainstorm output for epic #646). * docs(research): react-spa rule prior-art R-phase + SSOT #136-141 (#646 Stage 1) R-phase verdicts gating react-spa capability commits: A1-A3 (eslint-plugin-react / react-hooks v6 / jsx-a11y) ADOPT (own-stack backfill); B (no-business-logic) ADAPT not BUILD (boundary REUSE); C (require-error-boundary) T16 split BUILD-presence + ADOPT-usage. Primary-plugin fork resolved to eslint-plugin-react (dual-impl discipline); @eslint-react WATCHLIST (#141). DeepWiki down → WebSearch-grounded, re-probe flagged before B/C commits. Prior-art: skipped — R-phase research-patch + SSOT register entries, no new capability (these verdicts gate future capability commits per build-first-reuse-default §3). * chore(#646): re-trigger CI on corrected PR-body §1.7 headings No content change — forces a fresh SHA so the stale failed §1.7 check-run (from a re-run on the pre-fix PR body) detaches from PR head. Prior-art: skipped — empty re-trigger commit, no capability, no file change. * docs(research): react-native rule prior-art R-phase + SSOT #142-148 (#646 Stage 1) (#650) * docs(research): react-native rule prior-art R-phase + SSOT #142-148 (#646 Stage 1) R-phase verdicts for the react-native preset (epic #646 Stage 1, sibling of the react-spa R-phase in #648). All three issue BUILD hypotheses resolve to ADAPT/ADOPT/REJECT — zero clean BUILDs survive the prior-art consult: - no-web-only-globals -> ADAPT (REUSE no-restricted-globals + eslint-config-expo globals; BUILD-residue YAGNI) - require-stylesheet-create -> ADOPT (eslint-plugin-react-native/no-inline-styles) - prefer-flashlist-over-flatlist -> REJECT the BUILD (no upstream rule; perf-opinion not a correctness invariant; 3-way FlatList/FlashList/LegendList landscape) ADOPT layer: eslint-config-expo (Expo baseline; forbids web globals), @react-native + @callstack configs (bare-RN baseline), eslint-plugin-react-native (RN style rules, WATCHLIST low-maint), eslint-plugin-react-native-a11y (T16: RN a11y != jsx-a11y, supersedes #138 for RN). DeepWiki was UP (vs the react-spa sibling, where it was down) -> negative-existence claims are source-repo-verified. SSOT rows #142-148 continue after #648's #136-141. Depends on #648 (design spec + #136-141, branch claude/hopeful-clarke-6517e0, OPEN). Merge #648 first; the prior-art-evaluations.md tail then reorders to Prior-art: skipped -- R-phase research-patch; records ADOPT/ADAPT/REJECT verdicts + SSOT rows, builds no capability artifact. * docs(research): flag eslint-config-expo web-globals claim for I-phase verify (#646 rn R-phase) Adds a §5 open-item: the eslint-config-expo window:false / globals.browser-off claim (candidate B / SSOT #146) is DeepWiki-sourced, not file-verified in this R-phase; the core no-restricted-globals denylist is the independent fallback. (Lands on a fresh SHA -> clears the stale §1.7 check run frozen against the pre-rebase PR body; the gate snapshots github.event.pull_request.body at opened-event time, so the original opened-run stays red on the old SHA.) Prior-art: skipped -- R-phase research-patch addendum; records a verify-item, builds no capability. * docs(ssot): #146 — surface expo-globals verify caveat (consistency with R-phase §5) SSOT row #146 (no-web-only-globals / ADAPT) now carries the same I-phase verify-item just added to the research-patch §5: the eslint-config-expo window:false claim is DeepWiki-sourced, not file-verified; no-restricted-globals is the independent fallback. Keeps the SSOT row honest about its unverified leg. (Touches prior-art-evaluations.md -> re-runs the full path-filtered CI suite, incl. the §1.7 gate, on a fresh SHA — the prior docs-only addendum skipped them.) Prior-art: skipped -- SSOT honesty edit; adds a verify caveat to an existing row, builds no capability. * docs(research): correct falsified web-globals claim in react-native R-phase SSOT #142/#146 (#646 Stage 1) (#651) The react-native R-phase (#650, merged) recorded that `eslint-config-expo` "sets window:false and forbids globals.browser web-only globals -> closes candidate B (no-web-only-globals) for free on the Expo path". That claim was DeepWiki-prose, flagged by the patch's own §5 open-item-4 as "not file-verified". File-verified now (the open-item's intended I-phase check): - gh expo/expo:packages/eslint-config-expo/flat/default.js:23,38 — the globals block SPREADS `...globals.browser` and sets `window: false`, ships no `no-restricted-globals`. - ESLint globals docs — `false` ≡ "readonly" ≡ a DEFINED/readable global; only `"off"` disables. So `window: false` whitelists, it does not forbid. => The config WHITELISTS web-only globals (the opposite of forbidding) and does NOT close candidate B. This is the same `whitelists ≠ forbids` error the patch already caught for #144's `eslint-plugin-react-native` env claim — latent in the expo claim only because it was never file-verified. DeepWiki even returned two opposite answers on the semantics across probes (reliable for source contents, not for ESLint semantics — §1.10 spec-over-prose). Verdicts unchanged: ADOPT #142, ADAPT #146. Corrected the load-bearing consequence — the Expo baseline does NOT close candidate B; core `no-restricted-globals` is the PRIMARY forbid mechanism, not a fallback. Discharges react-native R-phase §5 open-item-4. Brings #142/#146 into agreement with the already-correct #144 row. Prior-art: skipped — docs-only correction of a falsified prior-art claim in #142/#146 (web-globals semantics: expo whitelists, not forbids); no new capability, no dep, no code. * fix(recovery): restore #642/#635/#636/#645 reverted by #648 staged-index contamination (#653) * fix(recovery): restore #642/#635/#636/#645 reverted by #648 staged-index contamination PR #648 (merge 691f44f) shipped 3 legit docs (multi-stack hybrid design + react-spa R-phase + SSOT), but its `--allow-empty` CI re-trigger sub-commit captured 16 foreign staged-WIP files, silently reverting merged fixes: - #642 — R2 wirer cwd-anchored ts-morph resolution (wire-eslint-r2.ts back to bare import) - #635 — hooks type:module refresh (install.sh do_refresh + pre-push.ts) - #636 — tsx-at-root probe (install.sh §8b) + DELETED both harness tests (gh-636-*.test.sh) - #645 — the #642 R2-wirer kickoff (deleted) plus package.json/lock dropping ts-morph, CI templates, husky hooks. No restorative commit followed → contamination live on staging. Restores all 16 contaminated files from dd05105 (parent of 691f44f, the pre-contamination state). #650/#651 touched none of the 16 (docs-only), so this is a clean reversal; the 3 legit docs from #648 and #650/#651 research are untouched. Mirrors the contamination exactly: +817 lines = the 817 it deleted. Verified: wire-eslint-r2.test.ts vitest passes; #642 createRequire, #636 tsx-at-root + both gh-636 tests, #645 kickoff all confirmed restored. Prior-art: skipped — recovery of an accidental revert (staged-index contamination), no new capability; the package.json ts-morph line is restoration of a pre-existing dependency removed by the contamination, not a new dependency. * ci(zizmor): pin zizmor==1.25.2 — unpinned install + 1.26.1 bump red-ed all PRs `pip install zizmor` (unpinned) installed 1.26.1, which tightened the adhoc-packages audit and surfaced 5 pre-existing LOW findings in our own self-CI workflows (audit-self.yml npm install --prefix / git init; framework-self-template-render.yml:35) that 1.25.2 did not flag. ci-success `needs: zizmor` → every fresh PR went red (recovery #653 included), with no code cause. Pin to the last known-green 1.25.2 for deterministic CI; a zizmor bump is now a deliberate edit, not a silent CI break. The 1.26.1 adhoc-packages findings (5 LOW, self-CI only — not shipped supply chain) are deferred to a separate triage (suppress-config vs fix), off the critical path here. Prior-art: skipped — CI tooling version pin (one-line determinism fix), no new capability, no new dependency. * fix(install): R2 Layer-2 wirer registers the plugin so the wired config loads (#644) (#647) * docs(spec): R2 Layer-2 wirer plugin-registration fix design (#644) * docs(plan): R2 Layer-2 wirer plugin-registration fix implementation plan (#644) * docs(plan): address Phase -1 cold-review — BLOCKER eslint resolve via package.json + 2 MAJOR + P2 assertion (#644) * feat(install): R2 wirer supports self-contained (plugin-registering) variant (#644) * feat(install): compute relative customRules import path for self-contained wire (#644) * feat(install): resolveAndWire orchestrates bare→self-contained via injected probe (#644) * feat(install): probeViaEslint default probe resolves eslint from consumer cwd (#644) * feat(install): apply-mode wiring goes through resolveAndWire probe loop (#644) * test(install): W3 loadability gate — wired config must load in ESLint (#644) P1 (plugin-less base) + P2 (plugin-registering base) run the wirer in apply mode and assert eslint --print-config LOADS (rc 0). Red-then-green verified: pre-fix wirer makes P1 fail with the exact 'could not find plugin' error on eslint 9.39.4 (confirms v9 parity, spec R1). _mk_consumer sets type:module (the .ts plugin-import precondition Layer-1 already needs) + installs ts-morph (wirer resolves it from cwd). Fixture X switched to --diff to isolate its ts-morph cross-checkout concern (#642) from the new apply-mode probe (#644). * fix(install): manual R2 snippets register the plugin (self-contained) (#644) * test(install): correct the false 'print-config in consumer-pipeline' comment (#644) * fix(install): load .ts eslint config on Node 20 via tsx loader (#644) The shipped eslint config (.mjs) imports the eslint-rules-local/index.ts barrel; on the shipped .nvmrc (Node 20, no native type-stripping) plain eslint fails with ERR_UNKNOWN_FILE_EXTENSION — breaking both the R2 wirer's self-contained escalation AND Layer-1's own lint. Preserve the consumer's Node choice (don't force a bump): run eslint through the tsx loader (already a CORE_DEVDEP + the pre-push-hook pattern; build-first-reuse). - probeViaEslint: node --import tsx (when tsx resolvable; else plain node, Node>=22.18). - consumer lint/lint:fix scripts (install.sh + setup.sh): NODE_OPTIONS=--import tsx. - W3 harness: _mk_consumer installs tsx; P1/P2 validate via the loader. Verified on simulated Node 20 (--no-experimental-strip-types): loader -> rc 0, plain -> ERR. * docs(#646): I-phase kickoffs for react-spa + react-native presets (Stage 1) (#655) Two parallel-dispatch I-phase kickoffs for the merged #646 Stage 1 R-phase (curated bridge). Each is self-contained for an autonomous aif worker. - react-spa: 3 ADOPT (eslint-plugin-react/hooks-v6/jsx-a11y/boundaries) + 1 BUILD (require-error-boundary presence-check, #140 — upstream covers usage not presence; AST-over-grep, narrow in-file scope per SSOT #115 precedent). - react-native: Expo-only (orchestrator scope decision 2026-06-22), 4 ADOPT + 1 ADAPT (no-restricted-globals denylist #146 — Expo baseline whitelists web globals per #651) + 1 REJECT (FlashList #148). Zero from-scratch BUILD = a build-first-reuse success per design-spec section 8. - Both fence off install.sh / packages/core/principles/* / prior-art-evaluations.md / preset-next-15-canonical (install+principle wiring is a separate follow-up task) -> no egress collision between the two parallel workers. - Phase -1 cold-reviewed (2x Opus, read-only): all load-bearing SSOT claims #136-148 + the #651 whitelist->denylist correction verified TRUE; MAJOR/MINOR findings folded in (peerDeps, package.json mirror, egress-guards, T-MS test cases). * docs(#646): revise react-native kickoff to ship BOTH Expo + bare-RN baselines (#656) Operator override (2026-06-22): Expo-only -> both baselines. The project goal is multi-stack + a factory for any stack (README#why-this-exists); covering the Expo AND bare-RN consumer up front serves that goal directly, weighted over the earlier YAGNI lean. - Ship eslint-config-expo/flat (#142) AND @react-native/eslint-config (#143) as two selectable baselines over a shared rn-common layer (style/a11y/no-restricted -globals denylist). Shipping #143 resolves the open DECISION-NEEDED the R-phase explicitly left to the operator (reviewer-discipline §2 path) — not a contradiction. - ZERO custom rules unchanged (build-first-reuse success); two-baseline = maximal REUSE. - Phase -1 cold-reviewed (1x Opus): GO, 0 BLOCKER/0 MAJOR; #143 ship-vs-swap consistency verified against R-phase + SSOT; egress fences still airtight; T-gate 4 distinct T-numbers. 1 MINOR folded in (denylist is a separate rule key, fires additively — not a key-collision to 'win'). * docs(654): kickoff for ci-adhoc-install-fix umbrella (option 2b) (#657) Dispatch input for aif autonomous execution of GH issue #654 option 2 (fix the 5 zizmor adhoc-packages findings via npm ci + re-pin zizmor 1.26.1 + internalise unpinned-CI-tool-install as a Class-A rule with an earliest-channel pre-push check). Kickoff passed Phase -1 (2x Opus round 1 caught 3 BLOCKER + 5 MAJOR; round 2 GO). Must reach staging before aif sees it (kickoff-staging-placement.md). * docs(#646): integration kickoff — install.sh wiring + principle 05/09 (Stage 1 Phase B) (#658) Single integration task owning the SHARED files both presets need: install.sh arg-parser/guard/menu/auto-detect/SHIPPED_DOCS/copy-blocks/dev-deps, plus principle 09 (REQUIRED_HEADER_DOCS + drift-test count 19->25) and principle 05 (manifest-rendered-vs-static, determined by grep on the render-rules provenance marker). - Binding dispatch dependency: ONLY after BOTH preset PRs merge to staging (copy-blocks reference real preset files; principle 09 registers real RULES.md/ARCHITECTURE.md). - Phase -1 cold-reviewed (1x Opus, REVISE->GO round 2): 3 MAJOR fixed (SPA eslint basename = eslint.config.react.mjs not -spa suffix; RN ships no playwright.config.ts; principle-05 rendered-vs-static via deterministic grep) + 4 MINOR (count->25 explicit, guard message, detect order RN-before-spa, don't-touch-react-next-blocks). M3 grep marker independently re-verified against next RULES.md:13. * docs(#646): stage-2-generate-path umbrella kickoff (Stage 2) (#659) * docs(#646): Stage 2 generate-path brainstorm spec Records the brainstorm feasibility framing for epic #646 Stage 2 (LLM generate-path). Corrects the Stage-1 design-doc claim that L4 does not exist (packages/core/validator/ ships gates 1/2/4/6, Phase 7); reframes Stage 2 as swap-curated-inputs-for-LLM at L2/L3 with L4/L5 unchanged. Lands reasoned recommendations on both strategic forks (entry point = L3-live-first vs existing L4; scope = Path A only) plus the mandatory build-vs-reuse gate (aif-evolve / OMC) before any live-LLM build. Status: proposed — pending operator ratification. Not a kickoff. Prior-art: skipped — docs-only brainstorm spec, no new capability (no dependency, no code under packages/). * docs(#646): stage-2-generate-path umbrella kickoff (Stage 2) Operationalises the 2026-06-22 brainstorm spec into a dispatch-ready umbrella kickoff for #646 Stage 2 (the LLM generate-path). Stage 0 = build-vs-reuse R-phase (aif-evolve / OMC) is BLOCKING — no L2/L3 code lands before its verdict. Reconstructed onto staging cleanly, without the cross-umbrella plugin commits that had tangled the original brainstorm branch. Prior-art: skipped — docs-only umbrella kickoff, no new capability (no dependency, no code under packages/). * style(#646): prettier-format stage-2 generate-path spec Hooks were bypassed on the original brainstorm commit (3da9578, core.hooksPath=/dev/null); this applies the prettier pass it missed — italic-emphasis normalisation (*x* → _x_) and table-column padding. No content change. Prior-art: skipped — prettier formatting only, no new capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * CC plugin packaging (S0–S1 + umbrella) — superpowers-style hybrid (#660) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * docs(plugin): REVISE round after Phase -1 cold-review — dispatch-ready kickoff Phase -1 adversarial cold-review (CLAUDE.md meta-orchestrator self-review obligation) returned REVISE; one round applied: - S0 SSOT entries #149-152 (superpowers ADOPT, run-hook ADOPT-audited, using-* ADAPT, CC-docs REFERENCE) - closes the S1 trailer forward-reference (B1). - kickoff records current state: S0+S1 DONE (63b344c, fa37b2b), dispatch starts S2 (B1/B2). - hook count 15 -> 14, all 14 enumerated in plan Task 2 (B3). - compliance-verifier: stop claiming 'per extension.json'; S4 adds it (B4). - principle 21 -> full filenames (M1); dispatcher/pipeline paths (M3); tool-bootstrapping OUT of v1 (m1). Prior-art: prior-art-evaluations.md#149-152 (S0 SSOT entries; superpowers plugin/run-hook ADOPT, using-superpowers ADAPT, CC docs REFERENCE). * fix(plugin): restore 654 kickoff erroneously deleted by S0 commit Commit 63b344c (S0 spec+plan+kickoff) deleted .claude/orchestrator-prompts/654-ci-adhoc-install-fix/kickoff.md — a foreign file of the active 654 ci-adhoc-install-fix umbrella (present on origin/staging, blob c9019eb). Likely a stale staged-index artifact at branch-creation time. Restored byte-for-byte from origin/staging so this PR does not drive-by-delete another umbrella's kickoff. No plugin-packaging content affected. Prior-art: skipped — doc restoration, no new capability or dependency. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(preset-react-spa): React-19 SPA preset (#646 Stage 1) (#661) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * feat(preset-react-spa): React-19 SPA preset — 3 ADOPT + require-error-boundary (#646 Stage 1) Built autonomously by aif task 547d9114; harvested selectively (preset dir only — excluded base-contamination: hook-mods, foreign kickoff deletions, root lock). - 3 ADOPT: eslint-plugin-react (#136), react-hooks v6 (#137), jsx-a11y (#138), eslint-plugin-boundaries (#139) — wired in self-contained eslint.config.react.mjs (no @next). - 1 BUILD: require-error-boundary (#140) — real AST rule (ESLintUtils.RuleCreator + AST_NODE_TYPES JSX visitor, not string-grep) + paired valid/invalid test (principle 02). - verify-before-harvest: 14 preset files present, out-of-scope clean (no install.sh/ principles/prior-art edits), AST + paired-test sanity passed. - install.sh wiring + principle 05/09 registration = separate integration task (#658). Prior-art: prior-art-evaluations.md#136/#137/#138/#139 (ADOPT upstream react lint plugins) + #140 (BUILD require-error-boundary, presence-gap confirmed, no upstream analog) + #141 (WATCHLIST @eslint-react). SSOT rows recorded in merged R-phase #648. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(preset-react-native): Expo + bare-RN preset, both baselines (#646 Stage 1) (#662) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * feat(preset-react-native): Expo + bare-RN preset, both baselines (#646 Stage 1) Built autonomously by aif task 808f858a (rebuilt for both-baselines per operator override); harvested selectively (preset dir only, excluding base-contamination + preset-local lock). - BOTH baselines: eslint-config-expo/flat (#142, Expo) + @react-native/eslint-config (#143, bare-RN) over a shared eslint.config.rn-common.mjs layer. - ADOPT plugins: eslint-plugin-react-native (#144/#147), react-native-a11y (#145). - ADAPT: no-restricted-globals denylist (#146) — present in all three configs (forbids web globals the Expo baseline whitelists, per #651 correction). - REJECT: FlashList custom rule (#148). ZERO eslint-rules/ (build-first-reuse success). - No playwright.config.ts (RN web-only, correctly omitted). - verify-before-harvest: both baseline configs + rn-common + denylist present; eslint-rules absent as designed; out-of-scope clean. - install.sh wiring + principle 05/09 = separate integration task (#658). Prior-art: prior-art-evaluations.md#142/#143 (ADOPT Expo + bare-RN baselines) + #144/#145/#147 (ADOPT react-native lint plugins) + #146 (ADAPT no-restricted-globals denylist) + #148 (REJECT FlashList BUILD). SSOT rows recorded in merged R-phase #650/#651. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S2 — relocate shippable hooks (per-hook path-class audit) (#664) * feat(plugin): relocate shippable hooks to plugin payload (path-class audit) (S2) Per-hook triage of all 14 .claude/hooks/*.sh (docs/superpowers/plans/plugin-hook-triage.md): SHIP=2, INTERNAL=12. Each hook's self-declared @dual-pair/@cc-only-rationale marker is the primary evidence. Relocate inject-matching-rule into plugin/hooks/ as an extensionless, context-agnostic script: every path it touches is project-data, resolved via ${CLAUDE_PROJECT_DIR} (T-PLUG-A — no blanket env swap; a swap to ${CLAUDE_PLUGIN_ROOT} would make it read the plugin's non-existent rules instead of the consumer's). hooks.json wires PostToolUse→inject-matching-rule via run-hook.cmd; SessionStart→session-start deferred to S3. inject-session-bootstrap→session-start, also S3. check-doc-authority stays INTERNAL (needs the framework TS toolchain); deps-hash-check already ships via install.sh. Verified: tests/plugin/hook-paths.test.sh 6/6; throwaway-consumer smoke proves the hook fires on a matching glob, reads the CONSUMER's .claude/rules via CLAUDE_PROJECT_DIR, and no-ops on non-match. Prior-art: skipped — relocation of an own hook into the plugin payload, no new external dependency or capability. * test(plugin): harden hook-paths assertion (d) vs T-PLUG-A inverse mis-rooting (S2 cold-review) Phase -1 cold-review CONCERN: assertion (d) only checked that CLAUDE_PROJECT_DIR appears somewhere in the file, so a hook rooting consumer .claude/rules at ${CLAUDE_PLUGIN_ROOT} while mentioning CLAUDE_PROJECT_DIR elsewhere would falsely pass. Added a negative arm rejecting CLAUDE_PLUGIN_ROOT-rooted .claude/rules. Verified: real hook still 6/6; a constructed inverse-bug fixture now FAILs; clean after removal. Prior-art: skipped — test hardening only, no new external dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * fix(654): zizmor adhoc-packages findings + ci-tool-pinning rule (option 2b) (#663) Fix the 5 zizmor adhoc-packages findings via npm ci + re-pin zizmor 1.26.1 + internalise unpinned-CI-tool-install as Class-A ci-tool-pinning.md (pre-push gate + paired-negative test, principle-09 registered). aif autonomous task e8528798 + orchestrator cold-QA rework (B1 SSOT #149->#153 staging-collision, M1 narrow Rule B). Prior-art: prior-art-evaluations.md#153 (zizmor adhoc-packages REFERENCE + bare-run tool-pin BUILD; HYBRID). §1.7: forward — no-paid-llm-in-ci (deterministic scan), build-first-reuse (REUSE zizmor + BUILD bare-run slice), doc-authority (Class-A + principle-09). backward — codifies 2026-06-22 unpinned-zizmor incident (audit-self.yml:741). See packages/core/hooks/checks/unpinned-tool-install.ts:1. * feat(plugin): using-rules-as-tests bootstrap + shippable skills (S3) (#665) Author plugin/skills/using-rules-as-tests/SKILL.md (ADAPT of superpowers using-superpowers — their mechanism: invoke-skills-before-responding + the instruction-priority ladder; our content: the rules-as-tests skill set + the soft/hard honesty boundary). Add plugin/hooks/session-start (extensionless, @cc-only-rationale, self-resolves dir, ${CLAUDE_PLUGIN_ROOT}) that injects the bootstrap so the skill auto-triggers without a manual Skill invocation. Copy the consumer-facing rules-as-tests skill (SKILL.md + 5 references) into plugin/skills/. Wire SessionStart→session-start in hooks.json. v1 ships only rules-as-tests + the bootstrap (per extension.json); tool-bootstrapping deferred. Verified: tests/plugin/bootstrap.test.sh 10/10 (session-start emits the bootstrap naming the skill, the CLAUDE.md/AGENTS.md instruction ladder, and the /install-enforcement boundary; both shipped skills carry valid frontmatter); hook-paths 10/10 (session-start extensionless+marked+wired); principle 09 + 22 green. Prior-art: prior-art-evaluations.md#151 (superpowers using-superpowers — ADAPT: their SessionStart-bootstrap mechanism, our content). Co-authored-by: Art <devartyhoo@gmail.com> * chore(654): umbrella done.md (closure convention) (#666) Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S4 — ship consumer-facing agents + principle 21 plugin-tree arm (#667) * feat(plugin): ship consumer-facing agent subset + principle 21 plugin-tree arm (S4) Triage (T-PLUG-B): SHIP review-sidecar, living-docs-auditor, compliance-verifier (consumer-facing per extension.json + the Artifact Ownership Contract); INTERNAL aif-init, manual-rule-liveness-prober, memory-codification-auditor, orchestrator-worker-discipline, shipped-agent-liveness-prober. Copy the 3 SHIP agents verbatim into plugin/agents/ (byte-identical to the agents/ source they mirror). Register compliance-verifier in extension.json. Extend principle 21 with collectPluginAgentFiles() + arm (h) so the plugin's shipped agents are guarded by the same #551 tool-name-validity gate as agents/*.md (non-vacuity floor ≥3; the formal manifest-integrity self-test lands in S6). Verified: principle 21 10/10 (arm (h) red with 0 agents → green after copy); principle 09 green (agents carry their REQUIRED_HEADER_DOCS doc-authority headers); plugin bash suite 4/4. Prior-art: skipped — test arm extension for an existing capability + verbatim relocation of own agents; no new external dependency. * test(plugin): relabel plugin-tree arm (h)→(j) to de-dup the label (S4 cold-review nit) Prior-art: skipped — cosmetic test-label fix, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * docs(stage-2-generate-path): Stage 0 close — land prior-art research-patch + SSOT #154–#168 (#668) Make the Stage-0 reuse-gate research-patch visible to dispatch (which reads from staging): the patch was untracked on a feature branch, so /pipeline + aif dispatch could not see it. - Add the research-patch under docs/meta-factory/research-patches/ (was untracked). - Append 15 SSOT entries (#154–#168) for candidates 3–17 (ESLint RuleTester, Semgrep, OpenRewrite, GritQL, config-inspector, antfu/Sheriff, Biome/oxlint, OPA/Checkov, SonarQube, CEGIS, Reflexion, compiler self-hosting, Renovate, Nx); cross-link #1/#50/#66/#105 (Last-reviewed bumped, verdicts unchanged). - Mark Stage 0 discharged in the kickoff + acceptance criterion met. Verdict: /aif-evolve = REFERENCE -> the L3-live synthesizer stays BUILD; Oh My ClaudeCode = REJECT. Landing-time CI-compliance fixes on the as-authored patch: add the principle-10 <!-- scope:... --> first-line annotation; collapse compound verdict cells to single principle-11-recognized tokens (dual nature preserved in each rationale). All 25 principle suites green (219 tests). Prior-art: skipped — Stage-0 R-phase research-patch + SSOT append (#154-#168) + kickoff discharge; doc-only, no new capability or dependency. Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): /install-enforcement fetch-and-wire seam (S5, Option C) (#669) Hybrid seam reaching the HARD enforcement layer (git hooks + CI). Option C (maintainer decision 2026-06-22, overrides spec §6 bundle): rather than bundle ~2MB of install.sh's payload into plugin/install/ (which would duplicate the plugin's own skills/agents + carry the not-shipped dispatcher/pipeline + risk repo-wide CI scans), the thin plugin/install/fetch-and-wire.sh FETCHES the project's own official install.sh (pinned to plugin version; RAT_INSTALL_SOURCE override for forks/offline/tests) and runs it verbatim against $CLAUDE_PROJECT_DIR. Default dry-run (writes nothing); --apply does the real run. commands/install-enforcement.md orchestrates consent (dry-run → show → [y/N] → --apply); installing-enforcement/SKILL.md documents WHEN to wire + the honest soft/hard boundary (T16). The thin-reimplemented-wirer alternative was rejected (would discard install.sh's battle-hardened wiring, fixes #532/#533/#635). Spec §6 + plan Task 5 updated to reflect the decision; plan S6 step updated (no bundled install.sh to hash → assert fetch-and-wire version pin === plugin.json). Verified offline (RAT_INSTALL_SOURCE=repo root): tests/plugin/install-seam.test.sh 9/9 — dry-run writes nothing+exits 0, --apply creates .husky/pre-commit+pre-push in a throwaway consumer, unknown flag exits 2; full plugin suite 5/5; principle 09 green. Prior-art: skipped — thin seam under plugin/ (no new dependency, no packages/ code); reuses the project's own install.sh verbatim per companion-install-principle.md (install via the official top-level installer). Co-authored-by: Art <devartyhoo@gmail.com> * test(plugin): S6 — principle 24 plugin-manifest integrity + paired-negative (T15) (#670) * test(plugin): principle 24 — plugin manifest integrity + paired-negative (S6) Recursive self-test (T15): the packaging enforces its own integrity. checkPluginIntegrity() is a pure function (plugin dir + manifest dir → violations) run on BOTH the real tree (arm a, 0 violations) and a deliberately-broken fixture (arm b — tests/fixtures/plugin-broken-manifest trips V2 version-drift + V3 header-less skill + V6 dangling hook target), the principle-02 paired-negative discipline that makes the gate non-tautological. Plus real-tree arms: version pin (fetch-and-wire RAT_PLUGIN_VERSION === plugin.json.version), drift guards (plugin/agents/*.md byte-identical to agents/ source; plugin/hooks/inject-matching-rule shares the source's @dual-pair anchor + glob_match core), and T15 self-application. Added Authoritative-for headers to the two new plugin skills so V3 passes. plugin doc-authority is owned HERE (V3 for skills; the byte-identical drift guard covers plugin/agents headers transitively via principle-09-enforced agents/ sources) rather than by extending principle 09 — closes the S4 cold-review gap without touching the shared principle. Verified: principle 24 6/6 (real-tree green, fixture red-detected); full principle suite 26 files / 226 tests; plugin bash suite 5/5. Prior-art: skipped — test addition (principle self-test) for the plugin-packaging capability already SSOT'd at #149-152; no new dependency or external capability. * test(plugin): harden principle 24 arm (e) — byte-identical core drift guard (S6 cold-review) Cold-review CONCERN: arm (e) only asserted @dual-pair + glob_match() string presence, so a regression INSIDE glob_match's body would not be caught. Now extracts the matcher+injection core (from glob_match() to EOF — the only legit divergence is the relocation above it) and asserts byte-equality between plugin/hooks/inject-matching-rule and the source. Verified: real tree green; a mutation inside the glob_match region now goes RED. Prior-art: skipped — test hardening only, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): OpenCode degradation adapter (S7) (#671) .opencode/INSTALL.md — fetch-and-follow adapter pointing OpenCode at the shared plugin/skills/ body (via opencode.json skills.paths). Documents the ONE accepted off-CC degradation: SessionStart auto-injection of the using-rules-as-tests bootstrap does not fire off-CC → read it on demand (a documented degradation per dual-implementation-discipline §3, NOT a portability gap — the skill content is fully portable). Tool mapping (CC Skill→OpenCode skill tool, Task→@mention, etc.); the hard layer routes through the portable plugin/install/fetch-and-wire.sh seam (same honest soft/hard boundary). extension.json gains a $plugin-packaging note reconciling it with the plugin payload (source paths kept — install.sh + principle 21 read them; not repointed). AGENTS.md cross-harness layer is referenced from the adapter, not duplicated. Verified: tests/plugin/opencode-adapter.test.sh 7/7; full plugin bash suite 6/6; markdownlint 0. Prior-art: skipped — docs + a small acceptance test, no new dependency or capability (ADAPT of superpowers' .opencode fetch-and-follow pattern, SSOT-adjacent). Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S8 — README per-harness install + fetch ref → main (umbrella finale) (#672) * feat(plugin): README per-harness install + fetch ref → main (S8) README 'As a Claude Code plugin (per-harness)' section: the marketplace-add/install flow, skills auto-trigger, the honest soft/hard boundary (T16 — the plugin never wires git/CI; /rules-as-tests:install-enforcement does, opt-in), the OpenCode path + accepted degradation, and plugin-vs-./setup. §Why this exists untouched (read-only per the Artifact Ownership Contract). fetch-and-wire.sh: decouple RAT_INSTALL_REF from the plugin version → default 'main'. Evidence: the framework's latest release tag v0.3.0 is 2026-05-06, staging is 72 files / +17551 ahead in install.sh + payload (incl. the hardening fixes #531/#551/#635/#636 the S5 cold-review valued); origin/main (2026-06-18) carries the current installer (only 65 lines behind staging). Pinning to v<plugin-version> would ship a 6-week-stale install.sh. Plugin keeps its own 0.1.0 version line; a reproducible per-plugin release tag is a future maintainer action. Spec §6 updated. Verified: composite e2e on a throwaway consumer — SessionStart bootstrap fires, inject-matching-rule reads consumer rules, /install-enforcement seam dry-run writes nothing + --apply wires .husky pre-commit/pre-push + CI, manifest version-parity True; all 6 plugin bash tests + principle 21/24/09 (39) green; README markdownlint 0. Prior-art: skipped — docs + a config-default change under plugin/, no new dependency or capability. * docs(plugin): umbrella closure — plugin-packaging done.md (S8) CLAUDE.md Umbrella closure schema: Final PR #672, all S0–S8 merged to staging. Records the stage-PR map + 3 maintainer-owned residuals (live /plugin install unproven-from-automation, install.sh-vs-plugin agent divergence, reproducible-tag option). Prior-art: skipped — closure doc, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * test(validator): L4 adversarial hardening — tautology/never-firing/conflict fixtures (paired-negative) (#674) Adds three adversarial SynthesisPlan fixture pairs (bad + valid) that exercise the exact reject surfaces of Gates 2, 4, and 6: - gate-tautology.adversarial.test.ts (T1): no-restricted-imports forbidding 'react' fires on negative-corpus/unrelated.tsx (Gate 4 fail); paired with a lodash-forbidding rule that does NOT fire on the corpus (Gate 4 pass). - gate-rule-tester.adversarial.test.ts (T2): comment-only input that never triggers no-restricted-imports (Gate 2 fail); paired with a lodash-import input that correctly fires (Gate 2 pass). - snapshot.adversarial.test.ts + expected-adv-*.json (T4): full validate() aggregate snapshots for each bad fixture confirm ok:false and the right gate routes to 'fail'. Separate from existing self/next-16 snapshots. T3 (Gate 6 conflict): adversarial orphan-ref + snippet-drop cases are already present in gate-conflict.test.ts:52-99; no new file needed. Gate 6 snippet-drop is additionally covered via snapshot.adversarial.test.ts using a no-debugger rule absent from eslintConfigSnippet: '{}' (clean Gate 2 + 4 pass, Gate 6 fail). Gate source files (gate-*.ts, validate.ts) are UNCHANGED — hardening adds tests/fixtures only. validator suite: 45 tests, all pass. Prior-art: prior-art-evaluations.md#154–#168 (Stage-0 SSOT for L4 generate-path hardening — adversarial corpus design patterns, no upstream production-grade paired-negative harness for ESLint-rule validation existed at SSOT survey time). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deps): unpin revoked eslint 10.4.1 → ^10.4.0 + sync root lockfile (preset-react-native/spa) (#676) eslint 10.4.1 was unpublished from the npm registry (version-API → 404; tarball still served by the CDN), so any root-level `npm install` / `npm ci` / `--package-lock-only` failed with ETARGET, leaving the root lockfile unable to regenerate. Consequently the new workspace packages preset-react-native (#646) and preset-react-spa (#661) were never added to package-lock.json. guard-liveness-fullsweep (pull_request → main only) is the one required check that runs `npm ci` on the root workspace, so the drift surfaced only on promote PR #673 — red on "Missing: @rules-as-tests/preset-react-native from lock file". Fix: pin packages/core eslint to ^10.4.0 (latest existing in the 10.4 line; API stable vs 10.4.1) and regenerate the root lockfile. Verified locally: npm ci rc=0; guard-liveness:fullsweep GREEN (v1 ESLint 8/0, v1.5 cmd 3/0, v3 structural pass); lockfileVersion stays 3 (node20/npm10 CI-compatible); linux-x64 bindings present; package count 458→770 (preset deps). Prior-art: skipped — dependency version-pin fix (revoked eslint 10.4.1 → existing 10.4.0) plus mechanical lockfile sync; no new capability introduced. Co-authored-by: Art <devartyhoo@gmail.com> --------- Co-authored-by: Art <devartyhoo@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
artyhoo
added a commit
that referenced
this pull request
Jun 22, 2026
) * fix(hooks): pre-push delivery completeness on pnpm monorepo (#635, #636) (#638) * fix(hooks): probe tsx runtime in pre-push dispatcher, degrade to bash fallback instead of crash (#636) * fix(install): refresh ships hooks/package.json type:module marker in do_refresh (#635) * fix(templates): shipped CI templates pass the framework's own zizmor gate + regression guard + honest brownfield hint (#637) (#639) * fix(templates): harden shipped CI workflows to pass zizmor (artipacked + unpinned-uses + template-injection) (#637) Both shipped CI templates failed the framework's own zizmor pre-push gate, blocking a fresh consumer on the framework's own scaffolding. Apply three hardening transforms to each: - artipacked: persist-credentials: false on every actions/checkout step - unpinned-uses: SHA-pin every action uses: (KEEP the # vX.Y.Z comment) - template-injection: use runner GITHUB_BASE_REF env in the Stryker run-block instead of interpolating ${{ github.base_ref }} into shell The actions/cache key: interpolation of github.base_ref is left unchanged (not a shell context — zizmor only flags the run:-block one). Prior-art: skipped — shipped-template security hardening (persist-credentials + SHA-pin + injection fix), no new capability * fix(pre-push): guard shipped CI templates against zizmor drift + honest brownfield fix hint (#637) Extend the pre-push zizmor gate (and the audit-self.yml CI backstop) to also scan the three shipped CI templates, so they cannot silently drift past the gate again after cab1a0e fixed them. The template guard uses an INVERTED existsSync direction vs sections 3c/3d: it fires in the maintainer repo (where the templates exist) and no-ops on consumers (who receive the rendered .github/workflows/ci.yml, not templates/). Also add an HONEST brownfield fix hint to every zizmor failure via a new optional requireTool(failHint) param. The hint spells out that `zizmor --fix=all` auto-fixes only artipacked + template-injection and that unpinned-uses is NOT auto-fixable (needs SHA-pinning) — a "just run --fix" hint would mislead the consumer. Prior-art: skipped — extend existing zizmor gate to shipped templates + brownfield fix hint, no new capability * fix(install): guarantee tsx resolves from workspace root for the pre-push TS hook (#636) (#640) GH #636 fix (a) — the install-time half, complementing #638's runtime degradation half. The pre-push dispatcher runs `node --import tsx/esm <root>/packages/core/hooks/pre-push.ts` from the repo ROOT, so tsx must resolve THERE. tsx is in CORE_DEVDEPS, but on a pnpm monorepo a tsx living in a sub-package is NOT hoisted to the root, so the TS hook silently degrades to the bash fallback (#638 made that degradation graceful instead of a crash). This closes the gap at install time. New install.sh step (§8b, after the §8 dep-install, before §6b-bis-L2 R2 wiring — so on --full the bulk install has already landed tsx via -w and the probe is a no-op, and tsx is ready before R2 wiring needs it). Reuses the #638 dispatcher probe expression and the existing detect_pm SSOT: tsx already resolvable from root -> nothing tsx missing, --full -> install at the workspace root, silently tsx missing, interactive (tty) -> offer [y/N]; on yes install, EVEN without --full tsx missing, refused / non-tty -> WARN: pre-push runs REDUCED (critical-only); print the exact PM-aware enabling command tsx ONLY (the hook runtime) — ts-morph / R2 untouched. Idempotent: the probe short-circuits when tsx already resolves. PM-aware command is built once as the SSOT for both the install and the warn (no #two-prompts-drift). No new dependency (tsx already in CORE_DEVDEPS). Test: tests/install-sh/gh-636-ensure-tsx-root.test.sh (fake PM on PATH, real node probe, python pty for the interactive arm; NO network). Covers --full silent install, interactive-yes (no --full), non-interactive WARN, PM/workspace command derivation, and a load-bearing paired-negative (tsx present -> no warn/install). Wired into audit-self.yml (meta-all-wired gate). Closes #636. * fix(tests): portable in-place sed in c1-wiring (BSD/GNU sed parity) (#641) GNU-style `sed -i 's/…/…/' file` errors on BSD sed (macOS): the substitute expression is consumed as the backup-suffix arg, the file path is parsed as a sed command, the command fails, and the file is never mutated. This made the C1-548-neg paired-negative arm VACUOUS on macOS (spurious FAIL=1) while Linux CI (GNU sed) stayed green and hid it. Replace with a POSIX temp-file + mv in-place edit — identical behaviour on both seds, no OS branching. Verified: macOS PASS=12 FAIL=0 (was 11/1). Test-only; install.sh and deps-hash-check.sh behaviour untouched. * fix(install): resolve ts-morph from consumer cwd in R2 Layer-2 wirer (#642) Resolve ts-morph from the consumer cwd (createRequire + pathToFileURL) in the R2 Layer-2 wirer; declare ts-morph@^24.0.0 as a packages/core devDep (matches setup.sh:336 / SSOT #131, fixes the latent undeclared audit-r4.ts:11 import); add fail-first Fixture X (in-pattern with f16/f17). Prior-art: prior-art-evaluations.md#131 (ts-morph REUSE — declaring an already-imported engine; SSOT pre-cleared, no new capability introduced) Closes #642 * docs(orchestrator): preserve #642 R2-wirer-fix kickoff (Phase -1 audit trail) (#645) Audit-trail for the #642 fix (shipped via #643, merged to staging as e1bb9c4): the orchestration kickoff capturing the 3-round Phase -1 cold-review reasoning (B->A CI-strategy flip away from a runtime-registry-install anti-pattern; the ts-morph ^24 vs npm-latest-28 version-drift catch) and the TDD cross-checkout fixture design. The fix itself already landed; this preserves the design record per the project's kickoff-tracking convention. Passes principle-12 citation (ai-laziness-traps + T-642-A/B/C domain traps). Prior-art: skipped — docs only (orchestration kickoff audit-trail under .claude/orchestrator-prompts/, not under packages/, no dependency), no new capability * docs(#646): multi-stack hybrid design + react-spa R-phase (#648) * docs(specs): multi-stack hybrid design (epic #646) Records the hybrid decision resolving the Stage-1 preset-vs-principle fork surfaced by the reviewer pass on #646: Stage 1 curated react/react-native presets now + Stage 2 generate-path as committed next umbrella, with a binding sequencing-commitment (phase-5 v2-trigger research-patch at Stage 1 close) so the bridge cannot strand 3 hand-maintained presets. Prior-art: skipped — design spec doc, no new capability (brainstorm output for epic #646). * docs(research): react-spa rule prior-art R-phase + SSOT #136-141 (#646 Stage 1) R-phase verdicts gating react-spa capability commits: A1-A3 (eslint-plugin-react / react-hooks v6 / jsx-a11y) ADOPT (own-stack backfill); B (no-business-logic) ADAPT not BUILD (boundary REUSE); C (require-error-boundary) T16 split BUILD-presence + ADOPT-usage. Primary-plugin fork resolved to eslint-plugin-react (dual-impl discipline); @eslint-react WATCHLIST (#141). DeepWiki down → WebSearch-grounded, re-probe flagged before B/C commits. Prior-art: skipped — R-phase research-patch + SSOT register entries, no new capability (these verdicts gate future capability commits per build-first-reuse-default §3). * chore(#646): re-trigger CI on corrected PR-body §1.7 headings No content change — forces a fresh SHA so the stale failed §1.7 check-run (from a re-run on the pre-fix PR body) detaches from PR head. Prior-art: skipped — empty re-trigger commit, no capability, no file change. * docs(research): react-native rule prior-art R-phase + SSOT #142-148 (#646 Stage 1) (#650) * docs(research): react-native rule prior-art R-phase + SSOT #142-148 (#646 Stage 1) R-phase verdicts for the react-native preset (epic #646 Stage 1, sibling of the react-spa R-phase in #648). All three issue BUILD hypotheses resolve to ADAPT/ADOPT/REJECT — zero clean BUILDs survive the prior-art consult: - no-web-only-globals -> ADAPT (REUSE no-restricted-globals + eslint-config-expo globals; BUILD-residue YAGNI) - require-stylesheet-create -> ADOPT (eslint-plugin-react-native/no-inline-styles) - prefer-flashlist-over-flatlist -> REJECT the BUILD (no upstream rule; perf-opinion not a correctness invariant; 3-way FlatList/FlashList/LegendList landscape) ADOPT layer: eslint-config-expo (Expo baseline; forbids web globals), @react-native + @callstack configs (bare-RN baseline), eslint-plugin-react-native (RN style rules, WATCHLIST low-maint), eslint-plugin-react-native-a11y (T16: RN a11y != jsx-a11y, supersedes #138 for RN). DeepWiki was UP (vs the react-spa sibling, where it was down) -> negative-existence claims are source-repo-verified. SSOT rows #142-148 continue after #648's #136-141. Depends on #648 (design spec + #136-141, branch claude/hopeful-clarke-6517e0, OPEN). Merge #648 first; the prior-art-evaluations.md tail then reorders to Prior-art: skipped -- R-phase research-patch; records ADOPT/ADAPT/REJECT verdicts + SSOT rows, builds no capability artifact. * docs(research): flag eslint-config-expo web-globals claim for I-phase verify (#646 rn R-phase) Adds a §5 open-item: the eslint-config-expo window:false / globals.browser-off claim (candidate B / SSOT #146) is DeepWiki-sourced, not file-verified in this R-phase; the core no-restricted-globals denylist is the independent fallback. (Lands on a fresh SHA -> clears the stale §1.7 check run frozen against the pre-rebase PR body; the gate snapshots github.event.pull_request.body at opened-event time, so the original opened-run stays red on the old SHA.) Prior-art: skipped -- R-phase research-patch addendum; records a verify-item, builds no capability. * docs(ssot): #146 — surface expo-globals verify caveat (consistency with R-phase §5) SSOT row #146 (no-web-only-globals / ADAPT) now carries the same I-phase verify-item just added to the research-patch §5: the eslint-config-expo window:false claim is DeepWiki-sourced, not file-verified; no-restricted-globals is the independent fallback. Keeps the SSOT row honest about its unverified leg. (Touches prior-art-evaluations.md -> re-runs the full path-filtered CI suite, incl. the §1.7 gate, on a fresh SHA — the prior docs-only addendum skipped them.) Prior-art: skipped -- SSOT honesty edit; adds a verify caveat to an existing row, builds no capability. * docs(research): correct falsified web-globals claim in react-native R-phase SSOT #142/#146 (#646 Stage 1) (#651) The react-native R-phase (#650, merged) recorded that `eslint-config-expo` "sets window:false and forbids globals.browser web-only globals -> closes candidate B (no-web-only-globals) for free on the Expo path". That claim was DeepWiki-prose, flagged by the patch's own §5 open-item-4 as "not file-verified". File-verified now (the open-item's intended I-phase check): - gh expo/expo:packages/eslint-config-expo/flat/default.js:23,38 — the globals block SPREADS `...globals.browser` and sets `window: false`, ships no `no-restricted-globals`. - ESLint globals docs — `false` ≡ "readonly" ≡ a DEFINED/readable global; only `"off"` disables. So `window: false` whitelists, it does not forbid. => The config WHITELISTS web-only globals (the opposite of forbidding) and does NOT close candidate B. This is the same `whitelists ≠ forbids` error the patch already caught for #144's `eslint-plugin-react-native` env claim — latent in the expo claim only because it was never file-verified. DeepWiki even returned two opposite answers on the semantics across probes (reliable for source contents, not for ESLint semantics — §1.10 spec-over-prose). Verdicts unchanged: ADOPT #142, ADAPT #146. Corrected the load-bearing consequence — the Expo baseline does NOT close candidate B; core `no-restricted-globals` is the PRIMARY forbid mechanism, not a fallback. Discharges react-native R-phase §5 open-item-4. Brings #142/#146 into agreement with the already-correct #144 row. Prior-art: skipped — docs-only correction of a falsified prior-art claim in #142/#146 (web-globals semantics: expo whitelists, not forbids); no new capability, no dep, no code. * fix(recovery): restore #642/#635/#636/#645 reverted by #648 staged-index contamination (#653) * fix(recovery): restore #642/#635/#636/#645 reverted by #648 staged-index contamination PR #648 (merge 691f44f) shipped 3 legit docs (multi-stack hybrid design + react-spa R-phase + SSOT), but its `--allow-empty` CI re-trigger sub-commit captured 16 foreign staged-WIP files, silently reverting merged fixes: - #642 — R2 wirer cwd-anchored ts-morph resolution (wire-eslint-r2.ts back to bare import) - #635 — hooks type:module refresh (install.sh do_refresh + pre-push.ts) - #636 — tsx-at-root probe (install.sh §8b) + DELETED both harness tests (gh-636-*.test.sh) - #645 — the #642 R2-wirer kickoff (deleted) plus package.json/lock dropping ts-morph, CI templates, husky hooks. No restorative commit followed → contamination live on staging. Restores all 16 contaminated files from dd05105 (parent of 691f44f, the pre-contamination state). #650/#651 touched none of the 16 (docs-only), so this is a clean reversal; the 3 legit docs from #648 and #650/#651 research are untouched. Mirrors the contamination exactly: +817 lines = the 817 it deleted. Verified: wire-eslint-r2.test.ts vitest passes; #642 createRequire, #636 tsx-at-root + both gh-636 tests, #645 kickoff all confirmed restored. Prior-art: skipped — recovery of an accidental revert (staged-index contamination), no new capability; the package.json ts-morph line is restoration of a pre-existing dependency removed by the contamination, not a new dependency. * ci(zizmor): pin zizmor==1.25.2 — unpinned install + 1.26.1 bump red-ed all PRs `pip install zizmor` (unpinned) installed 1.26.1, which tightened the adhoc-packages audit and surfaced 5 pre-existing LOW findings in our own self-CI workflows (audit-self.yml npm install --prefix / git init; framework-self-template-render.yml:35) that 1.25.2 did not flag. ci-success `needs: zizmor` → every fresh PR went red (recovery #653 included), with no code cause. Pin to the last known-green 1.25.2 for deterministic CI; a zizmor bump is now a deliberate edit, not a silent CI break. The 1.26.1 adhoc-packages findings (5 LOW, self-CI only — not shipped supply chain) are deferred to a separate triage (suppress-config vs fix), off the critical path here. Prior-art: skipped — CI tooling version pin (one-line determinism fix), no new capability, no new dependency. * fix(install): R2 Layer-2 wirer registers the plugin so the wired config loads (#644) (#647) * docs(spec): R2 Layer-2 wirer plugin-registration fix design (#644) * docs(plan): R2 Layer-2 wirer plugin-registration fix implementation plan (#644) * docs(plan): address Phase -1 cold-review — BLOCKER eslint resolve via package.json + 2 MAJOR + P2 assertion (#644) * feat(install): R2 wirer supports self-contained (plugin-registering) variant (#644) * feat(install): compute relative customRules import path for self-contained wire (#644) * feat(install): resolveAndWire orchestrates bare→self-contained via injected probe (#644) * feat(install): probeViaEslint default probe resolves eslint from consumer cwd (#644) * feat(install): apply-mode wiring goes through resolveAndWire probe loop (#644) * test(install): W3 loadability gate — wired config must load in ESLint (#644) P1 (plugin-less base) + P2 (plugin-registering base) run the wirer in apply mode and assert eslint --print-config LOADS (rc 0). Red-then-green verified: pre-fix wirer makes P1 fail with the exact 'could not find plugin' error on eslint 9.39.4 (confirms v9 parity, spec R1). _mk_consumer sets type:module (the .ts plugin-import precondition Layer-1 already needs) + installs ts-morph (wirer resolves it from cwd). Fixture X switched to --diff to isolate its ts-morph cross-checkout concern (#642) from the new apply-mode probe (#644). * fix(install): manual R2 snippets register the plugin (self-contained) (#644) * test(install): correct the false 'print-config in consumer-pipeline' comment (#644) * fix(install): load .ts eslint config on Node 20 via tsx loader (#644) The shipped eslint config (.mjs) imports the eslint-rules-local/index.ts barrel; on the shipped .nvmrc (Node 20, no native type-stripping) plain eslint fails with ERR_UNKNOWN_FILE_EXTENSION — breaking both the R2 wirer's self-contained escalation AND Layer-1's own lint. Preserve the consumer's Node choice (don't force a bump): run eslint through the tsx loader (already a CORE_DEVDEP + the pre-push-hook pattern; build-first-reuse). - probeViaEslint: node --import tsx (when tsx resolvable; else plain node, Node>=22.18). - consumer lint/lint:fix scripts (install.sh + setup.sh): NODE_OPTIONS=--import tsx. - W3 harness: _mk_consumer installs tsx; P1/P2 validate via the loader. Verified on simulated Node 20 (--no-experimental-strip-types): loader -> rc 0, plain -> ERR. * docs(#646): I-phase kickoffs for react-spa + react-native presets (Stage 1) (#655) Two parallel-dispatch I-phase kickoffs for the merged #646 Stage 1 R-phase (curated bridge). Each is self-contained for an autonomous aif worker. - react-spa: 3 ADOPT (eslint-plugin-react/hooks-v6/jsx-a11y/boundaries) + 1 BUILD (require-error-boundary presence-check, #140 — upstream covers usage not presence; AST-over-grep, narrow in-file scope per SSOT #115 precedent). - react-native: Expo-only (orchestrator scope decision 2026-06-22), 4 ADOPT + 1 ADAPT (no-restricted-globals denylist #146 — Expo baseline whitelists web globals per #651) + 1 REJECT (FlashList #148). Zero from-scratch BUILD = a build-first-reuse success per design-spec section 8. - Both fence off install.sh / packages/core/principles/* / prior-art-evaluations.md / preset-next-15-canonical (install+principle wiring is a separate follow-up task) -> no egress collision between the two parallel workers. - Phase -1 cold-reviewed (2x Opus, read-only): all load-bearing SSOT claims #136-148 + the #651 whitelist->denylist correction verified TRUE; MAJOR/MINOR findings folded in (peerDeps, package.json mirror, egress-guards, T-MS test cases). * docs(#646): revise react-native kickoff to ship BOTH Expo + bare-RN baselines (#656) Operator override (2026-06-22): Expo-only -> both baselines. The project goal is multi-stack + a factory for any stack (README#why-this-exists); covering the Expo AND bare-RN consumer up front serves that goal directly, weighted over the earlier YAGNI lean. - Ship eslint-config-expo/flat (#142) AND @react-native/eslint-config (#143) as two selectable baselines over a shared rn-common layer (style/a11y/no-restricted -globals denylist). Shipping #143 resolves the open DECISION-NEEDED the R-phase explicitly left to the operator (reviewer-discipline §2 path) — not a contradiction. - ZERO custom rules unchanged (build-first-reuse success); two-baseline = maximal REUSE. - Phase -1 cold-reviewed (1x Opus): GO, 0 BLOCKER/0 MAJOR; #143 ship-vs-swap consistency verified against R-phase + SSOT; egress fences still airtight; T-gate 4 distinct T-numbers. 1 MINOR folded in (denylist is a separate rule key, fires additively — not a key-collision to 'win'). * docs(654): kickoff for ci-adhoc-install-fix umbrella (option 2b) (#657) Dispatch input for aif autonomous execution of GH issue #654 option 2 (fix the 5 zizmor adhoc-packages findings via npm ci + re-pin zizmor 1.26.1 + internalise unpinned-CI-tool-install as a Class-A rule with an earliest-channel pre-push check). Kickoff passed Phase -1 (2x Opus round 1 caught 3 BLOCKER + 5 MAJOR; round 2 GO). Must reach staging before aif sees it (kickoff-staging-placement.md). * docs(#646): integration kickoff — install.sh wiring + principle 05/09 (Stage 1 Phase B) (#658) Single integration task owning the SHARED files both presets need: install.sh arg-parser/guard/menu/auto-detect/SHIPPED_DOCS/copy-blocks/dev-deps, plus principle 09 (REQUIRED_HEADER_DOCS + drift-test count 19->25) and principle 05 (manifest-rendered-vs-static, determined by grep on the render-rules provenance marker). - Binding dispatch dependency: ONLY after BOTH preset PRs merge to staging (copy-blocks reference real preset files; principle 09 registers real RULES.md/ARCHITECTURE.md). - Phase -1 cold-reviewed (1x Opus, REVISE->GO round 2): 3 MAJOR fixed (SPA eslint basename = eslint.config.react.mjs not -spa suffix; RN ships no playwright.config.ts; principle-05 rendered-vs-static via deterministic grep) + 4 MINOR (count->25 explicit, guard message, detect order RN-before-spa, don't-touch-react-next-blocks). M3 grep marker independently re-verified against next RULES.md:13. * docs(#646): stage-2-generate-path umbrella kickoff (Stage 2) (#659) * docs(#646): Stage 2 generate-path brainstorm spec Records the brainstorm feasibility framing for epic #646 Stage 2 (LLM generate-path). Corrects the Stage-1 design-doc claim that L4 does not exist (packages/core/validator/ ships gates 1/2/4/6, Phase 7); reframes Stage 2 as swap-curated-inputs-for-LLM at L2/L3 with L4/L5 unchanged. Lands reasoned recommendations on both strategic forks (entry point = L3-live-first vs existing L4; scope = Path A only) plus the mandatory build-vs-reuse gate (aif-evolve / OMC) before any live-LLM build. Status: proposed — pending operator ratification. Not a kickoff. Prior-art: skipped — docs-only brainstorm spec, no new capability (no dependency, no code under packages/). * docs(#646): stage-2-generate-path umbrella kickoff (Stage 2) Operationalises the 2026-06-22 brainstorm spec into a dispatch-ready umbrella kickoff for #646 Stage 2 (the LLM generate-path). Stage 0 = build-vs-reuse R-phase (aif-evolve / OMC) is BLOCKING — no L2/L3 code lands before its verdict. Reconstructed onto staging cleanly, without the cross-umbrella plugin commits that had tangled the original brainstorm branch. Prior-art: skipped — docs-only umbrella kickoff, no new capability (no dependency, no code under packages/). * style(#646): prettier-format stage-2 generate-path spec Hooks were bypassed on the original brainstorm commit (3da9578, core.hooksPath=/dev/null); this applies the prettier pass it missed — italic-emphasis normalisation (*x* → _x_) and table-column padding. No content change. Prior-art: skipped — prettier formatting only, no new capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * CC plugin packaging (S0–S1 + umbrella) — superpowers-style hybrid (#660) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * docs(plugin): REVISE round after Phase -1 cold-review — dispatch-ready kickoff Phase -1 adversarial cold-review (CLAUDE.md meta-orchestrator self-review obligation) returned REVISE; one round applied: - S0 SSOT entries #149-152 (superpowers ADOPT, run-hook ADOPT-audited, using-* ADAPT, CC-docs REFERENCE) - closes the S1 trailer forward-reference (B1). - kickoff records current state: S0+S1 DONE (63b344c, fa37b2b), dispatch starts S2 (B1/B2). - hook count 15 -> 14, all 14 enumerated in plan Task 2 (B3). - compliance-verifier: stop claiming 'per extension.json'; S4 adds it (B4). - principle 21 -> full filenames (M1); dispatcher/pipeline paths (M3); tool-bootstrapping OUT of v1 (m1). Prior-art: prior-art-evaluations.md#149-152 (S0 SSOT entries; superpowers plugin/run-hook ADOPT, using-superpowers ADAPT, CC docs REFERENCE). * fix(plugin): restore 654 kickoff erroneously deleted by S0 commit Commit 63b344c (S0 spec+plan+kickoff) deleted .claude/orchestrator-prompts/654-ci-adhoc-install-fix/kickoff.md — a foreign file of the active 654 ci-adhoc-install-fix umbrella (present on origin/staging, blob c9019eb). Likely a stale staged-index artifact at branch-creation time. Restored byte-for-byte from origin/staging so this PR does not drive-by-delete another umbrella's kickoff. No plugin-packaging content affected. Prior-art: skipped — doc restoration, no new capability or dependency. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(preset-react-spa): React-19 SPA preset (#646 Stage 1) (#661) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * feat(preset-react-spa): React-19 SPA preset — 3 ADOPT + require-error-boundary (#646 Stage 1) Built autonomously by aif task 547d9114; harvested selectively (preset dir only — excluded base-contamination: hook-mods, foreign kickoff deletions, root lock). - 3 ADOPT: eslint-plugin-react (#136), react-hooks v6 (#137), jsx-a11y (#138), eslint-plugin-boundaries (#139) — wired in self-contained eslint.config.react.mjs (no @next). - 1 BUILD: require-error-boundary (#140) — real AST rule (ESLintUtils.RuleCreator + AST_NODE_TYPES JSX visitor, not string-grep) + paired valid/invalid test (principle 02). - verify-before-harvest: 14 preset files present, out-of-scope clean (no install.sh/ principles/prior-art edits), AST + paired-test sanity passed. - install.sh wiring + principle 05/09 registration = separate integration task (#658). Prior-art: prior-art-evaluations.md#136/#137/#138/#139 (ADOPT upstream react lint plugins) + #140 (BUILD require-error-boundary, presence-gap confirmed, no upstream analog) + #141 (WATCHLIST @eslint-react). SSOT rows recorded in merged R-phase #648. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(preset-react-native): Expo + bare-RN preset, both baselines (#646 Stage 1) (#662) * docs(plugin): CC plugin packaging spec + plan + umbrella kickoff Spec/plan/kickoff for packaging rules-as-tests-aif as a CC-first plugin (in-repo marketplace, soft/hard layer split, hybrid install seam, recursive self-test). Kickoff carries ai-laziness-traps T-enumeration (7 canonical + 2 domain) per ai-laziness-traps.md §3. * feat(plugin): plugin.json + in-repo marketplace + run-hook.cmd skeleton (S1) Task 1 of the plugin-packaging umbrella. In-repo marketplace source verified as relative path string ("./plugin") against CC docs. run-hook.cmd adopted from obra/superpowers, audited (T13): its missing-arg guard is Windows-only — Unix path exits non-zero, asserted accordingly. Both acceptance tests green (manifest-valid 5/5, run-hook 2/2). Prior-art: skipped — new files outside packages/, no new dependency; not a capability commit per CLAUDE.md mechanical definition. run-hook.cmd is an ADOPT from superpowers (MIT), recorded for the S0 SSOT entry. * feat(preset-react-native): Expo + bare-RN preset, both baselines (#646 Stage 1) Built autonomously by aif task 808f858a (rebuilt for both-baselines per operator override); harvested selectively (preset dir only, excluding base-contamination + preset-local lock). - BOTH baselines: eslint-config-expo/flat (#142, Expo) + @react-native/eslint-config (#143, bare-RN) over a shared eslint.config.rn-common.mjs layer. - ADOPT plugins: eslint-plugin-react-native (#144/#147), react-native-a11y (#145). - ADAPT: no-restricted-globals denylist (#146) — present in all three configs (forbids web globals the Expo baseline whitelists, per #651 correction). - REJECT: FlashList custom rule (#148). ZERO eslint-rules/ (build-first-reuse success). - No playwright.config.ts (RN web-only, correctly omitted). - verify-before-harvest: both baseline configs + rn-common + denylist present; eslint-rules absent as designed; out-of-scope clean. - install.sh wiring + principle 05/09 = separate integration task (#658). Prior-art: prior-art-evaluations.md#142/#143 (ADOPT Expo + bare-RN baselines) + #144/#145/#147 (ADOPT react-native lint plugins) + #146 (ADAPT no-restricted-globals denylist) + #148 (REJECT FlashList BUILD). SSOT rows recorded in merged R-phase #650/#651. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S2 — relocate shippable hooks (per-hook path-class audit) (#664) * feat(plugin): relocate shippable hooks to plugin payload (path-class audit) (S2) Per-hook triage of all 14 .claude/hooks/*.sh (docs/superpowers/plans/plugin-hook-triage.md): SHIP=2, INTERNAL=12. Each hook's self-declared @dual-pair/@cc-only-rationale marker is the primary evidence. Relocate inject-matching-rule into plugin/hooks/ as an extensionless, context-agnostic script: every path it touches is project-data, resolved via ${CLAUDE_PROJECT_DIR} (T-PLUG-A — no blanket env swap; a swap to ${CLAUDE_PLUGIN_ROOT} would make it read the plugin's non-existent rules instead of the consumer's). hooks.json wires PostToolUse→inject-matching-rule via run-hook.cmd; SessionStart→session-start deferred to S3. inject-session-bootstrap→session-start, also S3. check-doc-authority stays INTERNAL (needs the framework TS toolchain); deps-hash-check already ships via install.sh. Verified: tests/plugin/hook-paths.test.sh 6/6; throwaway-consumer smoke proves the hook fires on a matching glob, reads the CONSUMER's .claude/rules via CLAUDE_PROJECT_DIR, and no-ops on non-match. Prior-art: skipped — relocation of an own hook into the plugin payload, no new external dependency or capability. * test(plugin): harden hook-paths assertion (d) vs T-PLUG-A inverse mis-rooting (S2 cold-review) Phase -1 cold-review CONCERN: assertion (d) only checked that CLAUDE_PROJECT_DIR appears somewhere in the file, so a hook rooting consumer .claude/rules at ${CLAUDE_PLUGIN_ROOT} while mentioning CLAUDE_PROJECT_DIR elsewhere would falsely pass. Added a negative arm rejecting CLAUDE_PLUGIN_ROOT-rooted .claude/rules. Verified: real hook still 6/6; a constructed inverse-bug fixture now FAILs; clean after removal. Prior-art: skipped — test hardening only, no new external dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * fix(654): zizmor adhoc-packages findings + ci-tool-pinning rule (option 2b) (#663) Fix the 5 zizmor adhoc-packages findings via npm ci + re-pin zizmor 1.26.1 + internalise unpinned-CI-tool-install as Class-A ci-tool-pinning.md (pre-push gate + paired-negative test, principle-09 registered). aif autonomous task e8528798 + orchestrator cold-QA rework (B1 SSOT #149->#153 staging-collision, M1 narrow Rule B). Prior-art: prior-art-evaluations.md#153 (zizmor adhoc-packages REFERENCE + bare-run tool-pin BUILD; HYBRID). §1.7: forward — no-paid-llm-in-ci (deterministic scan), build-first-reuse (REUSE zizmor + BUILD bare-run slice), doc-authority (Class-A + principle-09). backward — codifies 2026-06-22 unpinned-zizmor incident (audit-self.yml:741). See packages/core/hooks/checks/unpinned-tool-install.ts:1. * feat(plugin): using-rules-as-tests bootstrap + shippable skills (S3) (#665) Author plugin/skills/using-rules-as-tests/SKILL.md (ADAPT of superpowers using-superpowers — their mechanism: invoke-skills-before-responding + the instruction-priority ladder; our content: the rules-as-tests skill set + the soft/hard honesty boundary). Add plugin/hooks/session-start (extensionless, @cc-only-rationale, self-resolves dir, ${CLAUDE_PLUGIN_ROOT}) that injects the bootstrap so the skill auto-triggers without a manual Skill invocation. Copy the consumer-facing rules-as-tests skill (SKILL.md + 5 references) into plugin/skills/. Wire SessionStart→session-start in hooks.json. v1 ships only rules-as-tests + the bootstrap (per extension.json); tool-bootstrapping deferred. Verified: tests/plugin/bootstrap.test.sh 10/10 (session-start emits the bootstrap naming the skill, the CLAUDE.md/AGENTS.md instruction ladder, and the /install-enforcement boundary; both shipped skills carry valid frontmatter); hook-paths 10/10 (session-start extensionless+marked+wired); principle 09 + 22 green. Prior-art: prior-art-evaluations.md#151 (superpowers using-superpowers — ADAPT: their SessionStart-bootstrap mechanism, our content). Co-authored-by: Art <devartyhoo@gmail.com> * chore(654): umbrella done.md (closure convention) (#666) Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S4 — ship consumer-facing agents + principle 21 plugin-tree arm (#667) * feat(plugin): ship consumer-facing agent subset + principle 21 plugin-tree arm (S4) Triage (T-PLUG-B): SHIP review-sidecar, living-docs-auditor, compliance-verifier (consumer-facing per extension.json + the Artifact Ownership Contract); INTERNAL aif-init, manual-rule-liveness-prober, memory-codification-auditor, orchestrator-worker-discipline, shipped-agent-liveness-prober. Copy the 3 SHIP agents verbatim into plugin/agents/ (byte-identical to the agents/ source they mirror). Register compliance-verifier in extension.json. Extend principle 21 with collectPluginAgentFiles() + arm (h) so the plugin's shipped agents are guarded by the same #551 tool-name-validity gate as agents/*.md (non-vacuity floor ≥3; the formal manifest-integrity self-test lands in S6). Verified: principle 21 10/10 (arm (h) red with 0 agents → green after copy); principle 09 green (agents carry their REQUIRED_HEADER_DOCS doc-authority headers); plugin bash suite 4/4. Prior-art: skipped — test arm extension for an existing capability + verbatim relocation of own agents; no new external dependency. * test(plugin): relabel plugin-tree arm (h)→(j) to de-dup the label (S4 cold-review nit) Prior-art: skipped — cosmetic test-label fix, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * docs(stage-2-generate-path): Stage 0 close — land prior-art research-patch + SSOT #154–#168 (#668) Make the Stage-0 reuse-gate research-patch visible to dispatch (which reads from staging): the patch was untracked on a feature branch, so /pipeline + aif dispatch could not see it. - Add the research-patch under docs/meta-factory/research-patches/ (was untracked). - Append 15 SSOT entries (#154–#168) for candidates 3–17 (ESLint RuleTester, Semgrep, OpenRewrite, GritQL, config-inspector, antfu/Sheriff, Biome/oxlint, OPA/Checkov, SonarQube, CEGIS, Reflexion, compiler self-hosting, Renovate, Nx); cross-link #1/#50/#66/#105 (Last-reviewed bumped, verdicts unchanged). - Mark Stage 0 discharged in the kickoff + acceptance criterion met. Verdict: /aif-evolve = REFERENCE -> the L3-live synthesizer stays BUILD; Oh My ClaudeCode = REJECT. Landing-time CI-compliance fixes on the as-authored patch: add the principle-10 <!-- scope:... --> first-line annotation; collapse compound verdict cells to single principle-11-recognized tokens (dual nature preserved in each rationale). All 25 principle suites green (219 tests). Prior-art: skipped — Stage-0 R-phase research-patch + SSOT append (#154-#168) + kickoff discharge; doc-only, no new capability or dependency. Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): /install-enforcement fetch-and-wire seam (S5, Option C) (#669) Hybrid seam reaching the HARD enforcement layer (git hooks + CI). Option C (maintainer decision 2026-06-22, overrides spec §6 bundle): rather than bundle ~2MB of install.sh's payload into plugin/install/ (which would duplicate the plugin's own skills/agents + carry the not-shipped dispatcher/pipeline + risk repo-wide CI scans), the thin plugin/install/fetch-and-wire.sh FETCHES the project's own official install.sh (pinned to plugin version; RAT_INSTALL_SOURCE override for forks/offline/tests) and runs it verbatim against $CLAUDE_PROJECT_DIR. Default dry-run (writes nothing); --apply does the real run. commands/install-enforcement.md orchestrates consent (dry-run → show → [y/N] → --apply); installing-enforcement/SKILL.md documents WHEN to wire + the honest soft/hard boundary (T16). The thin-reimplemented-wirer alternative was rejected (would discard install.sh's battle-hardened wiring, fixes #532/#533/#635). Spec §6 + plan Task 5 updated to reflect the decision; plan S6 step updated (no bundled install.sh to hash → assert fetch-and-wire version pin === plugin.json). Verified offline (RAT_INSTALL_SOURCE=repo root): tests/plugin/install-seam.test.sh 9/9 — dry-run writes nothing+exits 0, --apply creates .husky/pre-commit+pre-push in a throwaway consumer, unknown flag exits 2; full plugin suite 5/5; principle 09 green. Prior-art: skipped — thin seam under plugin/ (no new dependency, no packages/ code); reuses the project's own install.sh verbatim per companion-install-principle.md (install via the official top-level installer). Co-authored-by: Art <devartyhoo@gmail.com> * test(plugin): S6 — principle 24 plugin-manifest integrity + paired-negative (T15) (#670) * test(plugin): principle 24 — plugin manifest integrity + paired-negative (S6) Recursive self-test (T15): the packaging enforces its own integrity. checkPluginIntegrity() is a pure function (plugin dir + manifest dir → violations) run on BOTH the real tree (arm a, 0 violations) and a deliberately-broken fixture (arm b — tests/fixtures/plugin-broken-manifest trips V2 version-drift + V3 header-less skill + V6 dangling hook target), the principle-02 paired-negative discipline that makes the gate non-tautological. Plus real-tree arms: version pin (fetch-and-wire RAT_PLUGIN_VERSION === plugin.json.version), drift guards (plugin/agents/*.md byte-identical to agents/ source; plugin/hooks/inject-matching-rule shares the source's @dual-pair anchor + glob_match core), and T15 self-application. Added Authoritative-for headers to the two new plugin skills so V3 passes. plugin doc-authority is owned HERE (V3 for skills; the byte-identical drift guard covers plugin/agents headers transitively via principle-09-enforced agents/ sources) rather than by extending principle 09 — closes the S4 cold-review gap without touching the shared principle. Verified: principle 24 6/6 (real-tree green, fixture red-detected); full principle suite 26 files / 226 tests; plugin bash suite 5/5. Prior-art: skipped — test addition (principle self-test) for the plugin-packaging capability already SSOT'd at #149-152; no new dependency or external capability. * test(plugin): harden principle 24 arm (e) — byte-identical core drift guard (S6 cold-review) Cold-review CONCERN: arm (e) only asserted @dual-pair + glob_match() string presence, so a regression INSIDE glob_match's body would not be caught. Now extracts the matcher+injection core (from glob_match() to EOF — the only legit divergence is the relocation above it) and asserts byte-equality between plugin/hooks/inject-matching-rule and the source. Verified: real tree green; a mutation inside the glob_match region now goes RED. Prior-art: skipped — test hardening only, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): OpenCode degradation adapter (S7) (#671) .opencode/INSTALL.md — fetch-and-follow adapter pointing OpenCode at the shared plugin/skills/ body (via opencode.json skills.paths). Documents the ONE accepted off-CC degradation: SessionStart auto-injection of the using-rules-as-tests bootstrap does not fire off-CC → read it on demand (a documented degradation per dual-implementation-discipline §3, NOT a portability gap — the skill content is fully portable). Tool mapping (CC Skill→OpenCode skill tool, Task→@mention, etc.); the hard layer routes through the portable plugin/install/fetch-and-wire.sh seam (same honest soft/hard boundary). extension.json gains a $plugin-packaging note reconciling it with the plugin payload (source paths kept — install.sh + principle 21 read them; not repointed). AGENTS.md cross-harness layer is referenced from the adapter, not duplicated. Verified: tests/plugin/opencode-adapter.test.sh 7/7; full plugin bash suite 6/6; markdownlint 0. Prior-art: skipped — docs + a small acceptance test, no new dependency or capability (ADAPT of superpowers' .opencode fetch-and-follow pattern, SSOT-adjacent). Co-authored-by: Art <devartyhoo@gmail.com> * feat(plugin): S8 — README per-harness install + fetch ref → main (umbrella finale) (#672) * feat(plugin): README per-harness install + fetch ref → main (S8) README 'As a Claude Code plugin (per-harness)' section: the marketplace-add/install flow, skills auto-trigger, the honest soft/hard boundary (T16 — the plugin never wires git/CI; /rules-as-tests:install-enforcement does, opt-in), the OpenCode path + accepted degradation, and plugin-vs-./setup. §Why this exists untouched (read-only per the Artifact Ownership Contract). fetch-and-wire.sh: decouple RAT_INSTALL_REF from the plugin version → default 'main'. Evidence: the framework's latest release tag v0.3.0 is 2026-05-06, staging is 72 files / +17551 ahead in install.sh + payload (incl. the hardening fixes #531/#551/#635/#636 the S5 cold-review valued); origin/main (2026-06-18) carries the current installer (only 65 lines behind staging). Pinning to v<plugin-version> would ship a 6-week-stale install.sh. Plugin keeps its own 0.1.0 version line; a reproducible per-plugin release tag is a future maintainer action. Spec §6 updated. Verified: composite e2e on a throwaway consumer — SessionStart bootstrap fires, inject-matching-rule reads consumer rules, /install-enforcement seam dry-run writes nothing + --apply wires .husky pre-commit/pre-push + CI, manifest version-parity True; all 6 plugin bash tests + principle 21/24/09 (39) green; README markdownlint 0. Prior-art: skipped — docs + a config-default change under plugin/, no new dependency or capability. * docs(plugin): umbrella closure — plugin-packaging done.md (S8) CLAUDE.md Umbrella closure schema: Final PR #672, all S0–S8 merged to staging. Records the stage-PR map + 3 maintainer-owned residuals (live /plugin install unproven-from-automation, install.sh-vs-plugin agent divergence, reproducible-tag option). Prior-art: skipped — closure doc, no new dependency or capability. --------- Co-authored-by: Art <devartyhoo@gmail.com> * test(validator): L4 adversarial hardening — tautology/never-firing/conflict fixtures (paired-negative) (#674) Adds three adversarial SynthesisPlan fixture pairs (bad + valid) that exercise the exact reject surfaces of Gates 2, 4, and 6: - gate-tautology.adversarial.test.ts (T1): no-restricted-imports forbidding 'react' fires on negative-corpus/unrelated.tsx (Gate 4 fail); paired with a lodash-forbidding rule that does NOT fire on the corpus (Gate 4 pass). - gate-rule-tester.adversarial.test.ts (T2): comment-only input that never triggers no-restricted-imports (Gate 2 fail); paired with a lodash-import input that correctly fires (Gate 2 pass). - snapshot.adversarial.test.ts + expected-adv-*.json (T4): full validate() aggregate snapshots for each bad fixture confirm ok:false and the right gate routes to 'fail'. Separate from existing self/next-16 snapshots. T3 (Gate 6 conflict): adversarial orphan-ref + snippet-drop cases are already present in gate-conflict.test.ts:52-99; no new file needed. Gate 6 snippet-drop is additionally covered via snapshot.adversarial.test.ts using a no-debugger rule absent from eslintConfigSnippet: '{}' (clean Gate 2 + 4 pass, Gate 6 fail). Gate source files (gate-*.ts, validate.ts) are UNCHANGED — hardening adds tests/fixtures only. validator suite: 45 tests, all pass. Prior-art: prior-art-evaluations.md#154–#168 (Stage-0 SSOT for L4 generate-path hardening — adversarial corpus design patterns, no upstream production-grade paired-negative harness for ESLint-rule validation existed at SSOT survey time). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deps): unpin revoked eslint 10.4.1 → ^10.4.0 + sync root lockfile (preset-react-native/spa) (#676) eslint 10.4.1 was unpublished from the npm registry (version-API → 404; tarball still served by the CDN), so any root-level `npm install` / `npm ci` / `--package-lock-only` failed with ETARGET, leaving the root lockfile unable to regenerate. Consequently the new workspace packages preset-react-native (#646) and preset-react-spa (#661) were never added to package-lock.json. guard-liveness-fullsweep (pull_request → main only) is the one required check that runs `npm ci` on the root workspace, so the drift surfaced only on promote PR #673 — red on "Missing: @rules-as-tests/preset-react-native from lock file". Fix: pin packages/core eslint to ^10.4.0 (latest existing in the 10.4 line; API stable vs 10.4.1) and regenerate the root lockfile. Verified locally: npm ci rc=0; guard-liveness:fullsweep GREEN (v1 ESLint 8/0, v1.5 cmd 3/0, v3 structural pass); lockfileVersion stays 3 (node20/npm10 CI-compatible); linux-x64 bindings present; package count 458→770 (preset deps). Prior-art: skipped — dependency version-pin fix (revoked eslint 10.4.1 → existing 10.4.0) plus mechanical lockfile sync; no new capability introduced. Co-authored-by: Art <devartyhoo@gmail.com> * fix(dispatcher): classify aif plan_ready + review as RUNNING in monitor-classify (#675) monitor-classify.sh fell through `plan_ready` and `review` to the `*)` arm, emitting UNKNOWN:<status>. dispatcher SKILL.md §2.2 branches only on RUNNING:* / DONE:* / PARKED:* / ERROR:* prefixes, so an UNKNOWN:* result is unhandled and the single-poll-per-turn loop stalls/misfires — and every aif task passes through plan_ready (backlog→planning→plan_ready→implementing→review→done). Map plan_ready and review to RUNNING, mirroring the canonical mapAifStatusToTaskStatus (packages/runtime-bridge/src/aifWsStatus.ts:82), which classifies plan_ready / implementing / review all as 'running'. blocked_external stays intercepted by the is_parked check (PARKED); genuinely-unknown future statuses still surface as UNKNOWN (catch-all preserved). Tests (packages/core/skills/dispatcher/monitor.test.ts): adds positive cases (plan_ready→RUNNING, review→RUNNING, backlog→RUNNING) and a paired-negative regression-guard block asserting running states never classify as UNKNOWN. 16/16 green; verified RED (4 failing) before the fix. Live-hit 2026-06-22: a poller dispatching stage-2-generate-path Stage 1 exited prematurely on UNKNOWN:plan_ready, treating a running task as terminal. Prior-art: skipped — bug fix + test additions for an existing capability, no new dependency/module/capability surface. Co-authored-by: Art <devartyhoo@gmail.com> --------- Co-authored-by: Art <devartyhoo@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What (S8 — docs + publish + integration; umbrella finale)
The last stage of the CC plugin packaging umbrella. After this, S2–S8 are all on
staging.### As a Claude Code plugin (per-harness)section: the marketplace-add/install flow, skills auto-trigger, the honest soft/hard boundary (T16 — the plugin never wires git/CI;/rules-as-tests:install-enforcementdoes, opt-in), the OpenCode path + accepted degradation, and plugin-vs-./setup.§Why this existsuntouched (read-only per the Artifact Ownership Contract).fetch-and-wire.shref decoupled →main— see the decision below.0.1.0(its first release;plugin.json===marketplace.json, principle 24 asserts parity).Decision (evidence-backed, decided not punted): fetch ref →
main, not a version tagThe seam previously pinned the fetch to
v<plugin-version>(v0.1.0). I verified: the framework's latest release tagv0.3.0is 2026-05-06 —stagingis 72 files / +17551 ahead ininstall.sh+ hard-layer payload, including exactly the hardening fixes the S5 cold-review valued (#531/#551/#635/#636).origin/main(2026-06-18) carries the current installer (only 65 lines behind staging). So pinning to a version tag ships a 6-week-stale installer. Verdict: trackmain(current + stable for a consumer-facing seam); plugin keeps its own0.1.0version line. A reproducible per-plugin release tag is a future maintainer release action (recorded indone.md).Publish-gate (from S3 cold-review) — PASS
The always-on bootstrap points at
/rules-as-tests:install-enforcement; before publishing, that must be real. Confirmed present:plugin/commands/install-enforcement.md+plugin/skills/installing-enforcement/SKILL.md+plugin/install/fetch-and-wire.sh.Verification (T3 — composite e2e on one throwaway consumer)
All 6 plugin bash tests + principle 21/24/09 (39 tests) green; README markdownlint 0.
Observations to surface (per CLAUDE.md PR strategy — not auto-resolved)
install.shships 6 agents (+aif-init, +memory-codification-auditor, +orchestrator-worker-discipline); the plugin ships the 3 consumer-facing ones (extension.jsonSSOT). Deliberate curation or worth aligning — a maintainer call.main; cutting a per-plugin release tag (and pinning to it) would make installs reproducible — a future release decision.§1.7 Forward-check applied
README.md:138; the plugin's git/CI wiring is opt-in via the command, not the install.maindecision keepsinstall.shreuse verbatim + current; the stale-tag trap is documented atdocs/superpowers/specs/2026-06-22-cc-plugin-packaging-design.md:116.node:fsonly (tests/plugin/install-seam.test.sh:1); zero API.§1.7 Backward-check applied
README.md§Why this exists(the goal section,README.md:31) is unmodified — confirmed by diff; only an install subsection was added.RAT_PLUGIN_VERSION="0.1.0"(plugin/install/fetch-and-wire.sh:33) so principle 24's version-pin arm stays green (parity unchanged); only the decoupledRAT_INSTALL_REFdefault moved..claude/rules/, principle, orCLAUDE.mdfile modified.🟢 Простыми словами
Финал: в README появилась честная инструкция «ставим как плагин Claude Code» — одной командой берёшь мягкий слой (навыки/агенты), а жёсткий (git-хуки + CI) включаешь отдельной командой, плагин его сам не трогает. Плюс важная починка: установщик теперь подкачивается со стабильной ветки
main, а не с тегаv0.3.0— потому что тот от 6 мая и не содержит кучу фиксов install.sh за полтора месяца. Проверил весь путь на одноразовом репо вживую; единственное непроверенное — сама команда/plugin installв живой сессии (её из скрипта не позвать), это твоя финальная проверка.