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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.763",
3
+ "version": "1.3.765",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -91,6 +91,14 @@
91
91
  "file": "src/core/ModelTierEscalation.ts",
92
92
  "regex": "'claude-code':\\s*\\{\\s*default:\\s*'([^']+)',\\s*escalated:\\s*'([^']+)'",
93
93
  "note": "DEFAULT_TIER_ESCALATION.frameworks['claude-code'] default + escalated ids."
94
+ },
95
+ {
96
+ "id": "claude-clean-door-reviewer-default",
97
+ "door": "claude-code",
98
+ "tier": "capable",
99
+ "file": "src/core/crossModelReviewer.ts",
100
+ "regex": "CLAUDE_REVIEWER_DEFAULT_MODEL\\s*=\\s*'((?:claude|gpt|gemini)-[^']+)'",
101
+ "note": "CLAUDE_REVIEWER_DEFAULT_MODEL — the Anthropic clean-door reviewer pin (REVIEWER-DOOR-REWIRING §1.3). Extraction matches a frontier CONSTANT (not a capable:'…' tier decl), so the non-vacuity test (tests/unit/model-registry-freshness-reviewer-pin.test.ts) rots this constant and asserts the strict lint fails — closing the vacuous-tooth risk."
94
102
  }
95
103
  ],
96
104
  "$flaggedStaleNote": "Both prior flagged pins RESOLVED (operator-confirmed 2026-07-03): gemini capable swapped gemini-2.5-pro -> gemini-3.1-pro-preview (pin + topModels updated in the same change); codex capable stays gpt-5.5 (GA flagship — gpt-5.6-sol is preview-only/gov-gated/unreachable, NOT pinned). Nothing pending confirmation.",
@@ -115,12 +115,14 @@ The external "cross-model" pass is a set of independent non-Claude reads that si
115
115
  --spec <spec-path> --family gemini-cli \
116
116
  --context docs/foo.md --context docs/bar.md
