instar 1.3.271 → 1.3.273

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 (41) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +43 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  5. package/dist/core/PostUpdateMigrator.js +14 -1
  6. package/dist/core/PostUpdateMigrator.js.map +1 -1
  7. package/dist/core/SessionManager.d.ts.map +1 -1
  8. package/dist/core/SessionManager.js +7 -3
  9. package/dist/core/SessionManager.js.map +1 -1
  10. package/dist/core/types.d.ts +10 -0
  11. package/dist/core/types.d.ts.map +1 -1
  12. package/dist/core/types.js.map +1 -1
  13. package/dist/feedback-factory/cutoverReadiness.d.ts +103 -0
  14. package/dist/feedback-factory/cutoverReadiness.d.ts.map +1 -0
  15. package/dist/feedback-factory/cutoverReadiness.js +118 -0
  16. package/dist/feedback-factory/cutoverReadiness.js.map +1 -0
  17. package/dist/monitoring/SessionReaper.d.ts +19 -0
  18. package/dist/monitoring/SessionReaper.d.ts.map +1 -1
  19. package/dist/monitoring/SessionReaper.js +57 -0
  20. package/dist/monitoring/SessionReaper.js.map +1 -1
  21. package/dist/scaffold/templates.d.ts.map +1 -1
  22. package/dist/scaffold/templates.js +6 -0
  23. package/dist/scaffold/templates.js.map +1 -1
  24. package/dist/server/AgentServer.d.ts +1 -0
  25. package/dist/server/AgentServer.d.ts.map +1 -1
  26. package/dist/server/AgentServer.js +45 -5
  27. package/dist/server/AgentServer.js.map +1 -1
  28. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  29. package/dist/server/CapabilityIndex.js +12 -0
  30. package/dist/server/CapabilityIndex.js.map +1 -1
  31. package/dist/server/routes.d.ts +4 -0
  32. package/dist/server/routes.d.ts.map +1 -1
  33. package/dist/server/routes.js +28 -0
  34. package/dist/server/routes.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/data/builtin-manifest.json +65 -65
  37. package/src/scaffold/templates.ts +6 -0
  38. package/upgrades/1.3.272.md +40 -0
  39. package/upgrades/1.3.273.md +52 -0
  40. package/upgrades/side-effects/cutover-readiness.md +63 -0
  41. package/upgrades/side-effects/post-transfer-session-closeout.md +116 -0
