instar 1.3.1068 → 1.3.1069

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.1068",
3
+ "version": "1.3.1069",
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",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-07-30T00:20:21.582Z",
5
- "instarVersion": "1.3.1068",
4
+ "generatedAt": "2026-07-30T02:09:04.451Z",
5
+ "instarVersion": "1.3.1069",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -1530,7 +1530,7 @@
1530
1530
  "type": "subsystem",
1531
1531
  "domain": "server",
1532
1532
  "sourcePath": "src/server/AgentServer.ts",
1533
- "contentHash": "d8daf8fbc106429fd99554b0dd5c4ec76ad2688caaeb416d35c439998d7ed81c",
1533
+ "contentHash": "894e0598284292440c21230f87a3b9cf2a71b27ccc5f0349fb5aecb1c2f2edb0",
1534
1534
  "since": "2025-01-01"
1535
1535
  },
1536
1536
  "subsystem:session-manager": {
@@ -2,5 +2,5 @@
2
2
  "sha256": "b75c147afd1f5a9843ca4eac372159f3623f24731fe63b2f59e48043ae488af9",
3
3
  "articleCount": 82,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1068"
5
+ "packageVersion": "1.3.1069"
6
6
  }
@@ -0,0 +1,91 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ `ClaimClauseArbiter` asked every call for a two-part envelope — `legacy` (the clause labelling the
9
+ system consumes) and `general` (an experimental extraction of up to 4 factual claims, ~20 fields
10
+ each, with byte offsets and 8–11-option enums).
11
+
12
+ But the call site admits `general` only on one framework:
13
+
14
+ ```
15
+ const general = resolvedModel?.framework === 'claude-code' ? parsedGeneral : null;
16
+ ```
17
+
18
+ The prompt was never told about that rule. On an install whose internal components route off
19
+ Claude — which is now the shipped default (`provider-fallback-default-policy`) — the model was
20
+ asked for `general`, spent real latency and tokens producing it, and the result was discarded
21
+ 100% of the time.
22
+
23
+ The knock-on effect is what makes it a fault rather than only waste: generating the discarded
24
+ half regularly pushed the call past its own `timeoutMs: 60_000`, which killed the `legacy` half
25
+ the system actually uses.
26
+
27
+ Now the framework is resolved **before** the prompt is built, via the router's existing
28
+ `for(component)` resolver, and `general` is requested only when that resolves to `claude-code`.
29
+
30
+ - **Claude-routed installs are byte-identical.** `includeGeneral` defaults to `true`; only a
31
+ positively-resolved, non-empty, non-`claude-code` string suppresses it.
32
+ - **Every uncertainty fails toward the old behaviour** — resolver absent, returning `undefined`,
33
+ returning a non-string, or throwing all send the full prompt. Suppressing a shipped (if dark)
34
+ extractor on a guess is the one outcome worth avoiding, so nothing guesses.
35
+ - **The two prompt shapes get distinct ids.** `CLAIM_ARBITER_PROMPT_ID_LEGACY_ONLY` keeps
36
+ per-prompt latency/quality attribution from blending a fast small prompt with a slow large one.
37
+ - **`CLAIM_ARBITER_COMPONENT` is shared** between the resolver and the call's attribution, so the
38
+ gate can never consult a different routing entry than the call bills to.
39
+
40
+ The admission rule itself is unchanged, byte for byte. This changes what is *asked for*, never
41
+ what is *accepted*.
42
+
43
+ ## What to Tell Your User
44
+
45
+ If your agent's internal checks run on a non-Claude model — the shipped default — one background
46
+ check was quietly asking for a large answer it then threw away, and the cost of producing it was
47
+ making that check time out and fail about 83% of the time. It now asks only for the part it uses.
48
+ Nothing changes for agents routed to Claude.
49
+
50
+ ## Summary of New Capabilities
51
+
52
+ None. This removes waste from an existing check; no new surface, route, config key, or capability.
53
+
54
+ ## Evidence
55
+
56
+ Measured on a live two-agent machine, 2026-07-29, before writing the change.
57
+
58
+ **Production symptom** (`/metrics/features`, 24h, `completion-claim-verify`): 1,207 calls,
59
+ **956 errors (83.5%)**, 62 shed, 189 succeeded. `p50 49,315 ms`, `p95 60,082 ms`, `max 64,966 ms`
60
+ against a 60,000 ms wall. Input 2,430,258 tokens; output 462,638. 964 of the 1,207 calls ran on
61
+ `codex-cli` — the path where `general` is discarded.
62
+
63
+ **Controlled A/B**, interleaved A,B,A,B,A,B so a slow patch on the door could not be attributed
64
+ to one variant. Same model (`gpt-5.4-mini`), same door, same message, clauses and evidence.
65
+
66
+ | Variant | n | Median | Range | Avg output tokens | Inside the 60s wall |
67
+ |---|---|---|---|---|---|
68
+ | Full (`legacy`+`general`) | 3 | **129,204 ms** | 91,520 – 148,719 | 8,338 | **0 / 3** |
69
+ | Legacy-only | 3 | **28,153 ms** | 24,965 – 33,708 | 1,386 | **3 / 3** |
70
+
71
+ **4.6× median latency, 6.0× output tokens, and the ranges do not overlap.**
72
+
73
+ **Correctness, not only speed.** The legacy-only reply was confirmed valid JSON matching the
74
+ legacy schema and materially correct: it labelled *"Restart whenever suits you"* as `neither`
75
+ (*"imperative to the user, not a commitment or completion assertion"*) and flagged
76
+ *"Everything green"* as `corroborated: false` (*"no independent test evidence is provided here"*).
77
+ So this is a cost removal, not a capability trade.
78
+
79
+ **Test discrimination.** The new tests were run against the unfixed source with only the test
80
+ staged: **6 failed / 4 passed**. The 4 passes are labelled `CONTROL` and pass on both revisions by
81
+ design — two of them only revealed themselves as non-discriminating *because* that unfixed run was
82
+ performed. Only the 6 failures are counted as evidence.
83
+
84
+ ## Honest limits
85
+
86
+ - n=3 per arm. The conclusion rests on the non-overlap and the size of the gap, not on sample count.
87
+ - Timings came from a direct `codex exec` invocation rather than through instar's provider
88
+ (different sandbox/env/out-dir), so **absolute** numbers may differ in production. The **ratio**
89
+ is the load-bearing result and it is controlled.
90
+ - The expected drop in that 83.5% error rate is a **prediction until the shipped number moves**,
91
+ and is deliberately not claimed as proven. <!-- tracked: CMT-1118 -->
@@ -0,0 +1,206 @@
1
+ # Side-effects review — gate the `general` envelope on the pre-call resolved framework
2
+
3
+ **Change:** `ClaimClauseArbiter` asks for the `general` claim-extraction envelope only when the
4
+ component's resolved framework is `claude-code` — the sole framework whose `general` result the
5
+ call site can admit.
6
+ **Files:** `src/monitoring/ClaimClauseArbiter.ts`, `src/monitoring/CompletionClaimVerifier.ts`,
7
+ `tests/unit/claim-arbiter-general-gating.test.ts`
8
+ **Tracked:** CMT-1118. Escalates ACT-1466 (filed 2026-07-28 as a coverage gap) with its cost consequence.
9
+ **Tier declared:** 1 — small (2 source files, ~45 net LOC), no new capability, no migration, no
10
+ irreversibility, no safety-invariant change. Risk floor unchanged.
11
+
12
+ ## Phase 1 — Principle check (signal vs authority)
13
+
14
+ **Does this change touch a decision point that gates information flow, blocks actions, filters
15
+ messages, or constrains agent behaviour?** No — and the distinction matters.
16
+
17
+ The admission rule is untouched. `const general = resolvedModel?.framework === 'claude-code' ?
18
+ parsedGeneral : null` is byte-identical before and after. What changes is only what we **ask the
19
+ model to generate**, not what the system **accepts**. No authority is added, moved, or widened;
20
+ if anything the change removes generation work whose product was already unreachable.
21
+
22
+ `CompletionClaimVerifier` is observe-only by construction (per the constitution: claim
23
+ verification "never blocks, rewrites, delays, sends, corrects, or authorizes an action"), so
24
+ there is no blocking authority anywhere on this path for brittle logic to attach to.
25
+
26
+ **Verdict: compliant.** This is a cost reduction inside a signal producer.
27
+
28
+ ## Phase 4 — The eight questions
29
+
30
+ ### 1. Over-block — what legitimate inputs does this reject that it shouldn't?
31
+
32
+ The failure mode is suppressing `general` on an install where it **would** have been admitted.
33
+ Every uncertain path is biased against that. `includeGeneral` starts `true` and only a
34
+ positively-resolved, non-empty, non-`claude-code` string flips it:
35
+
36
+ - no `resolveFramework` injected → full prompt (this is also the default for every existing
37
+ caller that constructs the arbiter directly, e.g. tests and `opts.arbiter` overrides)
38
+ - resolver returns `undefined` / `''` / a non-string → full prompt
39
+ - resolver throws → caught, full prompt
40
+ - provider is not a router (no `for()` method) → resolver is `undefined` → full prompt
41
+
42
+ So over-blocking requires the router to positively and correctly report a non-Claude framework —
43
+ which is precisely the case where the result could not have been admitted anyway. **No issue
44
+ identified.**
45
+
46
+ ### 2. Under-block — what failure modes does this still miss?
47
+
48
+ Two, both known and both leaving today's behaviour intact rather than degrading it:
49
+
50
+ - **Mid-call failure-swap.** The framework is resolved before the call; a runtime swap can land
51
+ the request on a different framework than predicted. Claude-predicted → codex-actual keeps
52
+ today's waste (no regression). Codex-predicted → Claude-actual sends the lean prompt to Claude,
53
+ so `general` is simply absent — and the call site already tolerates absence:
54
+ `envelopeIncludesGeneral = Object.hasOwn(modelRoot, 'general')` only fails when `general` is
55
+ *present but unparseable*. Verified by reading that branch, not assumed.
56
+ - **The 83%→lower error-rate improvement is a prediction, not a measurement.** The latency and
57
+ token ratios are measured; the production error rate moving is not yet observed. Recorded here
58
+ rather than claimed. Closing evidence is owed on CMT-1118 <!-- tracked: CMT-1118 -->.
59
+
60
+ ### 3. Level-of-abstraction fit
61
+
62
+ The gate belongs at the prompt-construction site because that is where the cost is incurred and
63
+ where the admission rule's twin already lives — the two are now adjacent and reference each other,
64
+ so they cannot drift apart unnoticed. Pushing it lower (into the provider) would make the provider
65
+ aware of one component's schema; pushing it higher (into routing config) would make an operator
66
+ maintain a duplicate of a rule the code already enforces. **Right layer.**
67
+
68
+ A smarter gate does not already exist for this: nothing else inspects what this component asks for.
69
+
70
+ ### 4. Signal vs authority compliance
71
+
72
+ Compliant — see Phase 1. No brittle logic acquires blocking authority; the one existing authority
73
+ (the `claude-code` admission check) is unchanged.
74
+
75
+ ### 5. Interactions — shadowing, double-fire, races
76
+
77
+ - **Prompt-id attribution.** The two prompt shapes have materially different latency and output
78
+ size. Recording both under one id would blend them in per-prompt quality/latency attribution and
79
+ make the decision-quality meter quietly wrong about whichever install it looked at. A distinct
80
+ `CLAIM_ARBITER_PROMPT_ID_LEGACY_ONLY` prevents that. This is the interaction most likely to have
81
+ been missed.
82
+ - **Component-name drift.** The resolver must ask about the *same* component the call attributes
83
+ to. If they diverged, the gate would consult the wrong routing entry and mis-gate silently, with
84
+ nothing visibly wrong. Both now read the shared `CLAIM_ARBITER_COMPONENT` constant, making the
85
+ drift structurally impossible rather than merely unlikely.
86
+ - **No shadowing or double-fire:** nothing else builds this prompt; the arbiter has one call site.
87
+
88
+ ### 6. External surfaces
89
+
90
+ No API, route, config key, or user-visible surface changes. The only externally-observable deltas
91
+ are (a) a smaller prompt on non-Claude installs and (b) a second `promptId` value appearing in
92
+ provenance records. Nothing another agent, user, or system consumes by name is removed or renamed.
93
+
94
+ ### 7. Multi-machine posture (Cross-Machine Coherence)
95
+
96
+ **Machine-local BY DESIGN, and correctly so.** The decision is derived per-call from that machine's
97
+ own resolved routing (`sessions.componentFrameworks`), which is legitimately per-machine — a pool
98
+ can genuinely run Claude on one machine and codex on another, and each machine should ask for
99
+ exactly what its own door can deliver. Nothing durable is written, so there is no state to
100
+ replicate, strand on topic transfer, or merge on read. No user-facing notice, so no one-voice
101
+ gating needed. No generated URLs.
102
+
103
+ ### 8. Rollback cost
104
+
105
+ Near-zero, and available three ways without a release: stop injecting `resolveFramework` at the
106
+ construction site (one line), or route the component to `claude-code` via
107
+ `sessions.componentFrameworks`, or revert the commit. No data migration, no agent-state repair,
108
+ no schema change. Nothing persists that a rollback would leave inconsistent, except historical
109
+ provenance rows carrying the legacy-only prompt id — which remain accurate for the calls they
110
+ describe.
111
+
112
+ ## Phase 4.5 — No-deferrals
113
+
114
+ No orphan deferrals. The one outstanding item (confirming the production error rate actually
115
+ falls) is tracked on CMT-1118 <!-- tracked: CMT-1118 --> and is evidence-gathering on shipped
116
+ behaviour, not deferred work.
117
+
118
+ ## Phase 5 — Second-pass review: NOT REQUIRED, with reasoning
119
+
120
+ Checked against the skill's own high-risk list. This change touches **none** of: block/allow
121
+ decisions on inbound/outbound messaging or dispatch (the admission rule is unchanged and the
122
+ component cannot block); session lifecycle; context exhaustion/compaction/respawn; coherence
123
+ gates, idempotency, or trust levels; nor anything named sentinel/guard/gate/watchdog.
124
+
125
+ Declared `--second-pass not-required`. Stated plainly because the analogous judgement was made on
126
+ PR #1749 earlier tonight and an independent reviewer there found a genuine, ship-blocking defect —
127
+ so "it looked small" is not by itself reassuring. The difference is concrete rather than a feeling:
128
+ #1749 altered delivery semantics on a path that could double-deliver a live instruction; this
129
+ alters only what is asked of a model, on a path whose result is discarded, with every failure mode
130
+ resolving to the pre-existing behaviour.
131
+
132
+ ## Evidence
133
+
134
+ - **Measurement:** interleaved A/B (A,B,A,B,A,B so door drift cannot be attributed to a variant),
135
+ `gpt-5.4-mini` via codex, identical message/clauses/evidence. n=3 per arm.
136
+ Full prompt median **129,204 ms** / ~8,338 output tokens, range 91,520–148,719.
137
+ Legacy-only median **28,153 ms** / ~1,386 output tokens, range 24,965–33,708. **Ranges do not
138
+ overlap.** Against this call's own `timeoutMs: 60_000`: full exceeded the wall 3/3, legacy-only
139
+ fit 3/3 — consistent with the observed production `errorRate 0.835`, `p50 49,315`, `p95 60,082`
140
+ over 1,207 calls/24h.
141
+ - **Correctness, not just speed:** the legacy-only response was confirmed valid JSON matching the
142
+ legacy schema, and materially correct — it labelled an imperative to the reader `neither`, and
143
+ flagged an unsupported "everything green" claim `corroborated: false`.
144
+ - **Test discrimination measured, not assumed:** the new tests were run against the unfixed source
145
+ with only the test staged → **6 failed / 4 passed**. The 4 passes are marked `CONTROL` and pass
146
+ on both revisions by design; two of them only revealed themselves as non-discriminating *because*
147
+ that unfixed run was done. Only the 6 failures are counted as evidence for the change.
148
+ - tsc clean (exit 0). 30/30 on this file plus `claim-observation-v1` and `action-claim`.
149
+
150
+ ## Two defects found AFTER this artifact was first written, both in my own change
151
+
152
+ Recorded rather than silently folded in, because both are the kind of thing this review exists to
153
+ catch and both were missed by the review's first pass.
154
+
155
+ ### 1. The change would have been INERT in production (wiring integrity)
156
+
157
+ `AgentServer` does not hand `CompletionClaimVerifier` the router. It hands it an anonymous
158
+ `{ evaluate }` wrapper so the call rides the metered LLM queue, and that wrapper exposes no
159
+ routing surface. The duck-typed resolver therefore returned `undefined` on every real install,
160
+ `includeGeneral` stayed `true`, and nothing changed — **while all ten unit tests passed**, because
161
+ they inject a router directly.
162
+
163
+ This is exactly "the logic is proven and the feature is not wired." Found by tracing the real
164
+ construction site and asking *what object is actually passed here?* — not by any test.
165
+
166
+ Fixed by adding an explicit `resolveFramework?` to `CompletionClaimVerifierOptions`, passed from
167
+ `AgentServer` where the real router is in scope; the duck-typed fallback remains for callers that
168
+ pass a router straight through. Two WIRING tests added — one pins the defect (production-shaped
169
+ wrapper yields NO resolver), one asserts an explicit resolver reaches the arbiter.
170
+
171
+ ### 2. I broke `lint-llm-attribution` by hoisting the attribution to a constant
172
+
173
+ Replacing the inlined `component: 'completion-claim-verify'` literal with `CLAIM_ARBITER_COMPONENT`
174
+ made the callsite look unattributed: that lint reads the callsite **statically** and cannot resolve
175
+ a constant. It failed the build, which surfaced as two e2e preflight failures in the full suite.
176
+
177
+ Reverted to the inlined literal with a comment explaining why it must stay inlined, and added a test
178
+ pinning the constant equal to the literal so the two cannot drift while the callsite stays lintable.
179
+
180
+ ## Suite status, measured
181
+
182
+ Full unit suite: **2 failed / 3003 passed** (46,869 tests). Both failures are the same
183
+ `tests/e2e/dev-preflight-cli.test.ts` case.
184
+
185
+ **Discriminated rather than assumed:** re-ran that test with my changes stashed — **it fails
186
+ identically without them.** Cause is `ERR_PNPM_IGNORED_BUILDS` on a fresh clone under pnpm 11.5.1,
187
+ which now demands explicit build-script approval; the repo declares no `pnpm.onlyBuiltDependencies`
188
+ and existing worktrees only pass because their `node_modules` predates the enforcement. So it is a
189
+ fresh-checkout packaging condition affecting anyone cloning today, independent of this change.
190
+
191
+ Filed separately rather than bundled here — mixing an unrelated packaging fix into this change is
192
+ the "batched release" anti-pattern, and it would make it impossible to attribute a later regression
193
+ to the right half. <!-- tracked: CMT-1120 -->
194
+
195
+ After the lint fix: `npm run lint` exit 0, `tsc --noEmit` exit 0, 13/13 on this file.
196
+
197
+ ## Honest limits
198
+
199
+ - n=3 per arm is small; the conclusion rests on the size of the separation and the non-overlap,
200
+ not on sample count.
201
+ - Timings came from a direct `codex exec` invocation, not through instar's provider (different
202
+ sandbox/env/out-dir), so **absolute** numbers may differ in production. The **ratio** is the
203
+ load-bearing result and it is controlled.
204
+ - A first attempt to confirm a *different* fix tonight compared two installed builds that were both
205
+ `1.3.1068` — a false control that would have produced a confident wrong answer. Recorded here as
206
+ the reason the A/B above was interleaved and the unfixed test run was not skipped.