117
117
  ```
118
- Each emits a JSON `ReviewerResult` on stdout: `{ status, framework?, model?, verdict?, findings?, reason?, flag }`. Fold each `findings` into the round alongside the internal reviewers'. `status:'degraded'` (framework present but the call failed — timeout / error / rate-limited) is a *partial* cross-model pass for the round: fold in whatever came back and record the `degraded` flag — it does **not** collapse to `unavailable`. (Omitting `--family` keeps the old single-pass first-match behavior for existing callers.)
118
+ Each emits a JSON `ReviewerResult` on stdout: `{ status, framework?, model?, verdict?, findings?, reason?, flag, crossFamily }`. Fold each `findings` into the round alongside the internal reviewers'. `status:'degraded'` (framework present but the call failed — timeout / error / rate-limited) is a *partial* cross-model pass for the round: fold in whatever came back and record the `degraded` flag — it does **not** collapse to `unavailable`. (Omitting `--family` keeps the old single-pass first-match behavior for existing callers.)
119
+
120
+ **Anthropic clean-door reviewer (`--family claude-code`) — a clean-door SECOND READ, NOT a cross-model opinion (REVIEWER-DOOR-REWIRING).** A third family reads the spec through the clean `claude -p` door on the strongest model (`claude-fable-5`), off the measured-penalized `opus × coding-harness` pair. It is **config-gated** (`specConverge.reviewers.anthropic.enabled` — omitted ⇒ live on a development agent, DARK on the fleet) and its result carries `crossFamily: false`, so it books into its OWN disclosure field **`clean-door-anthropic-review: claude-code:<model>`** — NEVER the `cross-model-review:` flag. A claude-only round therefore aggregates to `degraded-all-rounds`/`unavailable` exactly as before; the clean-door read can never launder a cross-model pass. Run it (when enabled) exactly like the others: `--family claude-code`. **D7 per-round-model disclosure:** the six INTERNAL reviewers run on the authoring session's model (opus, or `claude-fable-5` under tier-escalation — a quota-refused escalation silently drops them to opus-via-harness); record the ACTUAL per-round session/subagent model in the iteration log so a silent drop to opus is VISIBLE, not assumed.
119
121
 
120
122
  Two structural guards on the pass:
121
123
 
122
124
  - **Fail-loud model canary.** Model selection is dynamic (the `'capable'` tier resolved per framework — never a pinned name), and a tier word that falls through resolution (`'capable'` is not a model) is caught by `isConcreteReviewerModel` BEFORE the provider is invoked: the round degrades loudly with reason `model-resolution-canary` instead of silently selecting a dead reviewer.
123
- - **Trusted-provider allowlist.** `--family` only accepts frameworks on `TRUSTED_REVIEWER_FRAMEWORKS` (`codex-cli`, `gemini-cli` — first-party OAuth CLI adapters). The full spec text is handed to the reviewer, so it must NEVER be sent to a custom/base-URL endpoint; **pi-cli is deliberately excluded** from cross-model review for this reason (its provider may be a custom endpoint). An untrusted family is refused with `reason: 'untrusted-framework'`.
125
+ - **Trusted-provider allowlist.** `--family` only accepts frameworks on `TRUSTED_REVIEWER_FRAMEWORKS` (`codex-cli`, `gemini-cli`, `claude-code` — first-party OAuth CLI adapters). The full spec text is handed to the reviewer, so it must NEVER be sent to a custom/base-URL endpoint; **pi-cli is deliberately excluded** from cross-model review for this reason (its provider may be a custom endpoint). An untrusted family is refused with `reason: 'untrusted-framework'`; a trusted-but-config-disabled family (the `claude-code` clean-door family on the fleet) is refused with `reason: 'no-supported-framework'` (trusted-egress ≠ enabled). The `claude-code` clean-door call is additionally hardened to codex-door parity for the untrusted spec text (empty allowed-tools, `--strict-mcp-config`, neutral scratch cwd, stdin prompt, env allowlist) — a spec under review is untrusted inbound text.
124
126
 
125
127
  The detection + invocation + parsing live in the unit-tested `src/core/crossModelReviewer.ts` module (built to `dist/core/crossModelReviewer.js`); the script is a thin file-I/O wrapper. codex and gemini are the supported frameworks in an extensible registry (`SUPPORTED_REVIEWER_FRAMEWORKS`) — further first-party CLIs plug in there with **no skill change**.
126
128
 
@@ -125,9 +125,33 @@ function readRepoFile(rel) {
125
125
  return fs.readFileSync(abs, 'utf-8');
126
126
  }
127
127
 
128
+ /**
129
+ * Read the minimal reviewer config the module needs (REVIEWER-DOOR-REWIRING
130
+ * §1.5): the developmentAgent gate flag + the `specConverge.reviewers` block, so
131
+ * the Anthropic clean-door family resolves live-on-dev / dark-fleet. Best-effort:
132
+ * a missing/unparseable `.instar/config.json` ⇒ `{}` ⇒ fleet-dark (byte-identical
133
+ * `[codex, gemini]`). `.instar/config.json` is machine-local (no config
134
+ * replication in instar) — enabling the family is a per-machine edit.
135
+ */
136
+ function loadReviewerConfig() {
137
+ try {
138
+ const cfgPath = path.join(ROOT, '.instar', 'config.json');
139
+ if (!fs.existsSync(cfgPath)) return {};
140
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
141
+ const out = {};
142
+ if (typeof cfg.developmentAgent === 'boolean') out.developmentAgent = cfg.developmentAgent;
143
+ if (cfg.specConverge && typeof cfg.specConverge === 'object') out.specConverge = cfg.specConverge;
144
+ return out;
145
+ } catch {
146
+ return {};
147
+ }
148
+ }
149
+
128
150
  async function main() {
129
151
  const { spec, context, detectOnly, hashOnly, family, stateDir, timeoutMs } = parseArgs();
130
152
  const mod = await loadModule();
153
+ // Config-gate the Anthropic clean-door family (§1.5). Absent config ⇒ fleet-dark.
154
+ const reviewerConfig = loadReviewerConfig();
131
155
 
132
156
  // ── --hash-only: the delta-gating hash of the spec's reviewable body ──
133
157
  if (hashOnly) {
@@ -139,9 +163,9 @@ async function main() {
139
163
 
140
164
  // ── --detect-only: report ALL available families (Piece 3) ──
141
165
  if (detectOnly) {
142
- const all = mod.detectAllCrossModelReviewers();
166
+ const all = mod.detectAllCrossModelReviewers({}, reviewerConfig);
143
167
  // Back-compat: keep the old single-framework fields (first-match shape).
144
- const first = mod.detectCrossModelReviewer();
168
+ const first = mod.detectCrossModelReviewer({}, reviewerConfig);
145
169
  const report = {
146
170
  available: all.length > 0,
147
171
  frameworks: all,
@@ -152,10 +176,11 @@ async function main() {
152
176
  // Record the activation observation into the durable standing-framework
153
177
  // baseline when a state dir was provided. A record failure is surfaced in
154
178
  // the JSON (fail-loud), never silently swallowed — a missing baseline
155
- // would quietly weaken the externals-mandatory check.
179
+ // would quietly weaken the externals-mandatory check. Iterate the ACTIVE
180
+ // set so a config-disabled claude family is not recorded as present.
156
181
  if (stateDir) {
157
182
  const frameworks = {};
158
- for (const entry of mod.SUPPORTED_REVIEWER_FRAMEWORKS) {
183
+ for (const entry of mod.resolveActiveReviewerFrameworks(reviewerConfig)) {
159
184
  frameworks[entry.id] = all.some((d) => d.framework === entry.id);
160
185
  }
161
186
  try {
@@ -186,7 +211,10 @@ async function main() {
186
211
  );
187
212
  process.exit(0);
188
213
  }
189
- familyEntry = mod.SUPPORTED_REVIEWER_FRAMEWORKS.find((f) => f.id === family) ?? null;
214
+ // Config gate (§1.5): a trusted-but-disabled family (the claude clean-door
215
+ // family on the fleet) is refused here — trusted-egress ≠ enabled.
216
+ const active = mod.resolveActiveReviewerFrameworks(reviewerConfig);
217
+ familyEntry = active.find((f) => f.id === family) ?? null;
190
218
  if (!familyEntry) {
191
219
  process.stdout.write(
192
220
  JSON.stringify({
@@ -199,7 +227,7 @@ async function main() {
199
227
  }
200
228
  }
201
229
 
202
- const detection = familyEntry ? familyEntry.detect() : mod.detectCrossModelReviewer();
230
+ const detection = familyEntry ? familyEntry.detect() : mod.detectCrossModelReviewer({}, reviewerConfig);
203
231
 
204
232
  // Unavailable → print the unavailable flag, exit 0. Never block.
205
233
  if (!detection.available) {
@@ -227,9 +255,11 @@ async function main() {
227
255
  promptText: assembled.promptText,
228
256
  timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : mod.REVIEW_TIMEOUT_MS,
229
257
  detectionOverride: detection,
258
+ reviewerConfig,
230
259
  })
231
260
  : await mod.runCrossModelReview({
232
261
  assembled,
262
+ config: reviewerConfig,
233
263
  ...(Number.isFinite(timeoutMs) ? { timeoutMs } : {}),
234
264
  });
235
265