instar 1.3.1221 → 1.3.1223

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 (67) hide show
  1. package/dist/core/CodexDeliveryObserver.d.ts.map +1 -1
  2. package/dist/core/CodexDeliveryObserver.js +8 -8
  3. package/dist/core/CodexDeliveryObserver.js.map +1 -1
  4. package/dist/core/InboundDeliveryStore.d.ts.map +1 -1
  5. package/dist/core/InboundDeliveryStore.js +10 -2
  6. package/dist/core/InboundDeliveryStore.js.map +1 -1
  7. package/dist/core/ModelTierEscalation.d.ts +1 -1
  8. package/dist/core/ModelTierEscalation.d.ts.map +1 -1
  9. package/dist/core/ModelTierEscalation.js +10 -2
  10. package/dist/core/ModelTierEscalation.js.map +1 -1
  11. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  12. package/dist/core/PostUpdateMigrator.js +5 -0
  13. package/dist/core/PostUpdateMigrator.js.map +1 -1
  14. package/dist/core/SessionManager.d.ts.map +1 -1
  15. package/dist/core/SessionManager.js +13 -0
  16. package/dist/core/SessionManager.js.map +1 -1
  17. package/dist/core/StuckInputSentinel.d.ts.map +1 -1
  18. package/dist/core/StuckInputSentinel.js +16 -3
  19. package/dist/core/StuckInputSentinel.js.map +1 -1
  20. package/dist/core/topicProfileValidation.d.ts +2 -2
  21. package/dist/core/topicProfileValidation.js +3 -3
  22. package/dist/core/topicProfileValidation.js.map +1 -1
  23. package/dist/data/codexStageBReleaseEvidence.d.ts.map +1 -1
  24. package/dist/data/codexStageBReleaseEvidence.js +13 -12
  25. package/dist/data/codexStageBReleaseEvidence.js.map +1 -1
  26. package/dist/data/stageBCertifiedSet.d.ts +7 -7
  27. package/dist/data/stageBCertifiedSet.js +7 -7
  28. package/dist/data/stageBCertifiedSet.js.map +1 -1
  29. package/dist/data/standards-guard-index.json +1 -1
  30. package/dist/data/standards-guard-index.meta.json +2 -2
  31. package/dist/data/standards-registry.meta.json +1 -1
  32. package/dist/lifeline/MessageQueue.d.ts +3 -0
  33. package/dist/lifeline/MessageQueue.d.ts.map +1 -1
  34. package/dist/lifeline/MessageQueue.js.map +1 -1
  35. package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
  36. package/dist/lifeline/TelegramLifeline.js +34 -14
  37. package/dist/lifeline/TelegramLifeline.js.map +1 -1
  38. package/dist/lifeline/queueDeliveryNotice.d.ts +14 -0
  39. package/dist/lifeline/queueDeliveryNotice.d.ts.map +1 -0
  40. package/dist/lifeline/queueDeliveryNotice.js +31 -0
  41. package/dist/lifeline/queueDeliveryNotice.js.map +1 -0
  42. package/dist/monitoring/SessionWatchdog.d.ts +24 -1
  43. package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
  44. package/dist/monitoring/SessionWatchdog.js +107 -19
  45. package/dist/monitoring/SessionWatchdog.js.map +1 -1
  46. package/dist/scaffold/templates.js +1 -1
  47. package/dist/server/routes.d.ts +14 -0
  48. package/dist/server/routes.d.ts.map +1 -1
  49. package/dist/server/routes.js +33 -8
  50. package/dist/server/routes.js.map +1 -1
  51. package/package.json +1 -1
  52. package/scripts/model-registry-freshness.manifest.json +1 -1
  53. package/src/data/builtin-manifest.json +65 -65
  54. package/src/data/codexStageBReleaseEvidence.ts +13 -12
  55. package/src/data/stageBCertifiedSet.ts +7 -7
  56. package/src/data/standards-guard-index.json +1 -1
  57. package/src/data/standards-guard-index.meta.json +2 -2
  58. package/src/data/standards-registry.meta.json +1 -1
  59. package/src/scaffold/templates.ts +1 -1
  60. package/upgrades/1.3.1222.md +72 -0
  61. package/upgrades/1.3.1223.md +50 -0
  62. package/upgrades/inbound-delivery-unknown-terminalization.eli16.md +36 -0
  63. package/upgrades/side-effects/codex-gpt6-astra-allowlist.md +299 -0
  64. package/upgrades/side-effects/codex-observer-bounded-backlog.md +84 -0
  65. package/upgrades/side-effects/codex-test-worker-control-plane.md +86 -0
  66. package/upgrades/side-effects/inbound-delivery-unknown-terminalization.md +82 -0
  67. package/upgrades/side-effects/stage-b-evidence-code-binding.md +1 -0
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "8e6606a6722433c5ffc15b3870402283c5406083a14cdbd0e18e735731e2aae2",
5
- "packageVersion": "1.3.1221",
5
+ "packageVersion": "1.3.1223",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "5fa578c9e59c4db0835d48d02776497a23b3fbc74c2f0048192dd5ad9da9c679",
2
+ "sha256": "61b875cd1e0246e1a8594066393f7fef2f166b258b2bc88e589814bdb295eb0c",
3
3
  "registrySha256": "8e6606a6722433c5ffc15b3870402283c5406083a14cdbd0e18e735731e2aae2",
