instar 1.3.117 → 1.3.119

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.
@@ -0,0 +1,40 @@
1
+ # Instar Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Threadline now retires completed worker threads promptly when their backing
8
+ session finishes.**
9
+
10
+ Previously, a Threadline conversation bound to a worker session could remain in
11
+ the active-thread set until the stale-retirement backstop cleaned it up, even
12
+ after `SessionManager` had already emitted `sessionComplete` for the backing
13
+ session. That made completed conversations look live longer than they were.
14
+
15
+ Fix: the server now forwards `sessionComplete` into `ThreadlineRouter`, the
16
+ resume map can reverse-lookup live conversations by `boundSessionName`, and the
17
+ router demotes completed non-awaiting conversations through the existing
18
+ `onSessionEnd` lifecycle path. Conversations still waiting on a remote reply stay
19
+ untouched, and the reverse lookup is side-effect-free so a completion event for
20
+ one session cannot archive unrelated stale threads.
21
+
22
+ ## What to Tell Your User
23
+
24
+ Threadline conversations will now leave the active set as soon as their worker
25
+ session actually completes, instead of waiting for the old stale cleanup window.
26
+ This makes active-thread state match reality sooner while preserving
27
+ conversations that are still waiting for a peer response.
28
+
29
+ ## Summary of New Capabilities
30
+
31
+ | Capability | How to Use |
32
+ |-----------|-----------|
33
+ | Prompt Threadline retirement on session completion | Automatic when a bound worker session emits `sessionComplete` |
34
+ | Awaiting-reply preservation | Automatic — conversations waiting for the peer are not demoted by worker completion |
35
+
36
+ ## Evidence
37
+
38
+ - **Focused local tests:** `npm test -- tests/unit/threadline/ThreadlineRouter.test.ts tests/unit/threadline/ThreadResumeMap.test.ts` — 77/77 passed after rebasing onto `v1.3.118`.
39
+ - **Full PR matrix before main refresh:** docs, repo invariants, type, all 8 unit shards on node 20/22, build, integration, e2e, worktree trailer verify, and Vercel passed on PR #568 before the branch was refreshed onto `v1.3.118`.
40
+ - **Side-effects review:** `upgrades/side-effects/threadline-session-complete-retirement.md`.
@@ -0,0 +1,36 @@
1
+ # Side-effects — EmbeddingProvider idle-unload
2
+
3
+ **Change:** `EmbeddingProvider` disposes its loaded ONNX pipeline after
4
+ `idleUnloadMs` (default 5 min) of no `embed()` call, lazily reloading on the next
5
+ embed. New `inFlight` guard, rolling idle timer, and a public `dispose()`.
6
+
7
+ ## Behavioral side-effects
8
+
9
+ - **First embed after a long idle costs ~1-3s more** (a model reload). All
10
+ subsequent embeds during active use are unaffected — the idle timer resets on
11
+ every embed, so a busy agent keeps the model resident. Memory search/index are
12
+ not latency-critical, so the occasional reload is an acceptable trade for
13
+ eliminating the idle thread-pool spin (~3.6%-44% of a core).
14
+ - **`isReady` is now dynamic.** Previously it returned true forever once loaded;
15
+ now it returns false while the model is unloaded between idle periods. No caller
16
+ uses `isReady` to gate embeds (verified) — `embed()` lazily re-initializes
17
+ regardless — so this is observational only.
18
+ - **Embeddings are unchanged.** Verified live: dispose→reload→embed produces a
19
+ byte-identical vector. Unloading/reloading never alters output.
20
+ - **Default-ON.** The production singleton constructs with defaults, so all agents
21
+ get 5-min idle-unload on update. Set `idleUnloadMs: 0` (constructor config) to
22
+ restore the prior keep-resident behavior.
23
+
24
+ ## Blast radius
25
+
26
+ - Scoped to `src/memory/EmbeddingProvider.ts`. SemanticMemory / MemoryIndex /
27
+ TopicMemory consume it transparently (they call `embed()`/`embedBatch()`, which
28
+ handle reload internally). No call-site changes.
29
+ - The idle timer is `unref()`'d — it can never keep the process alive.
30
+ - No config schema, hook, or HTTP changes. No new dependencies.
31
+
32
+ ## Migration parity
33
+
34
+ None required — internal runtime logic in `src/`. No agent-installed file
35
+ (`.claude/settings.json`, `.instar/config.json`, CLAUDE.md template, hook, skill)
36
+ changes. Existing agents receive it on their normal version update.
@@ -0,0 +1,94 @@
1
+ # Side-Effects Review — Threadline Session Completion Retirement
2
+
3
+ **Version / slug:** `threadline-session-complete-retirement`
4
+ **Date:** `2026-05-30`
5
+ **Author:** `instar-codey`
6
+ **Second-pass reviewer:** `Feynman`
7
+
8
+ ## Summary of the change
9
+
10
+ This change wires `SessionManager`'s `sessionComplete` event into Threadline thread lifecycle handling. `ThreadResumeMap` gains a reverse lookup from bound tmux session name to live thread records, `ThreadlineRouter` gains `onSessionComplete()` to demote completed non-awaiting thread workers through the existing `onSessionEnd()` path, and `src/commands/server.ts` attaches the listener after the router is constructed. The touched decision point is session lifecycle state transition: whether a live Threadline conversation should leave the active set promptly when its worker session completes.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `ThreadlineRouter.onSessionComplete()` — add — decides which bound threads are demoted on a completed worker session.
15
+ - `ThreadResumeMap.getBySessionName()` — add — lookup primitive only; it returns live conversation state and does not decide.
16
+ - `server.ts sessionComplete listener` — add — forwards a completed tmux session name and optional resume UUID into the router.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+
22
+ No block/allow surface. The change does not reject inbound or outbound messages. The main over-demotion risk is a thread that is still waiting for the peer being moved to `idle`; this is guarded by checking the underlying ConversationStore state and skipping `awaiting-reply`.
23
+
24
+ ---
25
+
26
+ ## 2. Under-block
27
+
28
+ No block/allow surface. The remaining miss is any completed worker whose thread record lacks `boundSessionName`; there is no safe reverse mapping for that case, so it will still rely on the 24h stale-retirement backstop. A session with no captured `claudeSessionId` is still demoted using the existing stored UUID.
29
+
30
+ ---
31
+
32
+ ## 3. Level-of-abstraction fit
33
+
34
+ This is at the Threadline router/session lifecycle layer, which already owns `onSessionEnd(threadId, uuid, sessionName)`. The new method does not reimplement demotion; it reverse-maps session name to thread ids and delegates each eligible thread to the existing router primitive. The raw `awaiting-reply` guard stays near `ConversationStore` state because the legacy `ThreadResumeEntry` view maps `awaiting-reply` to `active`.
35
+
36
+ ---
37
+
38
+ ## 4. Signal vs authority compliance
39
+
40
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
41
+
42
+ **Does this change hold blocking authority with brittle logic?**
43
+
44
+ - [ ] No — this change produces a signal consumed by an existing smart gate.
45
+ - [x] No — this change has no block/allow surface.
46
+ - [ ] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history or equivalent).
47
+ - [ ] Yes, with brittle logic — STOP. Reshape the design. Brittle detectors must not own block authority. Either promote the logic to smart-gate level (with proper context) or demote it to a signal that feeds an existing smart gate.
48
+
49
+ The logic is a lifecycle transition, not a message judgment. It does not block, filter, or score content. The only deterministic guard is whether the stored lifecycle state is `awaiting-reply`, which is a hard state-machine invariant rather than a brittle semantic detector.
50
+
51
+ ---
52
+
53
+ ## 5. Interactions
54
+
55
+ - **Shadowing:** The listener runs after `SessionManager` emits completion and before the existing 24h stale-retirement backstop would archive stale active entries. It does not shadow inbound routing; future peer replies can still reactivate an idle thread through the normal resume path.
56
+ - **Double-fire:** Multiple completion events for the same tmux session are idempotent enough: already-idle entries may be saved as idle again, and `awaiting-reply` entries remain untouched. The log only emits when at least one matching thread is found.
57
+ - **Races:** The reverse lookup reads active conversation records without running stale-retirement as a lookup side effect, then each demotion uses `ThreadResumeMap.save()`, which writes through the ConversationStore mutation path. A concurrent state change to `awaiting-reply` after lookup remains the main race; the window is small and the next inbound/outbound action can restore state.
58
+ - **Feedback loops:** Demoting a thread reduces active-list noise and prevents the stale backstop from being the first lifecycle update. It does not spawn sessions or send messages.
59
+
60
+ ---
61
+
62
+ ## 6. External surfaces
63
+
64
+ Other agents see more accurate Threadline active-thread state sooner after a worker finishes. Persistent state changes in `{stateDir}/threadline/conversations.json` move eligible records from `active`/`open` to `idle` and refresh the stored session UUID when available. There are no Telegram, Slack, GitHub, Cloudflare, or wire-format changes. Timing depends on `SessionManager` completion detection, but that event already drives other lifecycle systems.
65
+
66
+ ---
67
+
68
+ ## 7. Rollback cost
69
+
70
+ Hot-fix release: revert the listener and helper methods, then ship the next patch. No migration is required. Existing records demoted to `idle` remain valid and can still be resumed/reactivated on the next peer reply; no agent state repair is expected.
71
+
72
+ ---
73
+
74
+ ## Conclusion
75
+
76
+ The change is clear to ship after second-pass review. It narrows the stale-active Threadline window from the 24h backstop to the worker's actual completion event while preserving the important `awaiting-reply` state. Focused regressions cover completed-session demotion and awaiting-reply preservation.
77
+
78
+ ---
79
+
80
+ ## Second-pass review (if required)
81
+
82
+ **Reviewer:** `Feynman`
83
+ **Independent read of the artifact:** `concern resolved`
84
+
85
+ Initial concern: `getBySessionName()` called `retireInactive()` before filtering by session, so a `sessionComplete` event could archive unrelated stale live conversations, including stale `awaiting-reply` records. Resolution: remove stale-retirement from the reverse lookup and add regression coverage that an unrelated stale `awaiting-reply` thread remains untouched during completion handling.
86
+
87
+ ---
88
+
89
+ ## Evidence pointers
90
+
91
+ - `npx vitest run tests/unit/threadline/ThreadlineRouter.test.ts` — 36/36 passed.
92
+ - `npx vitest run tests/unit/threadline/ThreadResumeMap.test.ts` — 41/41 passed.
93
+ - `npm test -- tests/unit/threadline/ThreadlineRouter.test.ts tests/unit/threadline/ThreadResumeMap.test.ts` — 77/77 passed after merging `v1.3.118` main into the PR branch and preparing `v1.3.119` release metadata.
94
+ - `npm run build` — passed; local signing key absent, lockfile signing skipped as documented transitional state.