research(universal-satellite): R-phase per-companion capability + integration matrix - #252
Merged
Conversation
…on capability + integration surface matrix Per universal-satellite vision (2026-05-27): maps each major companion (AI-Factory / Superpowers / OhMyOpencode / aif-handoff / TaskMaster / Cline/Cursor agent / OpenCode) against R1-R5 + integration surface for our Living Doc enforcement layer. Key findings: - AIF v2.13.2 ships formal injections[] API in extension.schema.json (target skill + append/prepend + file) — forward-compat path beyond skill-context workaround; SSOT #82 proposed - aif-handoff confirms .ai-factory/skill-context/ IS the injection directory (copied into each task worktree by ensureTaskWorktree) - OhMyOpencode 4-scope priority: our .claude/skills/rules-as-tests/ auto-loaded at scope 3; duplicate-tool-names escape hatch documented - CC settings.json hooks NOT loaded in OpenCode (separate runtime); gap for OpenCode = JS plugin API in .opencode/plugins/ - Cline hooks (.clinerules/hooks/) are separate system from CC hooks; no conflict, but separate deliverable needed for Cline support - SSOT #81 broken citation (Doriandarko/ -> code-yeongyu/) proposed fix - Pre-push + CI enforcement layers are UNIVERSAL across ALL companions - Conflict surface: only review-sidecar naming collision (managed) and OhMyOpencode skill-loading toggle (documented escape hatch) Output feeds future M-A umbrella scope; NOT picking one companion. Universal-satellite = our project works equally well alongside ANY companion stack. Prior-art: prior-art-evaluations.md (rows #27/#28/#29/#30/#43/#46/#50/ #55/#61/#62/#64/#65/#67/#68/#71/#73/#74/#76/#77/#80/#81 referenced; SSOT #82 proposed for AIF injections[] API).
6 tasks
Merged
11 tasks
artyhoo
added a commit
that referenced
this pull request
Aug 17, 2026
…h an honest degrade (#1416) ## Summary Implements the operator-ratified resolution (2026-08-17, **option b**) of the U10 validator-gates fork recorded in [`docs/meta-factory/getff-name-architecture-freeze.md`](../blob/staging/docs/meta-factory/getff-name-architecture-freeze.md) («U10 WARNING»). Shipped validator gates statically imported `@rules-as-tests/preset-next-15-canonical/eslint-rules` — a `"private": true` package that can never appear in a published `@getff/core` tarball — so the module crashed at load and `rules-as-tests-validate` was unrunnable from a tarball even with every dev-dependency promoted. The imports are replaced with a three-tier dynamic resolution (consumer barrel → workspace packages → honest degrade) mirroring the shipped ts-morph pattern at `packages/core/install/wire-eslint-r2.ts:115-143`, plus the decision record in the freeze doc. **Scope correction, measured:** the U10 WARNING table lists **four** files. Enumerating `packages/core/validator/` found **five files / six imports** — `gate-autofix-clean.ts:22` carried the same import, and `gate-rule-tester.ts:23` additionally imported the equally-private `@rules-as-tests/preset-react-spa/eslint-rules`. A fix scoped to the four listed rows would have left the bin crashing. ## Changes - **New** `packages/core/validator/preset-plugin-resolver.ts` — resolves the `rules-as-tests` rule registry in three tiers: 1. the consumer's vendored barrel `<cwd>/eslint-rules-local/index.mjs` (what `setup.d/lib.sh` `generate_eslint_barrel` writes), anchored with `createRequire(resolve(process.cwd(), 'package.json'))` — the GH #642 lesson: framework-anchored resolution misses the consumer's barrel and falsely degrades; 2. the workspace packages — the monorepo/CI path, unchanged; 3. honest degrade — core rules only, with every specifier tried and its concrete failure recorded. - **Five gates rewired** (`gate-tautology`, `gate-conflict`, `gate-rule-tester`, `gate-message-id-coverage`, `gate-autofix-clean`). Each takes an optional `PresetResolutionOptions` second argument; `validate()` calls them unchanged. - **New `degrade` `GateStatus` + `GateDegrade` entries** (`types.ts`). A degraded gate is distinct from `pass`: `to-aif-gate-result.ts` maps it to AIF status `warn` with `severity: "warning"` blockers, `diagnostics/to-diagnostics.ts` emits warning diagnostics, and `FF3022` is registered in `diagnostics/registry.ts` + the append-only codes snapshot. Real failures still outrank a degrade. `ValidationReport.ok` stays `true` — a degraded gate has not proven the plan bad. - **Resolution is synchronous by design.** `validate()` and `install()` are shipped sync APIs; a dynamic `import()` would have forced them and ~30 call sites async for no consumer benefit. `require(esm)` needs Node ≥22.12 (`engines.node` is `>=22`); below that it throws and lands in the tier-(iii) degrade like any other resolution failure — never a crash. - **Tests extended, not forked** — `gate-tautology.test.ts`, `gate-conflict.test.ts`, `gate-rule-tester.test.ts`, `gate-autofix-clean.adversarial.test.ts` + a new `preset-plugin-resolver.test.ts` and a fixture consumer barrel carrying one deliberately tautological rule. - **Anti-regression sweep as a test:** `preset-plugin-resolver.test.ts` enumerates `validator/*.ts` against **every** `private: true` workspace package (read from their `package.json`), rather than allowlisting known offenders — the mechanism that would have caught the fifth file, and that catches the sixth. - **Decision record** — freeze doc U10 WARNING gains the ratified verdict, the tier order, the table correction, and the evidence table; §4.2 scope-ownership is closed (below). - **Backward sweep follow-through** — `install/rule-bootstrap-cli.ts` comments asserting the gates «statically require the sibling preset AT MODULE LOAD» are corrected (shipped file, stale rationale). `hooks/checks/guard-liveness.ts:34` keeps its static import: `hooks/` is not in the `files` allowlist, so it never ships. - **Not touched**, per the task's scope and option (b): `private: true`, the `bin: getff` package, the `eslint`/`@typescript-eslint/*`/`ts-morph` runtime-dep promotion, the publish act. Option (b) keeps the presets out of `peerDependencies`, so no `package.json` changes. ## §4.2 scope-ownership — closed The freeze doc named `npm org ls getff` under an authed account as «the exact command that closes this». Run and reproduced in this session: ```text $ npm whoami artyhoo $ npm org ls getff artyhoo - owner ``` Same owner as the unscoped `getff` name (§4.1) — the §11 «different owner» stop condition does not fire. U10's remaining open items are now only `bin: getff`, the package metadata + `private: true` drop, the runtime-dep promotion, and the publish act. ## Prior-art consult - [x] Capability commits carry a `Prior-art:` trailer. The resolver trips the ≥80-LOC hook arm; the escape hatch is correctly rejected for it, so the fix commit cites the SSOT. - [x] New SSOT entry [`prior-art-evaluations.md#252`](../blob/staging/docs/meta-factory/prior-art-evaluations.md) added **in the same commit** as the capability artifact, with `Verdict` / `Rationale` / `Trigger to revisit`: **ADOPT VOCABULARY** from ESLint's own plugin-resolution ruling (`--resolve-plugins-relative-to` / `resolvePluginsRelativeTo`, default = the end user's cwd; the v6 migration notes record framework-relative resolution as «a design bug that caused ESLint to randomly fail to load plugins» — the same failure class as GH #642). **REJECT** `jchip/optional-require` + `stdlib-js/utils-try-require` (a runtime dep for a 20-line try/catch in a package that must install lean, and their bare `null` is exactly the silent pass this design forbids). **REJECT** `peerDependenciesMeta.<pkg>.optional` with a concrete disqualifier: an optional peer must still be *installable by name*, and a `private: true` package never is. - [x] Existing entries cited for the own-stack half: #131 (`ensure-then-use` presence check), #135 (the gracefully-degrading `wire-eslint-r2` wirer), #209 (a degrade must be a gated verdict, never a log line). - [x] context7 ×3 phrasings + WebSearch run 2026-08-17; candidates cited in the row. Squash-survival line (trailers are dropped at squash — incident PR #1094 → #1097): Prior-art: prior-art-evaluations.md#252 (ADOPT VOCABULARY — ESLint's own plugin-resolution ruling, anchor is an explicit decision and the end user's cwd is the right default; REJECT optional-require helpers as a runtime dep whose bare null is a silent pass; REJECT peerDependenciesMeta.optional — an optional peer must be installable by name, a private:true package never is) ## Test plan - [x] `npm test --workspace=@rules-as-tests/core --run` green — **266 files / 3276 tests passed**, 44 skipped. - [x] `npm run typecheck` green across all workspaces. - [x] `npm run format:check` (the shipped-prettier CI gate) green. - [x] Pre-push gate suite green (incl. the synth-bundle drift gate — bundle regenerated; its only delta is the FF3022 registry entry, and the validator gates are not in that bundle's import graph). - [x] `markdownlint-cli2` + `lychee --offline` green on both edited docs. - [x] **Seeded-break proofs — every new assertion verified RED before GREEN:** | seeded break | result | |---|---| | barrel tier throws (tier (i) disabled) | 6 tests RED — both barrel-anchor arms at gate level *and* resolver level | | a static private import restored in `gate-conflict.ts` | population sweep RED, naming the file and the package | | `gateOutcome` returns `pass` instead of `degrade` | 4 degrade arms RED across three gates | - [x] **End-to-end, tarball-shaped tree** (framework copy with the preset packages absent, `eslint`/`ajv` present, run under `tsx` with cwd = a consumer dir): | arm | result | |---|---| | gates as of `origin/staging` | rc=1 — `ERR_MODULE_NOT_FOUND: Cannot find package '@rules-as-tests/preset-next-15-canonical'` — the U10 crash, reproduced | | this PR, no consumer barrel | rc=0 — `ruleTester`/`tautology`/`conflict`/`autofixClean` = `degrade`, each naming the skipped rule and the specifiers tried; none reports `pass` | | this PR, consumer barrel vendored | rc=0 — the gates RUN: `tautology` **catches** a seeded always-firing rule (`FF3007`) supplied only by the barrel | The third arm is the operator's falsifier: if the barrel anchor were not implemented, consumers would always degrade and option (b) would collapse into theatre. ## Provenance n/a — non-stage PR (single-concern fix + decision record, no kickoff). ## Review findings n/a — non-stage PR. ## Fidelity verdict FIDELITY: skipped — non-stage PR: a single-concern fix implementing an already-ratified operator decision, no kickoff or acceptance contour applies. ## Parked questions n/a. ## §1.7 Self-discipline check ### §1.7 Forward-check applied Disciplines checked against this change, each with the citation that made it binding: - [`.claude/rules/attention-is-not-a-mechanism.md:29`](../blob/staging/.claude/rules/attention-is-not-a-mechanism.md) — «a WARNING whose only consumer is *someone reads the log* is attention-dependent detection». This is why the degrade is a **status** carried in the report and rendered by both consumers (`to-aif-gate-result.ts` → `warn` + `severity: "warning"` blockers; `to-diagnostics.ts` → warning diagnostics), not a `console.warn`. It is also why `preset-plugin-resolver.test.ts`'s sweep enumerates the population instead of trusting the freeze doc's four-row table — bare attention had already missed the fifth file. - [`.claude/rules/ai-laziness-digest.md`](../blob/staging/.claude/rules/ai-laziness-digest.md) **T3** (every finding carries command output or file:line) and **T19** (own cold adversarial pass before handoff) — the three seeded breaks and the three-arm tarball simulation above are that evidence; no claim in this PR rests on «would detect». - [`.claude/rules/build-first-reuse-default.md §1.1`](../blob/staging/.claude/rules/build-first-reuse-default.md) own-stack-first — the mechanism is the existing `install/wire-eslint-r2.ts:115-143` degrade generalized from one optional engine to a rule registry (SSOT #131/#135), not a new invention; the external survey (#252) was still run and recorded. - [`.claude/rules/effort-worthiness.md §1`](../blob/staging/.claude/rules/effort-worthiness.md) — the sync-vs-async fork was decided on reversibility and blast radius (~30 call sites and two shipped sync APIs) rather than on the more «rigorous» async refactor; recorded in the resolver header so the next reader does not re-litigate it. - CLAUDE.md capability-commit gate — the resolver trips the ≥80-LOC arm; the escape hatch was rejected by the hook, so the SSOT entry was written and cited rather than the trailer being softened. ### §1.7 Backward-check applied Sweep of the existing artefacts under the changed surface's scope: - **Whole shipped surface, not just the four named files.** `grep -rn "from '@rules-as-tests/preset-…" packages/core` over every `.ts`/`.mjs` returned the true population: five files under `validator/` (the four listed plus `gate-autofix-clean.ts:22`, and `gate-rule-tester.ts:23`'s second private preset), plus two outside it — `hooks/checks/guard-liveness.ts:34` (a real static import, but `hooks/` is absent from `packages/core/package.json` `files`, so it never ships — left as is) and `install/rule-bootstrap-cli.ts:42-50,367-370` (comments, not imports, asserting a module-load requirement this PR removes — corrected in the last commit). - **Report consumers swept for the new status**, so `degrade` cannot silently read as clean: `to-aif-gate-result.ts` (AIF lane), `diagnostics/to-diagnostics.ts` (diagnostics lane), `validate.ts` `ok` computation (documented: not a failure, not a pass). `aif-gate-result-schema.ts` needed no change — `warn` and `severity: "warning"` were already in the pinned schema. - **Generated artefacts swept**: `synth-and-wire.bundle.mjs` regenerated; verified the validator gates are absent from that bundle's import graph (no `runTautologyGate`/`resolvePluginRegistry` symbol), so install-time wiring is unaffected. - **Existing tests swept for behavioural drift**: the four rewired gates now build their plugin registry from `core ∪ resolved presets` uniformly (previously `gate-tautology`/`gate-conflict` saw presets only, which is *narrower* than the single barrel a consumer receives). The full 3276-test suite is green, and each gate has a «no `degraded` field when everything resolves» arm proving the monorepo path is unchanged.
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.
Summary
universal-satellite-integration-matrixumbrella (2026-05-27).Key findings:
injections[]API inextension.schema.json(targetskill +positionappend/prepend +file) — forward-compat path beyond skill-context workaround (SSOT feat(skill-context): C-1 follow-up — deliver review + rules-check residue into AIF (supersedes #80) #82 proposed).ai-factory/skill-context/IS the injection directory (copied into each task worktree byensureTaskWorktree) — our existinginstall.sh:319-322wiring is confirmed correct.claude/skills/rules-as-tests/auto-loaded at scope 3; duplicate-tool-names escape hatch ="claude_code.skills": falsesettings.jsonPostToolUse hooks NOT loaded in OpenCode harness — gap identified; OpenCode JS plugin API (.opencode/plugins/) is the correct injection path.clinerules/hooks/is a SEPARATE system from CCsettings.jsonhooks; no conflict but a separate deliverable is needed for full Cline supportDoriandarko/oh-my-openagent→code-yeongyu/oh-my-openagent) identified for maintainer fix§1.7 Forward-check applied
Prior-art:trailer present citing SSOT rows docs(research-patches): Wave 6 AI-doc cold audit (§13.26) #27/docs(wave-6): close §13.26 — AI-doc effectiveness cold audit + review verdicts #28/feat: Wave 7 — hot-checks + harness-hooks + template test + §13.23 4th-layer #29/docs(wave-5): readiness REVISE fixes — SSOT ID remap + §13.25 status #30/Wave 8.4: pre-push §7 Prior-art escape-hatch substance arm #43/docs(refactor): split open-questions.md into open + closed-questions archive #46/chore(deps): bump js-yaml and markdownlint-cli2 #50/docs(research-patches): commit 3 patches from 2026-05-16 autonomous research session #55/docs(research-patches): §7 Decision C2 — accept condensed Aider coverage #61/docs(open-questions): §13.35/36/37 — Superpowers ADAPT-candidates (ARMED, §7 Decision D2) #62/chore(skills+agents): cleanup batch from 2026-05-16 skills+agents audit #64/feat(skill-drift): multi-channel drift detection from D-AuditC-5 #65/docs: post-1A follow-up (Class C reviewer-discipline, Q3/Q5, README absolutism) #67/docs(rules): add dual-implementation discipline (D2) #68/feat(hooks): add end-of-turn reminder Stop hook #71/fix(hooks): aggregate full assistant turn for end-of-turn reminder #73/feat(principles): add principles 12 + 13 — ai-laziness-traps and research-patch §1.7 enforcement #74/docs(ssot): add prior-art entry #49 — Karpathy skills (DEFER/REFERENCE) #76/docs(research): cross-worktree gitignored coord-doc sync — verdict ADAPT #77/feat(agents): resolve C-1 agent-name collision with AI Factory (KEEP-AIF / skill-context / rename) #80/fix(hooks): force visible "🟢 Простыми словами" block in end-of-turn reminder #81<!-- scope:universal-satellite-integration-matrix -->annotation present (principle 10)doc-authority-hierarchy.md §5)§1.7 Backward-check applied
injections[]API discovered via npm-pack (not in prior SSOT); (3) T-CR-A probe refinement appliedHow to verify
docs/meta-factory/research-patches/2026-05-27-universal-satellite-integration-matrix.md(508 lines) — verifiedScope-out