4
- "packageVersion": "1.3.1221"
4
+ "packageVersion": "1.3.1223"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "8e6606a6722433c5ffc15b3870402283c5406083a14cdbd0e18e735731e2aae2",
3
3
  "articleCount": 90,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1221"
5
+ "packageVersion": "1.3.1223"
6
6
  }
@@ -1729,7 +1729,7 @@ instar test-as-self --slack # Slack permission demonstr
1729
1729
 
1730
1730
  Progress monitoring must not manufacture user-facing narration. These systems are signals, never gates, and every error path fails toward silence.
1731
1731
  - **Silent-freeze watchdog** (ActiveWorkSilenceSentinel): before claiming a session is stuck, it re-captures the LIVE frame and corroborates — if the frame still shows an active-work indicator (spinner / "esc to interrupt"), a sub-agent is live, or it's a clean idle prompt, it stays SILENT. It speaks only when genuinely wedged, and even then hedges ("…hasn't changed in N min and a nudge didn't wake it — it may be stuck, or on a long task I can't see into. Want me to check?"). Threshold raised 15m→30m; a 90m frozen-indicator backstop still surfaces a real mid-tool hang. Tune/disable: \`monitoring.activeWorkSilenceSentinel.enabled\` (off), \`.silenceThresholdMs\` (default 30m), \`.activeWorkMaxFrozenIndicatorMs\` (default 90m).
