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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +43 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +14 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +7 -3
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/feedback-factory/cutoverReadiness.d.ts +103 -0
- package/dist/feedback-factory/cutoverReadiness.d.ts.map +1 -0
- package/dist/feedback-factory/cutoverReadiness.js +118 -0
- package/dist/feedback-factory/cutoverReadiness.js.map +1 -0
- package/dist/monitoring/SessionReaper.d.ts +19 -0
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +57 -0
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +45 -5
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +12 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +28 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.272.md +40 -0
- package/upgrades/1.3.273.md +52 -0
- package/upgrades/side-effects/cutover-readiness.md +63 -0
- 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`.
|