instar 1.3.763 → 1.3.765

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/core/ClaudeCliIntelligenceProvider.d.ts +15 -0
  2. package/dist/core/ClaudeCliIntelligenceProvider.d.ts.map +1 -1
  3. package/dist/core/ClaudeCliIntelligenceProvider.js +111 -23
  4. package/dist/core/ClaudeCliIntelligenceProvider.js.map +1 -1
  5. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  6. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  7. package/dist/core/PostUpdateMigrator.js +56 -2
  8. package/dist/core/PostUpdateMigrator.js.map +1 -1
  9. package/dist/core/crossModelReviewer.d.ts +188 -9
  10. package/dist/core/crossModelReviewer.d.ts.map +1 -1
  11. package/dist/core/crossModelReviewer.js +360 -18
  12. package/dist/core/crossModelReviewer.js.map +1 -1
  13. package/dist/core/types.d.ts +14 -0
  14. package/dist/core/types.d.ts.map +1 -1
  15. package/dist/core/types.js.map +1 -1
  16. package/dist/scaffold/templates.js +1 -1
  17. package/dist/server/routes.d.ts.map +1 -1
  18. package/dist/server/routes.js +15 -5
  19. package/dist/server/routes.js.map +1 -1
  20. package/package.json +1 -1
  21. package/scripts/model-registry-freshness.manifest.json +8 -0
  22. package/skills/spec-converge/SKILL.md +4 -2
  23. package/skills/spec-converge/scripts/cross-model-review.mjs +36 -6
  24. package/src/data/builtin-manifest.json +63 -63
  25. package/src/scaffold/templates.ts +1 -1
  26. package/upgrades/1.3.764.md +45 -0
  27. package/upgrades/1.3.765.md +63 -0
  28. package/upgrades/side-effects/actionclaim-config-shape-fix.eli16.md +50 -0
  29. package/upgrades/side-effects/actionclaim-config-shape-fix.md +149 -0
  30. package/upgrades/side-effects/reviewer-door-rewiring-inc1.md +198 -0