1732
- - **Command-watchdog attribution:** \`SessionWatchdog\` never interrupts the Codex framework host or known bounded external waiters (including safe-merge and GitHub watch commands). If it interrupts a genuinely stuck user command, its audit entry names \`principal: session-watchdog\` and \`operatorInitiated: false\`; Codex's generic "aborted by user" text is transport wording, not proof the operator acted. Continuation is owned by the canonical continuation/recovery funnel, never an independent watchdog prompt.
1732
+ - **Command-watchdog attribution:** \`SessionWatchdog\` never interrupts the Codex framework host, known bounded external waiters, or pinned long-lived test/compiler services such as Vitest's esbuild service. A selected descendant is signaled directly only after an action-time PID/parent/argv identity check; the watchdog never sends pane-wide Ctrl+C for a descendant helper. If it interrupts a genuinely stuck user command, its audit entry names \`principal: session-watchdog\` and \`operatorInitiated: false\`; Codex's generic "aborted by user" text is transport wording, not proof the operator acted. Continuation is owned by the canonical continuation/recovery funnel, never an independent watchdog prompt.
1733
1733
  - **Crash-safe inbound delivery:** Normal tmux injection and Codex stranded-draft key attempts use FULL-durable armed/started/terminal phases. A crash-open mutation becomes \`effect-unknown\` and is never blindly repeated. Diagnose privacy-safe counts at \`GET /sessions/inbound-delivery-status\`; no message body is exposed.
1734
1734
  - **PromiseBeacon is internal by default:** commitment cadence, detection, revival, and escalation bookkeeping continue, but PromiseBeacon produces no topic/Slack messages, close-outs, escalation statuses, or Attention items. User output requires an explicit deployment opt-in with \`promiseBeacon.userOutputEnabled: true\`; do not enable it merely to retain follow-through. Effectiveness remains measurable in \`logs/sentinel-events.jsonl\` and the per-feature LLM-metrics surface (feature keys \`active-work-silence\`, \`promise-beacon\`).
1735
1735
 
@@ -0,0 +1,72 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The session watchdog now recognizes Vitest's long-lived esbuild service as
9
+ framework infrastructure and excludes it before stuck-command judgment. If a
10
+ genuine descendant process must be stopped, the watchdog revalidates the exact
11
+ process identity and session incarnation immediately before signaling that
12
+ process, then escalates through bounded targeted signals instead of sending a
13
+ pane-wide interrupt that can collapse the surrounding Codex turn.
14
+
15
+ Codex session discovery now uses the maintained state cache on asynchronous
16
+ control-plane paths, and quiet Codex panes are not synchronously probed unless
17
+ there is evidence of a stranded draft. This keeps test execution from starving
18
+ health and Telegram handling. Recovery notices also state their actual queue
19
+ reason so operators can distinguish a queued continuation from a delivered
20
+ one.
21
+
22
+ Inbound deliveries whose physical effect cannot be proven after bounded
23
+ observation now enter an explicit terminal effect-unknown state. They remain
24
+ auditable without occupying live-capacity slots forever, preventing an
25
+ ambiguous historical backlog from blocking new Telegram messages.
26
+
27
+ The release canary also found a second observer defect under real load: when a
28
+ busy Codex transcript grew by more than one read budget, a read ending partway
29
+ through the next JSONL record discarded the valid complete prefix as unknown.
30
+ The observer now durably advances through the last complete record and re-reads
31
+ the partial tail on its next bounded sweep. Truly oversized single records,
32
+ malformed JSON, schema drift, and identity mismatches still fail closed.
33
+
34
+ ## What to Tell Your User
35
+
36
+ Long-running Codex test work is protected from false watchdog interruptions,
37
+ and the agent's health and messaging paths remain responsive while those tests
38
+ run. Ambiguous old deliveries remain visible for investigation but no longer
39
+ crowd new messages out of the live queue.
40
+
41
+ ## Summary of New Capabilities
42
+
43
+ - Automatic protection for the exact Vitest esbuild service process.
44
+ - Process-targeted watchdog remediation with action-time identity checks.
45
+ - Responsive cached session discovery on asynchronous control-plane paths.
46
+ - An explicit terminal state for deliveries whose physical effect is unknown.
47
+ - Multi-pass observation of valid busy-session transcript backlogs without
48
+ false unknown outcomes at non-aligned byte boundaries.
49
+ - Clear queue-reason wording in Codex recovery notices.
50
+
51
+ ## Evidence
52
+
53
+ On topic 67366, the watchdog audit and live process tree showed the watchdog
54
+ selecting Vitest's persistent esbuild service and then issuing a pane-wide
55
+ interrupt; the surrounding test run collapsed while the local health endpoint
56
+ also stopped responding and Telegram retried the inbound message. The repaired
57
+ candidate's focused unit, HTTP integration, and production-lifecycle E2E gates
58
+ all pass, including a real long-running Vitest worker whose esbuild service
59
+ survives unchanged while the lifecycle suite completes. A backed-up copy of the
60
+ live inbound ledger contained 496 ambiguous rows consuming capacity; applying
61
+ the new terminal transition reduced the live count to four while retaining the
62
+ rows for audit, and normal inbound dispatch resumed.
63
+
64
+ The first exact-candidate two-hour/50-delivery canary correctly refused to sign
65
+ after finding three false unknowns on unrelated busy sessions. Their transcript
66
+ records proved the messages and responses existed beyond a non-aligned 256 KiB
67
+ scan boundary. The corrected scanner has unit, integration, and production-wired
68
+ E2E coverage for that exact multi-budget shape. The fresh corrected candidate
69
+ then passed 50/50 deliveries over 7,213,141 ms, all required special cases,
70
+ 30/30 responsiveness samples, and zero false-unknown, exhaustion, duplicate
71
+ ownership, lost-input, or stale-owner outcomes. Echo signed the resulting
72
+ evidence and the certified source manifest binds its exact digest and bytes.
@@ -0,0 +1,50 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ instar gates codex model ids against a closed list — an unknown id is refused so a
9
+ typo can never launch a session that dies at startup. That list did not include
10
+ `gpt-6-astra`, so pinning a topic to it was refused `off-enum` even on machines whose
11
+ codex CLI runs the model perfectly well. Sessions launched with the model passed
12
+ directly were unaffected, which made the gap read as "instar doesn't support this
13
+ model" rather than "instar's list is stale."
14
+
15
+ The id is added, verified working first: codex CLI 0.153.4 runs it; 0.149.0 returns
16
+ `400 … requires a newer version of Codex`. Only observed-working ids are added — a
17
+ plausible sibling name that was never run is still refused, and a test now asserts
18
+ that refusal so the list cannot drift from "verified" to "seemed likely."
19
+
20
+ The second half is the durable one. The `/sessions/spawn` route did not read the
21
+ shared list; it carried its own hand-typed copy, "kept in lockstep" by comment with
22
+ nothing enforcing it. Because `KNOWN_MODEL_IDS['codex-cli']` already IS
23
+ `KNOWN_CODEX_MODEL_IDS`, the general arm every other framework uses yields an
24
+ identical list — so the codex special case is deleted rather than kept in sync. A
25
+ test asserts the two surfaces resolve to one list, and it was falsified by
26
+ deliberately re-splitting them before being trusted.
27
+
28
+ ## What to Tell Your User
29
+
30
+ You can now pin a conversation to codex's gpt-6-astra model the way you pin any
31
+ other model — just say so in the topic. If your machine's codex tool is older than
32
+ version 0.153.4 the model itself will refuse at launch, so update it there first.
33
+ Nothing changes for any model that already worked.
34
+
35
+ ## Summary of New Capabilities
36
+
37
+ - `gpt-6-astra` is a valid codex model id for topic-profile pins and session spawns.
38
+ - Unverified sibling `gpt-6-*` names still fail closed, now with a test holding that.
39
+ - The spawn route and the pin validator read one codex list instead of two, so a
40
+ future model can no longer be spawn-accepted while pin-refused.
41
+
42
+ ## Evidence
43
+
44
+ Unit tests cover the new id against the pin validator and the spawn route, the
45
+ fail-closed refusal of an unverified sibling id, and the single-list parity between
46
+ `KNOWN_MODEL_IDS['codex-cli']` and `KNOWN_CODEX_MODEL_IDS`. Each was falsified before
47
+ being trusted: removing the id fails both acceptance tests for the right reason, and
48
+ pointing the map at a filtered copy fails the parity guard. Typecheck clean; full unit
49
+ suite green. Live verification of the model against both codex CLI versions is
50
+ recorded in the side-effects artifact.
@@ -0,0 +1,36 @@
1
+ # Inbound Delivery Capacity Recovery — Plain-English Overview
2
+
3
+ > The one-line version: delivery attempts whose observation window ended uncertain must stop occupying the finite live-message queue.
4
+
5
+ ## The problem in one breath
6
+
7
+ Instar safely refused to guess whether hundreds of older injected messages were consumed, marking them “unknown.” However, it left their transport state as “dispatched,” and the admission counter treats every dispatched row as live. Once 500 such rows accumulated, every new Telegram message was rejected with “Message could not be delivered to the session.”
8
+
9
+ ## What already exists
10
+
11
+ - **Durable inbound journal** — records each physical terminal injection before it happens so crashes cannot silently replay uncertain effects.
12
+ - **Codex delivery observer** — watches the composer and rollout transcript until it proves consumption/response or reaches a fixed observation deadline.
13
+ - **Admission ceilings** — prevent an unbounded number of live rows or bytes from exhausting the server.
14
+ - **Terminal evidence retention** — keeps consumed, failed, and effect-unknown evidence for audit, then garbage-collects it under bounded age/count rules.
15
+
16
+ ## What this adds
17
+
18
+ When the observer reaches its deadline without proof, the existing `markObservationUnknown` transition now records `effect-unknown` as the transport state as well as unknown transcript/composer eligibility. This does not claim that the message succeeded or failed. It only makes the already non-replayable uncertainty terminal, allowing the admission counter to release its slot while retaining the evidence for audit.
19
+
20
+ The status projection now reports these records as `effect-unknown` before considering their eligibility label, so uncertain-effect metrics remain honest. A regression test fills a one-row admission capacity, terminalizes the uncertain observation, verifies capacity returns, and verifies that a subsequent message can be prepared.
21
+
22
+ ## The safeguards
23
+
24
+ **No automatic replay.** Effect-unknown is explicitly non-replayable, preserving the core no-duplicate guarantee.
25
+
26
+ **No evidence deletion.** Rows are retained under the existing terminal evidence policy. This change is a state transition, not data erasure.
27
+
28
+ **Bounded authority.** Only dispatched rows that the observer already selected after its observation deadline can take this transition. Prepared or actively observed rows are untouched.
29
+
30
+ ## What ships when
31
+
32
+ The transition and status correction ship together. Existing stale rows on this machine are migrated with the identical narrow predicate after a complete SQLite backup; future rows self-terminalize through the observer.
33
+
34
+ ## What you actually need to decide
35
+
36
+ Should expired, already-uncertain delivery evidence become terminal so it cannot permanently block new Telegram messages?
@@ -0,0 +1,299 @@
1
+ # Side-Effects Review — codex `gpt-6-astra` allowlist + codex spawn-arm de-duplication
2
+
3
+ **Version / slug:** `codex-gpt6-astra-allowlist`
4
+ **Date:** 2026-09-05
5
+ **Author:** Echo (instar-dev agent)
6
+ **Second-pass reviewer:** required (touches `/sessions/spawn` allow decision — session lifecycle)
7
+
8
+ ## Summary of the change
9
+
10
+ Two changes, one root cause. (1) `gpt-6-astra` is added to `KNOWN_CODEX_MODEL_IDS`
11
+ (`src/core/ModelTierEscalation.ts`) — the closed enum the topic-profile validator and the
12
+ spawn route both gate codex model ids against. It was absent, so pinning a topic to a model
13
+ that codex CLI ≥ 0.153.4 runs fine was refused `off-enum`, while sessions launched with the
14
+ model passed directly ran it happily — a capability the agent HAD but could not be
15
+ configured to use. (2) The spawn route's codex special-case arm in `src/server/routes.ts` is
16
+ deleted. That arm read `CODEX_MODELS_SUBSCRIPTION`, a hand-maintained literal copy of the
17
+ same list, "kept in lockstep" by comment only with nothing enforcing it. `KNOWN_MODEL_IDS['codex-cli']`
18
+ IS `KNOWN_CODEX_MODEL_IDS`, so the general arm every other framework already uses yields the
19
+ identical list; deleting the special case removes the drift class instead of documenting it.
20
+
21
+ Files: `src/core/ModelTierEscalation.ts`, `src/server/routes.ts`,
22
+ `tests/unit/topicProfileValidation.test.ts`, `tests/unit/route-validation-edge.test.ts`.
23
+
24
+ ## Decision-point inventory
25
+
26
+ - `validateModelId` (`src/core/topicProfileValidation.ts`, reads `KNOWN_MODEL_IDS`) — **modify (data only)** — the closed enum it gates topic-profile model pins against gains one live-verified id. The check's logic and fail-closed shape are untouched.
27
+ - `POST /sessions/spawn` model allowlist (`src/server/routes.ts`) — **modify (source of the list, not the decision)** — the codex arm is removed; codex now reads the same canonical per-framework map as every other framework. Accepted set is provably identical to the post-(1) codex list.
28
+
29
+ ---
30
+
31
+ ## 1. Over-block
32
+
33
+ **What legitimate inputs does this change reject that it shouldn't?**
34
+
35
+ None newly. The change only ever WIDENS the accepted set, by exactly one id
36
+ (`gpt-6-astra`). Every id accepted before is still accepted: `KNOWN_MODEL_IDS['codex-cli']`
37
+ is a reference to `KNOWN_CODEX_MODEL_IDS`, and `GENERIC_TIERS` is still spread in both arms.
38
+ The residual over-block is pre-existing and deliberate: sibling `gpt-6-*` names that were
39
+ never live-verified are still refused `off-enum`. That is the intended fail-closed posture
40
+ (a typo must not strand a topic on a model the CLI will reject at launch), and it is now
41
+ asserted by a test rather than left implicit.
42
+
43
+ ---
44
+
45
+ ## 2. Under-block
46
+
47
+ **What failure modes does this still miss?**
48
+
49
+ - The enum cannot tell whether the LOCAL codex CLI is new enough. `gpt-6-astra` needs codex
50
+ CLI ≥ 0.153.4; 0.149.0 returns `400 … requires a newer version of Codex`. A pin set on a
51
+ machine with a current CLI, then resolved on a machine with an older one, still fails at
52
+ launch. That is unchanged by this PR and is the same shape the 5.6 family already carries
53
+ (its comment records the ≥ 0.144.0 floor). Naming it rather than fixing it here: a CLI
54
+ version probe at pin time is a real feature with its own failure modes, not a line in an
55
+ enum. <!-- tracked: ACT-420 -->
56
+ - The enum is a static list, so a genuinely new codex model is refused until a human adds it.
57
+ That is the deliberate closed-enum design (`§5.2(c)`), not a miss.
58
+
59
+ ---
60
+
61
+ ## 3. Level-of-abstraction fit
62
+
63
+ Correct layer, and the change moves it further in the right direction. The list is data
64
+ consumed by two existing validators; this PR adds an entry and deletes a duplicate reader.
65
+ It does not add a new check, a new layer, or a parallel path. The deletion specifically
66
+ removes a lower-quality copy in favour of the canonical primitive that already existed —
67
+ the "does a lower-level primitive already exist that this should USE instead of
68
+ re-implementing?" question, answered by deletion.
69
+
70
+ ---
71
+
72
+ ## 4. Signal vs authority compliance
73
+
74
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
75
+
76
+ - [x] No — this change has no block/allow surface of its own.
77
+
78
+ The blocking authority (a closed-enum validator) already existed and is unchanged in shape,
79
+ strictness, and failure mode. This PR edits the DATA the authority reads and removes a
80
+ duplicate copy of that data. No brittle logic gains authority; no authority is weakened —
81
+ unknown ids still fail closed.
82
+
83
+ ---
84
+
85
+ ## 4b. Judgment-point check (Judgment Within Floors standard)
86
+
87
+ No new static heuristic at a competing-signals decision point. The domain is enumerable by
88
+ construction: it is a closed set of model identifiers accepted by a CLI, which is exactly
89
+ the "it's an invariant, name it" case. There are no competing live signals to arbitrate —
90
+ an id is in the vendor's supported set or it is not.
91
+
92
+ ---
93
+
94
+ ## 5. Interactions
95
+
96
+ - **Shadowing:** none introduced. The codex arm and the general arm were mutually exclusive
97
+ branches of one ternary; collapsing them removes a branch rather than reordering checks.
98
+ `GENERIC_TIERS` still precedes the per-framework ids in the spread, unchanged.
99
+ - **Double-fire:** not applicable — validation is a pure function of the request body.
100
+ - **Races:** none. Both constants are module-level frozen-by-convention arrays read
101
+ synchronously; no shared mutable state.
102
+ - **Feedback loops:** none. The enum is not written by anything at runtime.
103
+ - **Adjacent consumers — full inventory** (the first draft named only the first two; the
104
+ second-pass reviewer found three more by grepping the tree, so this list is now the result
105
+ of a sweep rather than recall):
106
+ - `escalatedModelIds` / `resolveTierModel` (`ModelTierEscalation.ts`) — fail closed on an
107
+ out-of-enum config value. A `models.tierEscalation.frameworks['codex-cli']` naming
108
+ `gpt-6-astra` now resolves instead of nulling; the shipped codex default is
109
+ `{ default: null, escalated: null }`, so nothing changes without a deliberate operator
110
+ config edit, which is the intended effect.
111
+ - `ModelSwapService.ts:254` — clamps a topic's pinned baseline against
112
+ `KNOWN_MODEL_IDS[framework]` before a **live-session** model swap. This is the most
113
+ material consumer of the three and the first draft missed it: widening the list means a
114
+ topic pinned to `gpt-6-astra` can now be swapped onto rather than clamped away. That is
115
+ the intended effect (it is the same authority the pin itself passed), and the swap path
116
+ keeps its own independent verification — but it deserved naming, not discovery.
117
+ - `ProfileIntentClassifier.ts:157` (`defaultKnownModelValues`) — the union is the
118
+ extraction vocabulary for conversational pins, so "use gpt-6-astra here" now resolves to
119
+ a model value instead of falling through unrecognised. Intended, and the reason the
120
+ conversational surface works at all.
121
+ - `CodexCliIntelligenceProvider.ts:496` — reads the list benignly; no behavior change.
122
+
123
+ ---
124
+
125
+ ## 6. External surfaces
126
+
127
+ - **Other agents on the same machine:** none — no shared state touched.
128
+ - **Install base:** the only user-visible change is that a topic-profile pin naming
129
+ `gpt-6-astra` now succeeds where it returned `off-enum`. No previously-working
130
+ configuration changes behavior.
131
+ - **External systems:** none. instar does not call the model vendor here; it validates a
132
+ string that is later handed to the codex CLI.
133
+ - **Persistent state:** none written or migrated. A topic profile that could not previously
134
+ store this model id simply had nothing stored.
135
+ - **Timing / runtime conditions:** none introduced. The pre-existing dependence on the local
136
+ codex CLI version is recorded under §2.
137
+ - **Operator surface (Mobile-Complete Operator Actions):** no new operator-facing action.
138
+ The existing surface — the operator saying "use gpt-6-astra here" in the topic, which the
139
+ topic-profile write path already accepts from a verified operator — is phone-complete and
140
+ unchanged. This PR is what makes that sentence succeed rather than be refused.
141
+
142
+ ---
143
+
144
+ ## 6b. Operator-surface quality
145
+
146
+ No operator surface — not applicable. No dashboard renderer, approval page, or
147
+ grant/revoke/secret-drop form is touched.
148
+
149
+ ---
150
+
151
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
152
+
153
+ **Posture: machine-local BY DESIGN — with a replicated consumer, and that split is the
154
+ interesting part.**
155
+
156
+ The enum itself is compiled-in constant data, so it is machine-local in the same trivial
157
+ sense all code is: each machine runs whatever instar version it has installed. The reason
158
+ this is BY DESIGN rather than an oversight is that the thing it gates — a topic profile
159
+ pin — IS replicated (`multiMachine.stateSync.topicPins`). So the honest posture is: the
160
+ DATA is per-version, the STATE it validates is pool-wide.
161
+
162
+ That produces one real cross-machine behavior worth stating: a pin set on a machine running
163
+ this version replicates to a peer still on ≤ 1.3.1222, whose validator does not know the id.
164
+ This is not a new failure mode — it is exactly the documented "a pinned model that is no
165
+ longer available falls back to defaults with a once-per-transition notice" path, which is
166
+ already the shipped contract for a version-skewed pool and degrades to a working session
167
+ with a notice rather than a block. A rolling fleet update converges it, and the
168
+ machine-coherence guard already surfaces version skew.
169
+
170
+ - **User-facing notices:** emits none. The fallback notice above is pre-existing and already
171
+ one-voice gated.
172
+ - **Durable state on topic transfer:** holds none of its own. The topic pin it validates is
173
+ already replicated and already survives transfer.
174
+ - **Generated URLs:** none.
175
+
176
+ ---
177
+
178
+ ## 8. Rollback cost
179
+
180
+ - **Hot-fix release:** revert the commit, ship as the next patch. Pure code change.
181
+ - **Data migration:** none — but one honest caveat. If an operator pins a topic to
182
+ `gpt-6-astra` and the change is then reverted, that stored pin becomes an unknown id on
183
+ the next resolve. It does NOT wedge the topic: the shipped behavior is fallback-to-default
184
+ with a once-per-transition notice. Cost is one notice per affected topic, no repair.
185
+ - **Agent state repair:** none required.
186
+ - **User visibility during rollback:** a topic pinned to the new id falls back to the
187
+ framework default and says so. No silent behavior change.
188
+
189
+ ---
190
+
191
+ ## Conclusion
192
+
193
+ The review changed the shape of the fix twice, both times toward less code. The first draft
194
+ edited two lists in lockstep — which reproduced, in the same commit, the exact
195
+ comment-enforced coupling that caused the bug. The second draft made the route read the
196
+ canonical constant. The final version deletes the codex arm entirely, because
197
+ `KNOWN_MODEL_IDS['codex-cli']` already IS that constant and the general arm was provably
198
+ equivalent. Net effect: one id added, one duplicate reader removed, one drift class closed.
199
+
200
+ Two findings were surfaced rather than silently absorbed. The CLI-version floor (§2) is a
201
+ genuine residual gap and is tracked, not hand-waved. The version-skewed-pool pin (§7) is a
202
+ real cross-machine interaction, and it lands on an existing, correct degradation path.
203
+
204
+ The second-pass review then rejected the first version of the drift guard, correctly. That
205
+ guard iterated `KNOWN_MODEL_IDS['codex-cli']` and asserted `validateModelId` — but the
206
+ validator reads that same map, so the loop was tautological, and nothing in it read the
207
+ spawn route at all. The reviewer's negative control proved it: re-splitting the route to a
208
+ literal carrying 3 of 9 codex ids left the entire suite green. I had "falsified" that guard
209
+ by breaking the map alias, which it did catch — and mistook that for proof it watched the
210
+ route, which it never did. That is the precise failure mode of a guard that is blind to its
211
+ own subject, reproduced inside the very commit whose purpose is to close a drift class.
212
+
213
+ The fix is structural rather than another assertion: `spawnModelAllowlist(framework)` is
214
+ exported from `routes.ts`, the handler calls it, and the guard reads THAT — the route's own
215
+ accepted set — in both directions (the route accepts nothing the validator refuses, and
216
+ drops nothing the validator accepts). The reviewer's exact control now fails on
217
+ `gpt-5.2 missing from the spawn route`.
218
+
219
+ The review also caught that the commit re-planted the instruction it was deleting: three
220
+ comments still told a maintainer to "keep in lockstep with CODEX_MODELS_SUBSCRIPTION",
221
+ including one I newly added, pointing at a constant this change removes. All three are
222
+ rewritten. Clear to ship.
223
+
224
+ ---
225
+
226
+ ## Second-pass review (required — touches the `/sessions/spawn` allow decision)
227
+
228
+ **Reviewer:** independent reviewer subagent, 2026-09-05
229
+ **Independent read of the artifact: CONCERN — resolved, re-review recommended**
230
+
231
+ The reviewer verified sections A, B, E, §7 and §8 against the code (including confirming
232
+ that `CODEX_MODELS_SUBSCRIPTION` and `KNOWN_CODEX_MODEL_IDS` were entry-identical and
233
+ same-order on `origin/main`, so the deletion is a provable no-op on the accepted set, and
234
+ that `?? KNOWN_CLAUDE_MODEL_IDS` is unreachable for codex because `KNOWN_MODEL_IDS` is an
235
+ exhaustive `Record` over the framework union). Five findings were raised; all five are
236
+ resolved in this commit:
237
+
238
+ 1. **(blocking) The drift guard was blind to its own subject.** Proven by negative control:
239
+ re-splitting the route to a literal dropping 6 of 9 codex ids left both test files green,
240
+ 58/58. Resolved — `spawnModelAllowlist` is exported and the guard reads the route's own
241
+ list bidirectionally; the same control now fails on
242
+ `codex-cli pinnable id gpt-5.2 missing from the spawn route`.
243
+ 2. **The commit re-planted the instruction it deletes.** Three "keep in lockstep with
244
+ CODEX_MODELS_SUBSCRIPTION" comments survived, one newly added by this change, pointing at
245
+ the constant it removes. Resolved — all three rewritten; zero references remain in `src/`.
246
+ 3. **Consumer inventory incomplete.** `ModelSwapService.ts:254`, `ProfileIntentClassifier.ts:157`
247
+ and `CodexCliIntelligenceProvider.ts:496` were unlisted. Resolved — §5 now carries the
248
+ swept inventory, with the live-session swap path called out as the material one.
249
+ 4. **Falsification evidence not reproducible.** The quoted output did not correspond to the
250
+ committed code. Resolved — both controls re-run against the shipped code and quoted
251
+ verbatim under Evidence.
252
+ 5. **Spawn-side coverage reduced under a wrong justification.** Resolved — `gpt-5.6-terra`
253
+ restored to the HTTP sample, and the comment now points at the route-observing guard
254
+ rather than claiming the validator test covers the route.
255
+
256
+ The reviewer's `Concern raised` verdict stands against the version it read. Every finding it
257
+ raised is addressed above; the load-bearing one changed the design.
258
+
259
+ **Re-review verdict: "Concur with the review"** (same reviewer, against the fixed tree). It
260
+ re-ran both injection controls itself and reproduced the quoted evidence verbatim (2/56 and
261
+ 1/57), instrumented the guard's per-framework loop to confirm neither direction runs vacuous
262
+ (codex 9/9, claude 10/10, gemini 3/3 ids after the generic-tier filter), confirmed the
263
+ handler has exactly one list-computation site and that the test import creates no runtime
264
+ cycle (the CapabilityIndex back-reference is type-only). Two non-blocking nits were raised
265
+ and are fixed in this commit: a dangling `CODEX_MODELS_SUBSCRIPTION` reference in the
266
+ model-registry freshness manifest's descriptive note, and `grok-build` added to the guard
267
+ loop (the reviewer verified it passes; it was ROUND-21's original victim framework, so its
268
+ absence from the guard was the omission most worth closing). `pi-cli` stays omitted — its
269
+ enum is closed-empty by design, so both loop directions would be vacuous.
270
+
271
+ ---
272
+
273
+ ## Evidence pointers
274
+
275
+ - Live verification of the model id, Mac Mini, 2026-09-05: codex CLI 0.149.0 →
276
+ `400 invalid_request_error: The 'gpt-6-astra' model requires a newer version of Codex.`
277
+ After upgrading to 0.153.4, the same one-shot prompt returns a normal completion.
278
+ - Refusal that prompted the change: `POST /topic-profile/69507` →
279
+ `{"failure":"off-enum","reason":"'gpt-6-astra' is not a known codex-cli model id"}`, on an
280
+ agent whose own Studio machine was concurrently running seven sessions on that model.
281
+ - Falsification runs, re-captured against the COMMITTED code after the second-pass review
282
+ (the first draft quoted `expected [ …(6) ] to be [ …(7) ]`, which corresponded to an
283
+ intermediate version of the guard, not to what shipped — the reviewer was right that it
284
+ was unreproducible):
285
+ - **Control A — remove the id from the enum:** 2 failed | 56 passed.
286
+ `accepts codex-cli GPT-5.6 + GPT-6 family model values` → `expected 400 not to be 400`;
287
+ `accepts the GPT-6 family against codex-cli` → `expected { field: 'model', …(4) } to be null`.
288
+ - **Control B — re-split the route to a literal dropping 6 of 9 codex ids** (the reviewer's
289
+ own control, which the FIRST guard passed green): 1 failed | 57 passed.
290
+ `the spawn ROUTE accepts every id the pin validator does, for every framework` →
291
+ `codex-cli pinnable id gpt-5.2 missing from the spawn route: expected [ Array(3) ] to include 'gpt-5.2'`.
292
+
293
+ ---
294
+
295
+ ## Class-Closure Declaration (display-only mirror)
296
+
297
+ No agent-authored-artifact defect — not applicable. This is a defect in hand-maintained
298
+ source data (a duplicated constant), not in an LLM prompt, hook, config, skill, or standards
299
+ text; and it adds no self-triggered controller.
@@ -0,0 +1,84 @@
1
+ # Side-Effects Review — Codex observer bounded backlog
2
+
3
+ **Version / slug:** `codex-observer-bounded-backlog`
4
+ **Date:** `2026-09-04`
5
+ **Author:** Codex
6
+ **Second-pass reviewer:** independent Codex reviewer
7
+
8
+ ## Summary of the change
9
+
10
+ The Codex delivery observer now advances every complete JSONL prefix inside its bounded read even when the byte budget ends partway through the next record. The incomplete tail is left unread and starts the next sweep. A single record with no newline inside the full budget remains typed `unknown`.
11
+
12
+ This was found by the exact-candidate Stage-B canary: all 50 dedicated rows converged, but three real messages in busy sessions generated more than 256 KiB between sweeps. The old scanner marked the entire valid backlog unknown solely because byte 262,144 was not a newline, even though hundreds of complete record boundaries preceded it. Transcript inspection proved the injected user events and assistant responses were present.
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `scanSharedRollout` — modify — advances only through the last complete newline in a bounded read.
17
+ - `scanRollout` — modify — applies the same prefix rule to the compatibility scanner.
18
+
19
+ ## 1. Over-block
20
+
21
+ No new block is introduced. A record larger than the entire per-row budget still has no complete boundary and remains unknown; schema drift and malformed complete JSON remain unknown.
22
+
23
+ ## 2. Under-block
24
+
25
+ An incomplete tail is not parsed or trusted. It is re-read from its exact first byte on the next sweep. The cursor advances only to a verified newline, so no partial JSON can be skipped or treated as evidence.
26
+
27
+ ## 3. Level-of-abstraction fit
28
+
29
+ This is byte-framing behavior in the JSONL scanner, the layer that owns cursor advancement. It does not change delivery authority, replay policy, or message interpretation.
30
+
31
+ ## 4. Signal vs authority compliance
32
+
33
+ Required reference: [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md).
34
+
35
+ - [x] No — this change has no block/allow judgment surface.
36
+
37
+ No new judgment is added. A complete newline is structural framing evidence; absence of any boundary at the hard budget remains the conservative uncertainty floor.
38
+
39
+ ## 4b. Judgment-point check
40
+
41
+ No new static heuristic is added at a competing-signals decision point. This is an enumerable JSONL framing invariant: only bytes through a complete newline may advance the durable cursor.
42
+
43
+ ## 5. Interactions
44
+
45
+ - **Races:** scans use the stat/read prefix already in place; bytes appended after the read wait for a later sweep.
46
+ - **Budgets:** each sweep still reads at most the configured per-row and aggregate limits.
47
+ - **Retries:** progress is durable in `inbound_rollout_cursor`; restart resumes from the last complete boundary.
48
+ - **Failure posture:** malformed complete JSON, unsupported events, identity mismatch, truncation, and an oversized single record still fail closed.
49
+
50
+ ## 6. External surfaces
51
+
52
+ Busy Codex sessions no longer show false unknown delivery outcomes or strand valid messages from observability merely because their transcript grew by more than one scan budget. No API shape or user-facing wording changes.
53
+
54
+ ## 6b. Operator-surface quality
55
+
56
+ No operator surface is changed — not applicable.
57
+
58
+ ## 7. Multi-machine posture
59
+
60
+ Machine-local by design: each observer reads the local transcript for the delivery effect performed on that machine. Ownership and transfer fencing are unchanged.
61
+
62
+ ## 8. Rollback cost
63
+
64
+ Pure scanner code; revert is mechanically simple but would restore demonstrated false-unknown outcomes under large valid transcript bursts.
65
+
66
+ ## Conclusion
67
+
68
+ The first canary correctly refused certification after exposing three false unknowns. The fix preserves every hard bound and uncertainty rule while distinguishing a safely resumable complete prefix from a truly boundary-free oversized record. Unit, integration, and production-wired E2E tests cover the non-aligned multi-budget case. The fresh exact candidate subsequently passed 50/50 deliveries over 7,213,141 ms, the complete case matrix, 30/30 responsiveness samples, and zero forbidden outcomes; its signed evidence is now bound to the certified sources.
69
+
70
+ ## Second-pass review
71
+
72
+ Concur. Cursor advancement is restricted to complete newline boundaries, partial bytes remain unread, and the next sweep revalidates them from the record start. The change cannot authorize delivery or replay and narrows false uncertainty without weakening malformed-input handling. The reviewer noted that the compatibility scanner needed the same direct regression assertion as the production shared path; that coverage was added before commit.
73
+
74
+ ## Evidence pointers
75
+
76
+ - `tests/unit/CodexDeliveryObserver.test.ts`
77
+ - `tests/integration/codex-observer-bounded-backlog.test.ts`
78
+ - `tests/e2e/codex-session-lifecycle-reliability.test.ts`
79
+ - First canary ledger: 50/50 dedicated rows responded; three candidate-window false unknowns on busy sessions identified at non-aligned >256 KiB backlogs.
80
+ - Corrected canary ledger: 50/50 rows consumed/cleared/responded; zero false unknown, exhaustion, duplicate ownership, lost inbound, or stale-owner outcomes; signed artifact digest `a2db23a95530681953ffa3002ab14e349ebf53d19be2a54c1f4afacc3aead997`.
81
+
82
+ ## Class-Closure Declaration
83
+
84
+ No agent-authored-artifact defect and no added or modified self-triggered controller — not applicable. The regression is nevertheless pinned at all three required test tiers by the evidence above.