@@ -0,0 +1,52 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ When a conversation (topic) moves from one machine to another in the
9
+ multi-machine session pool, the OLD machine's session for that topic is now
10
+ closed automatically — previously it lingered indefinitely, doing duplicate
11
+ work alongside the new machine's session (the operator-named duplicate-sessions
12
+ issue).
13
+
14
+ Two layers:
15
+
16
+ 1. **Immediate** — an explicit "move this to X" closes the local session the
17
+ moment the move executes (the user's command is the authority; protected
18
+ sessions are never auto-closed; the close is silent since the user already
19
+ got the "Moving…" confirmation).
20
+ 2. **Standing invariant** — the SessionReaper closes any topic-bound session
21
+ whose topic the ownership registry shows owned by ANOTHER machine, after a
22
+ 2-tick (~4 min) dwell that absorbs in-progress transfers. This covers
23
+ failovers and automatic re-placements. The close goes through the existing
24
+ guarded terminate authority: a KEEP-guard veto is audited and retried, never
25
+ forced. Works even when the duplicate is BUSY — busy-on-a-duplicate is the
26
+ bug.
27
+
28
+ Every close (and every refusal) lands in the reap-log with reason "topic moved
29
+ to <machine>". Config: `monitoring.sessionReaper.topicMovedCloseout` (default
30
+ true; inert on single-machine installs) + `topicMovedConfirmTicks` (default 2).
31
+
32
+ ## What to Tell Your User
33
+
34
+ When you move a conversation to another machine, the machine you left now
35
+ cleans up after itself — no more duplicate sessions quietly doing the same
36
+ work twice. If you ever wonder where a session went, the reap-log says exactly
37
+ why ("topic moved to Laptop").
38
+
39
+ ## Summary of New Capabilities
40
+
41
+ - Explicit topic moves immediately close the old machine's session.
42
+ - A reaper rule guarantees eventual closeout for failover/re-placement paths.
43
+ - `monitoring.sessionReaper.topicMovedCloseout` / `topicMovedConfirmTicks` config.
44
+ - Reap-log + audit rows (`rule: 'topic-moved-away'`) for full traceability.
45
+ - CLAUDE.md template + migration so agents explain auto-closed sessions correctly.
46
+
47
+ ## Evidence
48
+
49
+ `tests/unit/session-reaper-topic-moved.test.ts` (9 — including closing a BUSY
50
+ duplicate, churn-resets-dwell, guard-veto-retry, dry-run, budgets, all inert
51
+ paths) + 3 migration tests; full reaper family regression 65/65 green; tsc +
52
+ lint clean.
@@ -0,0 +1,63 @@
1
+ # Side-effects review — Cutover-readiness checker (coordination-mandate spec §7, G2.4)
2
+
3
+ Spec: `docs/specs/coordination-mandate.md` (approved by Justin, A/A/B, 2026-06-05) — §7
4
+ names G2.4 and decision 1A scopes it: everything UP TO the door, never the door. Change:
5
+ new `src/feedback-factory/cutoverReadiness.ts` + two `/cutover-readiness*` routes + the
6
+ REAL condition resolvers replacing the deny-safe stubs in `AgentServer`'s coordination
7
+ block.
8
+
9
+ ## 1. Blast radius
10
+
11
+ Additive, with ONE deliberate behavior change inside the coordination block: the
12
+ `integrity-gate-pass` and `parity-zero-divergence` conditions were `() => false` stubs;
13
+ they now resolve from REAL durable state (the persisted import IntegrityReport and the
14
+ durable parity window). Until that state genuinely clears, both still evaluate false —
15
+ identical behavior to the stubs. The first mandate (per decision 3B) has no conditioned
16
+ authority, so nothing live consumes them yet; the change makes the future
17
+ execute-cutover authority REAL instead of permanently-denied. No other route, gate, or
18
+ store is modified.
19
+
20
+ ## 2. State / data
21
+
22
+ Two new files under the `.instar/state/` convention, created on first use:
23
+ `feedback-parity-passes.jsonl` (append-only durable parity window, torn-line tolerant —
24
+ the merged #781 persistence) and `feedback-integrity-report.json` (the import tooling's
25
+ persisted IntegrityReport envelope). Absent files mean blocked/not-ready (deny-safe).
26
+
27
+ ## 3. Security model (T7 — the assertion-proof boundary)
28
+
29
+ - **No "set the condition" input exists.** The ONLY writes: (a)
30
+ `POST /cutover-readiness/parity-pass` TRIGGERS a server-side live fetch+compare
31
+ (HttpParitySource → runDryRunCompare); the request body contributes NOTHING to the
32
+ result (integration-tested with a hostile body asserting cleanliness). (b)
33
+ `recordIntegrityReport()` is called by server-side import tooling — no HTTP route
34
+ writes it by design.
35
+ - **A failed live check records NOTHING** — a fetch error is absence of evidence, not
36
+ evidence of divergence, and it cannot extend the clean window either (unit-tested
37
+ both ways).
38
+ - **Freshness bound (readiness-layer addition):** the merged gate has no max-staleness —
39
+ a cleared streak stays cleared forever without new passes. The readiness layer marks
40
+ the window STALE when the last pass is older than 6h (default) and `ready` requires
41
+ fresh. Stated honestly: the gate's own policy is unchanged; staleness lives here.
42
+ - **The door is structural:** there is NO fire-cutover route (integration-tested), the
43
+ status carries `door: 'manual-operator-click'` machine-readably, and the agent
44
+ template explicitly forbids presenting `ready` as permission to flip.
45
+ - **Token handling:** the parity source token is read lazily from the encrypted
46
+ SecretStore (`portal.instarReadToken` by default) at check time — never persisted in
47
+ config, never logged, never in a response.
48
+
49
+ ## 4. Failure modes
50
+
51
+ - No `feedbackMigration.paritySource` config → `runParityCheck` is null → the trigger
52
+ route 409s with a plain reason; the readiness read surface still works.
53
+ - SecretStore miss → the check throws → 409, nothing recorded.
54
+ - Engine init failure → coordination block's existing try/catch → routes 503, boot
55
+ unaffected.
56
+
57
+ ## 5. Test coverage
58
+
59
+ 9 unit + 5 integration + 3 e2e (17 total). The load-bearing e2e: a conditioned
60
+ execute-cutover authority DENIES on a fresh boot and flips to ALLOW only after the
61
+ durable state genuinely clears (pre-seeded parity window + integrity report written to
62
+ the production state path, no restart) — then re-blocks on a failed report. Conditions
63
+ provably resolve from REAL state on the production init path.
@@ -0,0 +1,116 @@
1
+ # Side-Effects Review — Post-transfer session closeout
2
+
3
+ **Version / slug:** `post-transfer-session-closeout`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required` (kill-path change, but every kill goes through the existing terminate authority; every boundary side test-pinned; inert single-machine)
7
+
8
+ ## Summary of the change
9
+
10
+ Two halves closing the operator-named duplicate-sessions gap: (A) the transfer
11
+ handler immediately closes the local topic session when the user explicitly
12
+ moves a topic to another machine (operator origin, protected-skipped, silent
13
+ recovery-bounce disposition); (B) a SessionReaper rule closes any topic-bound
14
+ session whose topic the ownership registry shows OWNED BY ANOTHER MACHINE,
15
+ after a 2-tick dwell, through the guarded autonomous terminate (vetoes audited
16
+ + retried). New `topicOwnerElsewhere` reaper dep late-binds the pool objects;
17
+ config `monitoring.sessionReaper.topicMovedCloseout` (default true) +
18
+ `topicMovedConfirmTicks` (default 2).
19
+
20
+ ## Decision-point inventory
21
+
22
+ 1. Plan is `transfer` to another machine vs `noop`/self → immediate close vs
23
+ none. Covered by the rule conditions + transfer tests from #790 (noop paths
24
+ unchanged).
25
+ 2. Session protected → immediate close skipped (and the sweeper's guard veto
26
+ path). Pinned via reaper-family protected test + the explicit skip.
27
+ 3. Topic owned elsewhere vs by-self vs unowned vs no binding vs dep absent →
28
+ sweeper fires vs inert. All five pinned.
29
+ 4. Dwell below/at threshold; churn reset. Pinned.
30
+ 5. killsEnabled (enabled/dryRun/auto-disabled) → terminate vs `would-reap`
31
+ audit. Pinned.
32
+ 6. Guard veto → `reap-skipped-topic-moved` + retry. Pinned.
33
+ 7. Per-tick + hourly budgets. Pinned (per-tick), hourly shares the existing
34
+ tested budget mechanism.
35
+
36
+ ## 1. Over-block
37
+
38
+ The rule only ever closes MORE sessions, never keeps one it shouldn't — the
39
+ risk axis is over-KILLING, addressed in Under-block. No legitimate
40
+ operation is rejected; single-machine installs are bit-identical in behavior
41
+ (dep absent → rule unreachable).
42
+
43
+ ## 2. Under-block (over-kill risk — the one that matters)
44
+
45
+ - **Wrong-owner false positive:** the ownership registry is the same source
46
+ routing already trusts; a 2-tick dwell absorbs CAS churn mid-transfer, and
47
+ churn RESETS the dwell (test-pinned). Worst case (registry wrong for >2
48
+ ticks): the session closes while the registry routes messages to the other
49
+ machine anyway — the close follows the routing truth, not vice versa.
50
+ - **In-flight work on the duplicate:** deliberately closable — duplicate work
51
+ is the bug (operator's words). The sweeper still honors KEEP-guard vetoes
52
+ (retry, not force); the immediate half is the user's explicit command.
53
+ - **Protected sessions:** never auto-closed on either half.
54
+ - **Half A races the release CAS:** the close is fired after the release
55
+ attempt regardless of CAS outcome — but only when `plan.action ===
56
+ 'transfer'`, which already passed the planner's owner/pin checks; a failed
57
+ CAS still means the user commanded the move, and the pin routes the next
58
+ message away from this machine.
59
+
60
+ ## 3. Level-of-abstraction fit
61
+
62
+ The immediate half lives in the one handler that executes user moves; the
63
+ invariant lives in the reaper — the component whose job is "sessions that
64
+ should not be running, aren't" — via an injected signal, keeping
65
+ SessionReaper free of pool imports (fully unit-testable, same pattern as
66
+ every other dep). The terminate authority remains the single kill funnel.
67
+
68
+ ## 4. Signal vs authority compliance
69
+
70
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
71
+
72
+ The sweeper REQUESTS kills through the guarded authority (autonomous origin —
73
+ vetoes win). The immediate half uses operator origin, which is the documented
74
+ user-command bypass; the terminateSession doc comment is updated to name this
75
+ second stamp site, and protected sessions are excluded before the call so the
76
+ bypass never reaches them on this path.
77
+
78
+ ## 5. Interactions
79
+
80
+ - ReapGuard/ReapAuthority untouched — same funnel, same vetoes.
81
+ - Reap-log + §P3 notifier: sweeper closes are `terminal` (one honest "session
82
+ was shut down — topic moved to X" notice via the existing reapNotify);
83
+ Half A is `recovery-bounce` (silent — the user just got "Moving…").
84
+ - Ownership registry is read-only here (ownerOf) — no new writers.
85
+ - The dep's try/catch absorbs the construction-order TDZ window (reaper is
86
+ built before the mesh block wires the registry) — rule inert until wired.
87
+
88
+ ## 6. External surfaces
89
+
90
+ Two config keys (defaults preserve behavior except the new closeout itself,
91
+ which is the fix). New audit rows (`rule: 'topic-moved-away'`,
92
+ `reap-skipped-topic-moved`). CLAUDE.md template line + idempotent append
93
+ migration. No routes, no schema.
94
+
95
+ ## 7. Rollback cost
96
+
97
+ `monitoring.sessionReaper.topicMovedCloseout: false` is an instant per-agent
98
+ off-switch; full revert restores the old lingering behavior. No state.
99
+
100
+ ## Conclusion
101
+
102
+ The operator-named duplicate-session bug closed at both speeds — instantly on
103
+ the explicit command, eventually-but-guaranteed everywhere else — without a
104
+ single new kill path outside the existing authority.
105
+
106
+ ## Second-pass review (if required)
107
+
108
+ Not required — see header.
109
+
110
+ ## Evidence pointers
111
+
112
+ - `tests/unit/session-reaper-topic-moved.test.ts` (9) — incl. the headline
113
+ "closes a BUSY duplicate" case.
114
+ - `tests/unit/PostUpdateMigrator-poolSessionsVisibility.test.ts` (+3).
115
+ - Reaper family regression 65/65 green; tsc clean.
116
+ - `docs/specs/post-transfer-session-closeout.md` + `.eli16.md`.