@@ -0,0 +1,63 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Added a third **spec-converge external reviewer family**: the strongest Anthropic model
9
+ (`claude-fable-5`) now reads a spec through the clean `claude -p` door — off the measured-penalized
10
+ `opus × coding-harness` pair (INSTAR-Bench v2: Opus-4.8-via-Claude-Code 81.7% vs clean-API 99.1%, a
11
+ 17.4-point door penalty). This is **inc1** of REVIEWER-DOOR-REWIRING, and ships **dark on the fleet /
12
+ live on a development agent** (the `specConverge.reviewers.anthropic.enabled` developmentAgent gate —
13
+ absent config keeps today's exact `[codex, gemini]` behavior byte-for-byte).
14
+
15
+ The reviewer is a **clean-door second read, NOT a cross-model opinion** — it carries `crossFamily:
16
+ false` and books into its own `clean-door-anthropic-review` disclosure field, so a claude-only round
17
+ can never launder the `cross-model-review` flag. Because a spec under review is untrusted ~60KB text,
18
+ the call is **hardened to codex-door parity**: empty allowed-tools (`--allowedTools ''`, an allow-list
19
+ not a denylist), `--strict-mcp-config`, a neutral scratch cwd, the prompt via stdin (never argv), an
20
+ env allowlist that strips agent secrets (`INSTAR_AUTH_TOKEN`), and a runtime fail-closed preflight
21
+ that degrades (never runs unhardened) if the installed CLI lacks the flags. The model argument is the
22
+ CONCRETE frontier pin — never the tier word `'capable'`, which resolves to opus.
23
+
24
+ ## What to Tell Your User
25
+
26
+ Nothing proactive — this is instar-developing-agent tooling that ships off on the fleet. If a user
27
+ asks why a spec-converge run now shows a "clean-door Anthropic review" line, or whether the strongest
28
+ Claude model reviews specs: on a development agent the spec is now also read by Fable 5 (the strongest
29
+ Claude model) through a clean door (separate from the six in-session reviewers), disclosed on its own line and never
30
+ counted as a cross-model opinion. It is dogfooded on the development agent first, before any wider
31
+ rollout; the "clean door" claim is scoped to *off the measured-penalized coding-harness door*, not
32
+ bench-verified-clean (that direct bench is a tracked follow-up).
33
+
34
+ ## Summary of New Capabilities
35
+
36
+ - **`--family claude-code` clean-door reviewer** in spec-converge — config-gated
37
+ (`specConverge.reviewers.anthropic.enabled`, dev-on / fleet-dark); an optional
38
+ `specConverge.reviewers.anthropic.model` override is validated against the frontier accept-set
39
+ (a non-frontier concrete id like `claude-opus-4-8` is rejected, never silently honored).
40
+ - **Cross-model honesty guard** — `crossFamily` is threaded onto the reviewer registry,
41
+ `ReviewerResult`, and `CrossModelDetectionResult` (fail-closed on an unknown id); the aggregate
42
+ flag, both detection paths, and the 7-day externals-mandatory baseline all filter on
43
+ `crossFamily: true`, so the claude family can never masquerade as cross-model.
44
+ - **Inbound-safety hardening** for reviewing untrusted spec text (empty allowed-tools,
45
+ strict-mcp-config, neutral cwd, stdin prompt, env allowlist), verified by a STATE-level
46
+ zero-tool-execution test against the real Claude CLI.
47
+ - No behavior change while the family stays dark (the fleet default); this only makes the reviewer
48
+ reachable on a development agent.
49
+
50
+ ## Evidence
51
+
52
+ - `tests/unit/crossModelReviewer-clean-door.test.ts` — detection (static presence reasons only),
53
+ model resolution (default pin, tier-word + non-frontier-override rejection), the §5 lockdown
54
+ battery (a–e), the required-`crossFamily`-field guard, per-family concrete-pin model-arg, and the
55
+ config-gate default (fleet-absent = exactly `[codex, gemini]`).
56
+ - `tests/unit/claude-reviewer-inbound-safety.test.ts` — hardened argv/env asserted deterministically
57
+ + a live-claude-gated STATE test proving a benign tool-invoking payload creates ZERO files.
58
+ - `tests/unit/model-registry-freshness-reviewer-pin.test.ts` — the freshness-lint tooth on the
59
+ reviewer pin is NOT vacuous (a rotted constant fails the strict lint).
60
+ - `tests/integration/clean-door-reviewer-driver.test.ts` — the driver `--family`/`--detect-only`
61
+ paths: clean-door flag shape, trusted-but-disabled refusal, family-present-only-when-enabled.
62
+ - `tests/unit/PostUpdateMigrator-anthropicReviewerDisclosure.test.ts` — the SKILL.md content
63
+ migration reaches already-installed agents (idempotent, fingerprint-guarded).
@@ -0,0 +1,50 @@
1
+ # ELI16 — Why the "I'll do that in 5 minutes" tracker couldn't be turned on
2
+
3
+ ## The one-sentence version
4
+
5
+ There's a safety feature that watches for promises the agent makes ("I'll restart it", "I'll post
6
+ that in about 5 minutes") and quietly writes them down so they survive a restart — and it turned out
7
+ you literally could not switch it on. This change fixes the switch.
8
+
9
+ ## What was broken
10
+
11
+ Think of the config file as a form. Most settings live in labeled boxes you can fill in. The switch
12
+ for this promise-tracker was written as "fill in the box `messaging → actionClaim → enabled`."
13
+
14
+ The problem: `messaging` isn't a box that holds other boxes. It's a **list** — one row per chat
15
+ platform (Telegram, Slack, WhatsApp). You can't write "actionClaim" *inside a list*; there's nowhere
16
+ to put it. So when the program looked for `messaging → actionClaim → enabled`, it always found
17
+ nothing, and "nothing" means "off." No matter what you did, the feature stayed off.
18
+
19
+ Why nobody noticed for so long: every automated test filled the form out the *wrong* way — it made
20
+ `messaging` a box instead of a list, so in the tests the switch worked fine. But no real install
21
+ looks like that. Real installs always use the list. So the tests were green while the real thing was
22
+ un-turn-on-able. Two sibling features have the same quirk, but they default to **on**, so nobody ever
23
+ had to find the switch — this promise-tracker is the first one that defaults to **off** and actually
24
+ needs its switch to work.
25
+
26
+ ## What this change does
27
+
28
+ It moves the switch to a box that actually exists: a **top-level `actionClaim`** setting, right
29
+ alongside the other real settings — not buried inside the platform list. The old (broken) location
30
+ is still honored if anyone used it, so nothing that already worked breaks.
31
+
32
+ Two places read that switch: the live server route that receives the promise, and a small "when a
33
+ turn finishes" hook script. Both now look in the new top-level spot first, and both are careful never
34
+ to trip over the list.
35
+
36
+ ## Why it's safe
37
+
38
+ - The feature is *signal-only*: it just writes a note; it never blocks or changes a message. So there
39
+ is no risk of it wrongly rejecting something.
40
+ - It still ships **off by default** — this change only makes the "on" switch reachable, it doesn't
41
+ turn anything on.
42
+ - Old configs keep working (the old location is a fallback), so it's backwards compatible.
43
+ - If it's ever wrong, the fix is a plain code revert — there's no saved data to clean up.
44
+
45
+ ## How we know it works now
46
+
47
+ A new test fills the form out the **real** way — `messaging` as a list — and flips the new top-level
48
+ switch, then checks that a promise like "I'll restart the server now" actually gets written down. It
49
+ fails without the fix and passes with it. Older tests (the ones using the box shape) still pass, so
50
+ the backwards-compatibility promise holds.
@@ -0,0 +1,149 @@
1
+ # Side-Effects Review — Action-Claim sentinel enablable on array-shaped `messaging`
2
+
3
+ **Version / slug:** `actionclaim-config-shape-fix`
4
+ **Date:** `2026-07-04`
5
+ **Author:** `Echo`
6
+ **Second-pass reviewer:** `not required (Tier-1)`
7
+
8
+ ## Summary of the change
9
+
10
+ The Action-Claim Follow-Through Sentinel (including the Slack-followthrough lane from #1361)
11
+ gated on the config path `messaging.actionClaim.enabled`. On every real install `messaging` is a
12
+ JSON **array** of adapter configs, so `messaging.actionClaim.*` is unreachable — `LiveConfig`'s
13
+ `getNestedValue` walks `messaging`, gets the array, evaluates `array['actionClaim']` → `undefined`,
14
+ and returns the `false` default. Because this sentinel defaults OFF, the master gate could never be
15
+ set true: the feature was **structurally un-enablable in production**. CI never caught it because
16
+ every test wrote an object-shaped `messaging`, which no real install uses. Fix: read the config from
17
+ a reachable **top-level `actionClaim`** block (canonical), honoring the legacy object-shaped
18
+ `messaging.actionClaim` as a back-compat fallback. Files: `src/server/routes.ts` (the
19
+ `/action-claim/observe` reads, via a small `acGet` live-read helper), `src/core/PostUpdateMigrator.ts`
20
+ (the generated Stop hook's raw-file resolution + the CLAUDE-template enable-key text),
21
+ `src/scaffold/templates.ts` (the scaffold CLAUDE-template enable-key text), plus tests.
22
+
23
+ ## Decision-point inventory
24
+
25
+ - `POST /action-claim/observe` config reads (`src/server/routes.ts`) — **modify** — master `enabled`,
26
+ `slack.enabled` dev-gate value, `slack.dryRun`, `perTopicCap`, `expiresHours` now resolve
27
+ top-level-first via `acGet`.
28
+ - Generated `action-claim-followthrough.js` Stop hook (`PostUpdateMigrator.getActionClaimFollowthroughHook`)
29
+ — **modify** — the raw-file `enabled` resolution reads top-level `cfg.actionClaim` and Array-guards
30
+ the legacy `cfg.messaging.actionClaim` fallback.
31
+ - The `messaging.actionClaim.slack.enabled` dev-gate (`resolveDevAgentGate`) — **pass-through** — value
32
+ now sourced via `acGet` but the `undefined → live-on-dev, dark-fleet` semantics are unchanged.
33
+
34
+ ---
35
+
36
+ ## 1. Over-block
37
+
38
+ No block/allow surface — over-block not applicable. The change only affects whether a
39
+ signal-only, never-blocking sentinel can be turned ON. An explicit top-level `actionClaim.enabled:false`
40
+ correctly keeps it off (covered by a test).
41
+
42
+ ## 2. Under-block
43
+
44
+ No block/allow surface — under-block not applicable. The sentinel remains dark by default (absent
45
+ config → `false`); the fix does not change detection precision, only config reachability.
46
+
47
+ ## 3. Level-of-abstraction fit
48
+
49
+ Right layer. This is a config-resolution fix at the exact read sites that were broken; it introduces
50
+ no new authority and reuses the existing `LiveConfig` getter and the raw-file read in the generated
51
+ hook. It does not re-implement config parsing — it adds a top-level-first fallback around the
52
+ existing getters.
53
+
54
+ ## 4. Signal vs authority compliance
55
+
56
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
57
+
58
+ - [x] No — this change has no block/allow surface. It changes config reachability for a signal-only
59
+ sentinel (the Stop hook always `exit(0)`; the observe route never blocks a message). No brittle
60
+ logic gains blocking authority.
61
+
62
+ ## 5. Interactions
63
+
64
+ - **Shadowing:** none. The `acGet` helper reads top-level first, then falls back to the old
65
+ `messaging.actionClaim` path with the same default — a superset of prior behavior. Object-shaped
66
+ configs (existing tests) resolve identically via the fallback.
67
+ - **Double-fire:** none. Single read path per field; no new emitter.
68
+ - **Races:** none. Reads are per-request via `LiveConfig` (live, no shared mutable state added).
69
+ - **Feedback loops:** none.
70
+
71
+ ## 6. External surfaces
72
+
73
+ - **Other agents / install base:** the generated Stop hook is regenerated on every migration
74
+ (`instar/` hooks are always-overwritten), so existing agents pick up the array-safe resolution on
75
+ their next update — no per-agent config migration required (a fresh top-level `actionClaim` enable
76
+ is purely additive).
77
+ - **External systems:** none changed. Slack/Telegram delivery paths untouched.
78
+ - **Persistent state:** none. No new ledger/column/file.
79
+ - **Operator surface:** no operator-facing action added — enabling is a config edit (dev-first soak),
80
+ same as before, now at a reachable key. "No operator-facing actions" — the CLAUDE-template text was
81
+ corrected so agents point operators at the reachable `actionClaim.enabled` key.
82
+
83
+ ## 6b. Operator-surface quality
84
+
85
+ No operator surface — not applicable. No dashboard renderer, approval page, or grant/secret form is
86
+ touched.
87
+
88
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
89
+
90
+ **machine-local BY DESIGN.** Config (`.instar/config.json`) is per-machine, and the Action-Claim
91
+ sentinel already runs per-machine on the machine that owns the responding session (it registers under
92
+ that session's own bind token — slack-followthrough-generalization §4.5). This change only alters
93
+ *where in the local config file* the enable flag is read; it introduces no cross-machine state, no
94
+ user-facing notice, no durable state, and no URLs. Enabling it on one machine does not and should not
95
+ implicitly enable it on another — each machine's config is authoritative for its own sessions.
96
+
97
+ ## 8. Rollback cost
98
+
99
+ Pure code change — revert and ship as a patch. No persistent state, no data migration, no user-visible
100
+ regression during the rollback window. The generated hook is regenerated on the next migration in
101
+ either direction. A deployed agent that had set top-level `actionClaim.enabled:true` would, on
102
+ rollback, simply return to un-enablable (the pre-fix state) — no cleanup needed.
103
+
104
+ ## Conclusion
105
+
106
+ The review confirms a low-risk, additive config-resolution fix that makes a previously un-enablable
107
+ default-off sentinel actually turnable-on on real (array-`messaging`) installs, with the legacy
108
+ object-shape fully back-compatible. It adds the missing test coverage (array-shaped `messaging`) that
109
+ would have caught the original bug. Clear to ship. Follow-up worth tracking: the sibling default-ON
110
+ `messaging.*` sentinels (`toneGate`, `outboundAdvisory`) share the same latent unreachability but are
111
+ masked by their on-by-default posture — noted here, not fixed in this change (out of scope).
112
+
113
+ ---
114
+
115
+ ## Second-pass review (if required)
116
+
117
+ **Reviewer:** not required (Tier-1)
118
+ **Independent read of the artifact: n/a**
119
+
120
+ ---
121
+
122
+ ## Evidence pointers
123
+
124
+ - `tests/integration/action-claim-config-shape.test.ts` — array-shaped `messaging` + top-level
125
+ `actionClaim`: enablement, tuning-knob read, explicit-false off-switch, object-shape back-compat.
126
+ - `tests/unit/action-claim-hook-slack.test.ts` — hook-body array-safe resolution assertion.
127
+ - Existing `tests/integration/action-claim-route.test.ts` (14) + `tests/e2e/action-claim-lifecycle.test.ts`
128
+ (4) + `tests/unit/generated-hooks-parse.test.ts` (25) + `tests/unit/migrate-actionclaim-slack-devgate.test.ts`
129
+ (5) all stay green; `tsc --noEmit` clean.
130
+ - `docs/investigations/s7-slack-delivery-repro-2026-07-04.md` — the S7 keystone investigation that
131
+ surfaced this bug (the merged #1361 fix could not be turned on).
132
+
133
+ ---
134
+
135
+ ## Class-Closure Declaration (display-only mirror)
136
+
137
+ - **`defectClass`** — `config-unreachable-on-shape` (`novel`; nearestExistingClass:
138
+ `feature-un-enablable`; includes: a config gate whose dot-path is unreachable given the container's
139
+ real runtime shape, e.g. a key nested under an array-valued parent; excludes: a feature disabled by a
140
+ correctly-read explicit flag). Enters `status: "unconfirmed"` pending operator confirmation, so this
141
+ fix carries `closure: gap` (below), not `closure: guard`.
142
+ - **`closure`** — `gap` — the class-level guard (a lint that flags a `messaging.<x>` dot-path read when
143
+ `messaging` is array-shaped in the shipped config, or a shape-fuzzing test harness for config gates)
144
+ is out of scope for this fix.
145
+ - **`guardEvidence`** — n/a for `closure: gap`.
146
+ - **`gap`** — tracked as a standards-gap follow-up: "config-gate shape-reachability lint/fuzz — a
147
+ default-off feature gated on an unreachable dot-path is un-enablable; add a guard that fails when a
148
+ gate reads `messaging.<child>.*` given `messaging` ships as an array." (This change ships the direct
149
+ regression test for THIS feature; the class-level guard is the tracked gap.)
@@ -0,0 +1,198 @@
1
+ # Side-Effects Review — Reviewer-Door Rewiring inc1 (Anthropic clean-door reviewer family)
2
+
3
+ **Version / slug:** `reviewer-door-rewiring-inc1`
4
+ **Date:** `2026-07-04`
5
+ **Author:** `echo (build hand)`
6
+ **Second-pass reviewer:** `not required (Tier-1: dark-on-fleet, config-reversible, no durable state / external side-effects; PR is the review surface)`
7
+
8
+ ## Summary of the change
9
+
10
+ inc1 of REVIEWER-DOOR-REWIRING adds a third spec-converge external reviewer family — the strongest
11
+ Anthropic model (`claude-fable-5`) read through the clean `claude -p` door, off the measured-penalized
12
+ `opus × coding-harness` pair. It ships **dark on the fleet / live on a development agent** via the
13
+ `specConverge.reviewers.anthropic.enabled` developmentAgent gate; absent config keeps today's exact
14
+ `[codex, gemini]` behavior byte-for-byte. The family is a clean-door SECOND READ (`crossFamily: false`)
15
+ that books into its own `clean-door-anthropic-review` disclosure field and can never launder the
16
+ `cross-model-review` flag. Files touched: `src/core/crossModelReviewer.ts` (detection, model
17
+ resolution, registry entry, config gate, crossFamily plumbing, baseline-predicate swap, aggregate
18
+ filter, hardening preflight), `src/core/ClaudeCliIntelligenceProvider.ts` (reviewer-hardening
19
+ invocation + exported arg/env builders), `src/core/types.ts` (`IntelligenceOptions.reviewerHardening`),
20
+ `scripts/model-registry-freshness.manifest.json` (reviewer pin), `skills/spec-converge/scripts/cross-model-review.mjs`
21
+ (config gate), `skills/spec-converge/SKILL.md` (family + D7 disclosure), `src/core/PostUpdateMigrator.ts`
22
+ (SKILL.md content migration), `docs/LLM-ROUTING-REGISTRY.md` (stale `report`→`strict` line), plus five
23
+ test files + the converged spec docs.
24
+
25
+ ## Decision-point inventory
26
+
27
+ - `SUPPORTED_REVIEWER_FRAMEWORKS` — **modify** — one new `claude-code` entry with REQUIRED `crossFamily: false`; codex remains the preference leader; existing ordering unchanged.
28
+ - `TRUSTED_REVIEWER_FRAMEWORKS` — **modify** — gains `claude-code`, COUPLED ATOMICALLY with the §5.4 baseline-predicate swap (`isTrustedReviewerFramework` → `isCrossFamilyReviewerFramework`).
29
+ - `ReviewerResult` / `CrossModelDetectionResult` — **modify** — gain a `crossFamily` field (populated from the registry; existing families byte-identical).
30
+ - `aggregateRoundOutcomes` / `detectCrossModelReviewer` / `detectAllCrossModelReviewers` / `wasNonClaudeFrameworkActiveWithin` — **modify** — gain `crossFamily` filtering (behavior for existing families byte-identical).
31
+ - `IntelligenceOptions.reviewerHardening` — **add** — the claude-provider inbound-safety lockdown option (other providers ignore it).
32
+ - `specConverge.reviewers.anthropic.{enabled,model}` config block — **add** — the developmentAgent gate + optional frontier-validated model override.
33
+ - No block/allow gate, no HTTP route, no scheduler job, no watcher is introduced or modified.
34
+
35
+ ---
36
+
37
+ ## 1. Over-block
38
+
39
+ **What legitimate inputs does this change reject that it shouldn't?**
40
+
41
+ No user-message block/allow surface — over-block not applicable. The one rejection path is the config
42
+ model-override validator: a concrete-but-non-frontier id (e.g. `claude-opus-4-8`) is rejected with
43
+ `override-not-frontier`. This is *intended* (it prevents re-pinning the reviewer to the penalized opus
44
+ door); a legitimate frontier id (`claude-fable-5`) is accepted, and the default pin needs no override.
45
+ The `--family claude-code` refusal on the fleet (`no-supported-framework`) is intended dark-gating,
46
+ not over-block.
47
+
48
+ ---
49
+
50
+ ## 2. Under-block
51
+
52
+ **What illegitimate inputs does this change let through that it shouldn't?**
53
+
54
+ The load-bearing under-block risk is the untrusted spec text reaching a tool/MCP execution path via the
55
+ `claude -p --setting-sources user` door (which loads user hooks + MCP servers and inherits full env).
56
+ §1.4 hardening closes this: empty allowed-tools + `--strict-mcp-config` + neutral scratch cwd + stdin
57
+ prompt + env allowlist, plus a fail-closed runtime preflight (`hardening-unsupported` → degrade, never
58
+ run unhardened). Verified by a STATE-level zero-tool-execution test against the real Claude CLI (a
59
+ benign tool-invoking payload creates NO file). The cross-model-honesty under-block (a claude-only run
60
+ masquerading as cross-model) is closed structurally by `crossFamily`-keyed filtering, fail-closed on an
61
+ unknown id, unit-locked.
62
+
63
+ ---
64
+
65
+ ## 3. Level-of-abstraction fit
66
+
67
+ **Is the change at the right layer?**
68
+
69
+ Yes. The reviewer family is a registry entry in the existing `SUPPORTED_REVIEWER_FRAMEWORKS` seam (the
70
+ established extension point); the hardening is invocation options on the existing
71
+ `ClaudeCliIntelligenceProvider` (no new adapter class, no `IntelligenceFramework` union change); the gate
72
+ rides the standard `resolveDevAgentGate` funnel; the anti-rot pin rides the existing freshness-lint
73
+ manifest. No new subsystem, no new abstraction — the change reuses every existing seam.
74
+
75
+ ---
76
+
77
+ ## 4. Signal vs authority compliance
78
+
79
+ **Does anything here gain blocking authority it shouldn't?**
80
+
81
+ No. Every reviewer (internal, external, clean-door) remains a SIGNAL into convergence synthesis; no
82
+ pass gains blocking authority; a degraded/unavailable family degrades loudly and convergence proceeds.
83
+ The config gate governs *availability of a signal source*, not any authority. The `crossFamily` guard
84
+ is a classification of what a signal COUNTS AS, not a block. The hardening preflight degrades (a signal
85
+ outcome), never blocks.
86
+
87
+ ---
88
+
89
+ ## 5. Interactions
90
+
91
+ **What existing features does this change interact with, and how?**
92
+
93
+ - **Freshness lint** (`scripts/lint-model-registry-freshness.mjs`) — the new `claude-clean-door-reviewer-default`
94
+ pin is checked under strict enforcement; verified green + non-vacuity-tested.
95
+ - **subscription pool / circuit breaker / spawn cap** — the claude reviewer rides `buildIntelligenceProvider`,
96
+ so it inherits the spawn-cap funnel + per-framework breaker; quota pressure surfaces as `degraded:
97
+ rate-limited`. Quota-correlation honesty: the claude family draws on the SAME Anthropic pool as the
98
+ authoring session (called out in the spec, degrades loudly).
99
+ - **7-day externals-mandatory baseline** — the predicate swap keeps the mandatory check keyed on
100
+ cross-model families only; a claude-only activation never satisfies it (unit-locked).
101
+ - **PostUpdateMigrator** — the SKILL.md content migration reaches already-installed agents.
102
+
103
+ ---
104
+
105
+ ## 6. External surfaces
106
+
107
+ **Does this change touch any external service, network call, filesystem path outside the project, or spawn?**
108
+
109
+ - **Egress:** the claude reviewer sends the spec text to Anthropic (or the operator's OWN configured
110
+ `ANTHROPIC_BASE_URL` proxy) — the SAME destination the authoring session already uses. ZERO new
111
+ egress destinations. No third-party aggregator (OpenRouter declined, §2).
112
+ - **Spawn:** one `claude -p` one-shot per round per available family (≤10 rounds), run in a neutral
113
+ mkdtemp scratch cwd with an allowlist env. The `--help` preflight is a cheap one-shot, cached
114
+ per-process.
115
+ - **Filesystem:** the reviewer pin adds a row to the in-repo freshness manifest. No paths outside the
116
+ project are written; the scratch cwd is under `os.tmpdir()`.
117
+
118
+ ---
119
+
120
+ ## 6b. Operator-surface quality (Operator-Surface Quality standard)
121
+
122
+ No new operator-facing surface (no HTTP route, no dashboard tab, no user-facing config the operator
123
+ edits conversationally). The `specConverge.reviewers.*` config is instar-developing-agent tooling set
124
+ per-machine by a maintainer, documented in the spec + SKILL.md + the release fragment. Not applicable
125
+ beyond that.
126
+
127
+ ---
128
+
129
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
130
+
131
+ - **Reviewer availability (which families detect on this machine)** — machine-local BY DESIGN.
132
+ machine-local-justification: physical-credential-locality — each family's door is a per-disk CLI
133
+ login (claude OAuth config-home, `~/.codex/auth.json`, `~/.gemini/oauth_creds.json`); reachability
134
+ cannot replicate without replicating credentials, which is forbidden.
135
+ - **Config (`specConverge.reviewers.*`)** — machine-local.
136
+ machine-local-justification: physical-credential-locality — `.instar/config.json` has no replication
137
+ path in instar (not one of the stateSync stores), and the config selects which per-disk CLI door
138
+ (a machine-local credentialed login) the reviewer uses. Enabling the family on more than one machine
139
+ is a per-machine edit.
140
+ - **`state/framework-activation-history.jsonl`** — existing surface, unchanged; already machine-local
141
+ for the same reason (it records THIS machine's detections).
142
+ machine-local-justification: physical-credential-locality — a record of per-disk login availability.
143
+ - Convergence runs on one machine per run; no cross-machine notice, URL, or durable-topic surface is
144
+ added. No `unified` surface is infeasible-and-claimed (bidirectional check clear).
145
+
146
+ ---
147
+
148
+ ## 8. Rollback cost
149
+
150
+ **How hard is it to undo this change if it goes wrong?**
151
+
152
+ Trivial. The family ships dark on the fleet (absent config = byte-identical `[codex, gemini]`). Rollback
153
+ is a config flip (`specConverge.reviewers.anthropic.enabled: false`) or a revert of a docs/constants-only
154
+ change. No data migration, no durable state, no external side-effects. The freshness manifest `enforcement`
155
+ has its own documented `strict`→`report` rollback. The SKILL.md migration is idempotent + fingerprint-guarded
156
+ (a customized skill is never touched).
157
+
158
+ ---
159
+
160
+ ## Conclusion
161
+
162
+ **Ship / hold / needs second pass:** Ship (Tier-1). The change is dark-on-fleet, fully config-reversible,
163
+ adds no durable state or new external destination, and the two load-bearing risks (untrusted-text tool
164
+ execution; cross-model-flag laundering) are closed structurally and verified by tests — including a live
165
+ STATE-level zero-tool-execution proof against the real Claude CLI. Operator approval of the converged
166
+ spec (`approved: true`) is deliberately left to the operator per the run mandate.
167
+
168
+ ---
169
+
170
+ ## Second-pass review (if required)
171
+
172
+ Not required — Tier-1 (dark-on-fleet, config-reversible, no durable/external side-effects). The PR is the
173
+ review surface. Note for the operator: this increment implements a CONVERGED-but-not-yet-approved spec;
174
+ if a Tier-2 re-land is desired post-approval, the trace can be re-cut with `--spec` + the approved tag.
175
+
176
+ ---
177
+
178
+ ## Evidence pointers
179
+
180
+ - `tests/unit/crossModelReviewer-clean-door.test.ts` (28 tests) — detection, model resolution, §5
181
+ lockdown battery (a–e), required-crossFamily-field guard, per-family concrete-pin model-arg, config gate.
182
+ - `tests/unit/claude-reviewer-inbound-safety.test.ts` (7 tests, incl. live STATE) — hardened argv/env +
183
+ zero-tool-execution against the real Claude CLI.
184
+ - `tests/unit/model-registry-freshness-reviewer-pin.test.ts` (3 tests) — non-vacuity of the reviewer pin.
185
+ - `tests/integration/clean-door-reviewer-driver.test.ts` (7 tests) — driver `--family`/`--detect-only` paths.
186
+ - `tests/unit/PostUpdateMigrator-anthropicReviewerDisclosure.test.ts` (4 tests) — SKILL.md content migration.
187
+ - `node scripts/lint-model-registry-freshness.mjs` → PASS (strict). `node cross-model-review.mjs --detect-only`
188
+ → `[codex, gemini]` (fleet-absent config, dark).
189
+
190
+ ---
191
+
192
+ ## Class-Closure Declaration (display-only mirror)
193
+
194
+ This change closes ONE instance of a class (a per-provider clean-door reviewer). The generalization —
195
+ a structured `{ provider, door, signalKind }` reviewer descriptor replacing the `crossFamily` boolean —
196
+ is tracked as a deferral (spec §8.5) for when a future reviewer breaks the binary Claude/non-Claude
197
+ taxonomy or the one-door-per-framework assumption. The paid-Gemini-key door + the direct `claude -p`
198
+ bench are tracked deferrals (spec §8.1/§8.3), registered as an evolution action at merge.