instar 1.3.104 → 1.3.106

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,64 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **The Framework-Onboarding mentor now reports WHY a tick failed, and its Stage-A
8
+ step is more resilient.** The mentor's Stage-A step spawns a tiny tool-less
9
+ session to compose the next coaching message, then captures it. When that spawn
10
+ failed (common on a busy multi-agent box — session-cap pressure / load), the
11
+ whole tick collapsed to an opaque `stage-a-failed` with the real cause thrown
12
+ away, so it was undiagnosable from `GET /mentor/status`. Now: the real error is
13
+ surfaced into `lastResult.error`, and the Stage-A compose-session spawn retries
14
+ once with a short backoff before failing with a clear, specific message.
15
+
16
+ **Multi-machine: the SEND side of the cross-machine command channel now works on
17
+ a normally-created machine.** The private per-machine key used to sign every
18
+ machine-to-machine command was being loaded from the wrong filename
19
+ (`signing-private.pem`), but a normally-created machine stores it as
20
+ `signing-key.pem`. So a normal machine signed its outbound commands with an empty
21
+ key and the send silently failed — it could receive from peers but never call out
22
+ to them (check a peer's presence, hand off a conversation, move one). The loader
23
+ now reads the canonical filename, falling back to the legacy one. This is the
24
+ send-side companion to the recent command-channel auth fix; together they make
25
+ the cross-machine pool actually work over the network. (Found on real hardware:
26
+ the laptop couldn't see the mini until its signing key loaded.)
27
+
28
+ ## What to Tell Your User
29
+
30
+ Only relevant if you run the (off-by-default) Framework-Onboarding mentor. It now
31
+ recovers from a transient compose-session spawn failure, and when it does fail it
32
+ tells you exactly why (visible on the mentor status page) instead of an
33
+ unexplained "stage-a-failed."
34
+
35
+ If you run across two machines: this release fixes the last piece that kept a
36
+ normally-created machine from being able to reach its peers — a second machine
37
+ will now correctly show as online and be eligible to receive conversations, and
38
+ moving a conversation between machines works. Both machines need to update.
39
+
40
+ ## Summary of New Capabilities
41
+
42
+ - `GET /mentor/status` → `lastResult.error` now carries the real Stage-A failure
43
+ cause (`MentorTickResult.error`), instead of swallowing it.
44
+ - `spawnStageA` retries the compose-session spawn once (transient resilience) and
45
+ throws a clear `stage-a-spawn-failed: … — <cause>` on persistent failure.
46
+ - The MeshRpcClient signing-key loader reads the canonical `signing-key.pem`
47
+ (falling back to legacy `signing-private.pem`), so a normally-created machine
48
+ can sign — and therefore SEND — cross-machine commands.
49
+
50
+ ## Evidence
51
+
52
+ - `tests/unit/MentorOnboardingRunner.test.ts` (+1): a `spawnStageA` that throws
53
+ now yields `lastResult = {ran:false, reason:'stage-a-failed', error:<msg>}` —
54
+ asserts the cause is surfaced (was swallowed by a bare catch in
55
+ `runMentorTick`). Full mentor/stage suite (113 tests / 9 files) green;
56
+ `tsc --noEmit` clean.
57
+ - `tests/unit/mesh-signing-key-resolution.test.ts` — the loader reads the
58
+ canonical `signing-key.pem`, tries it before the legacy `signing-private.pem`,
59
+ and the canonical name matches `MachineIdentity.SIGNING_KEY_FILE`.
60
+ - Found on real hardware (laptop + mini): the laptop's puller recorded the mini
61
+ online over the signed HTTP channel only after its signing key loaded; before,
62
+ it signed with an empty key and the send threw.
63
+ - Side-effects: `upgrades/side-effects/mentor-stage-a-robustness.md`,
64
+ `upgrades/side-effects/mesh-signing-key-filename-fix.md`.
@@ -0,0 +1,39 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Fixed the root cause of the Framework-Onboarding mentor's Stage-A failures: the
8
+ compose prompt grew past tmux's command-line limit.** Stage-A spawns a session to
9
+ compose the next coaching message, passing the prompt — which embeds the whole
10
+ growing mentor↔mentee conversation — as a `tmux new-session` command-line
11
+ argument. tmux has a command-length limit (~12-16KB); once the accumulated
12
+ history crossed it, `tmux new-session` failed with "command too long", so the
13
+ step failed and the mentee got nothing (it worked early on, then broke as history
14
+ piled up). The conversation history fed into the Stage-A prompt is now bounded to
15
+ the most-recent ~6KB (with an explicit "older conversation elided" marker), so the
16
+ spawn stays under tmux's limit no matter how long the mentorship runs.
17
+
18
+ ## What to Tell Your User
19
+
20
+ Only relevant if you run the (off-by-default) Framework-Onboarding mentor. Its
21
+ Stage-A step is now reliable for long-running mentorships — the prompt can't grow
22
+ past the limit that was making it fail. No behaviour change for short
23
+ conversations.
24
+
25
+ ## Summary of New Capabilities
26
+
27
+ - `buildStageAContext` bounds the conversation history at 6000 chars (keeps the
28
+ most-recent exchanges + a marker), keeping the Stage-A prompt under tmux's
29
+ command-line limit — fixes the `stage-a-failed` root cause.
30
+
31
+ ## Evidence
32
+
33
+ - `tests/unit/MentorStageA.test.ts` (+1): an ~80KB history produces a prompt
34
+ under 12KB that keeps the most-recent exchange, carries the elision marker, and
35
+ preserves the prompt structure. Full mentor/stage suite (114 tests / 9 files)
36
+ green; `tsc` + `npm run lint` clean.
37
+ - Cold tmux repro: a 120KB argument to `tmux new-session` fails with "command too
38
+ long"; 12KB passes — confirming the 6KB history cap is safely below the limit.
39
+ - Side-effects: `upgrades/side-effects/mentor-stage-a-prompt-bound.md`.
@@ -0,0 +1,55 @@
1
+ # Side-effects review — Mentor Stage-A: bound the compose prompt
2
+
3
+ **Spec:** `docs/specs/MENTOR-STAGE-A-PROMPT-BOUND-SPEC.md`
4
+ **Change:** `src/monitoring/MentorStageA.ts` (`buildStageAContext`) + unit test
5
+ **Class:** mentor reliability fix (root cause of `stage-a-failed`).
6
+
7
+ ## What changed
8
+
9
+ `buildStageAContext` now caps `surface.threadlineHistory` at `MAX_HISTORY_CHARS =
10
+ 6000`, keeping the most-recent tail + an explicit `older conversation elided`
11
+ marker, so the assembled Stage-A prompt can't exceed tmux's `new-session`
12
+ command-line limit (~12-16KB) — the confirmed cause of the spawn failure.
13
+
14
+ ## Blast radius
15
+
16
+ - **`buildStageAContext` callers:** only `runMentorTick` (Stage-A). The function
17
+ signature/output shape is unchanged (still returns the prompt string); only the
18
+ history portion is bounded when it exceeds 6000 chars.
19
+ - **Mentor behaviour:** unchanged for short histories (the bound only engages
20
+ above 6000 chars). For long histories, the prompt now contains the recent
21
+ exchanges + a marker instead of the full transcript — the mentor still has the
22
+ agenda + recent state to decide the next action.
23
+ - **Two-hats isolation / leak detection / agenda logic:** untouched.
24
+ - **Public API / DB schema / config / other spawns:** none changed. Regular
25
+ (non-mentor) session spawns are unaffected (their prompts are small and were
26
+ never the growth vector).
27
+
28
+ ## What could break (and why it doesn't)
29
+
30
+ - **Mentor lacks older context?** By design — the recent 6KB + agenda + status
31
+ carry the "what's next" decision; older middle history is least relevant.
32
+ - **A single huge recent message?** The hard char cap bounds the history block
33
+ regardless of message boundaries.
34
+ - **Leak detector seeing a truncated transcript?** It runs on the Stage-A
35
+ *output* transcript, not on this input prompt — unaffected.
36
+
37
+ ## Security
38
+
39
+ No new external input / network / auth / fs surface. Pure in-memory string bound.
40
+
41
+ ## Migration parity
42
+
43
+ Server-internal monitoring code — every agent gets it by running the new build.
44
+ No PostUpdateMigrator entry required.
45
+
46
+ ## Rollback
47
+
48
+ Revert the commit. No persisted state, schema, or API contract affected.
49
+
50
+ ## Tests
51
+
52
+ `tests/unit/MentorStageA.test.ts` (+1): an ~80KB history → prompt < 12KB, keeps
53
+ the most-recent exchange, carries the elision marker, preserves structure. Full
54
+ mentor/stage suite (114 tests / 9 files) green; `tsc` + `npm run lint` clean.
55
+ Cold tmux repro confirmed the limit (120KB → "command too long"; 12KB passes).
@@ -0,0 +1,57 @@
1
+ # Side-effects review — Mentor Stage-A: surface error + harden spawn
2
+
3
+ **Spec:** `docs/specs/MENTOR-STAGE-A-ROBUSTNESS-SPEC.md`
4
+ **Changes:** `src/scheduler/MentorOnboardingTick.ts`, `src/scheduler/MentorOnboardingRunner.ts`, `src/server/AgentServer.ts` (+ unit test)
5
+ **Class:** mentor reliability + diagnosability fix.
6
+
7
+ ## What changed
8
+
9
+ 1. `MentorTickResult` gains `error?: string`.
10
+ 2. `runMentorTick`'s bare `catch {}` around `spawnStageA` → `catch (err)` that
11
+ captures the message into the returned result (`{…, error}`) + the finding
12
+ title.
13
+ 3. `MentorOnboardingRunner.startTick().catch` adds the same `error` field (for a
14
+ rejected `tick()`).
15
+ 4. `spawnStageA` (AgentServer.ts) wraps `spawnSession` in a single retry with a
16
+ 3s backoff; on persistent failure throws `stage-a-spawn-failed: … — <cause>`.
17
+
18
+ ## Blast radius
19
+
20
+ - **`/mentor/status` consumers:** purely additive — `lastResult` may now include
21
+ an `error` string on the failure path. No field removed/renamed.
22
+ - **Healthy mentor ticks:** unchanged — new code runs only on the catch/failure
23
+ path or a throwing spawn.
24
+ - **Stage-A two-hats isolation:** unchanged — still spawns with the empty
25
+ `STAGE_A_ALLOWED_TOOLS` grant; only error handling + a retry added.
26
+ - **Public API / DB schema / config:** none changed. No new route (the
27
+ AgentServer change is inside an existing closure — E2E-PAIRING exempt).
28
+
29
+ ## What could break (and why it doesn't)
30
+
31
+ - **The retry doubling spawn load?** Only on a spawn that already failed once,
32
+ and capped at 2 attempts with a 3s gap — negligible, and only on the
33
+ already-failing path.
34
+ - **`error` field typing:** flows through `MentorRunResult extends
35
+ Omit<MentorTickResult,'reason'>` which keeps `error?`. `tsc` clean.
36
+ - **Masking a persistent failure:** no — a persistent failure still throws after
37
+ 2 attempts, now with the cause in the message.
38
+
39
+ ## Security
40
+
41
+ No new external input / network / auth / fs surface. The surfaced `error` is an
42
+ internal spawn/timeout message (no secrets).
43
+
44
+ ## Migration parity
45
+
46
+ Server-internal scheduler/server code — every agent gets it by running the new
47
+ build. No PostUpdateMigrator entry required.
48
+
49
+ ## Rollback
50
+
51
+ Revert the commit. No persisted state, schema, or API contract affected.
52
+
53
+ ## Tests
54
+
55
+ `tests/unit/MentorOnboardingRunner.test.ts` (+1): a throwing `spawnStageA` now
56
+ surfaces `lastResult.error` with the real message (was swallowed). Full
57
+ mentor/stage suite — 113 tests across 9 files — green; `tsc --noEmit` clean.
@@ -0,0 +1,54 @@
1
+ # Side-effects review — mesh signing-key filename fix (the SEND-side break)
2
+
3
+ ## What was happening (real-hardware, 2026-05-30)
4
+
5
+ After the /mesh/rpc auth exemption (v1.3.104) deployed to BOTH machines, the Mac
6
+ mini STILL showed offline on the laptop (the router). The mini→laptop presence
7
+ pull worked (the mini's log: `recorded 1 peer(s) online: m_cc2ec651`), but the
8
+ laptop→mini direction never fired and the mini never even RECEIVED the laptop's
9
+ `session-status` call.
10
+
11
+ Root cause: the server-boot loader for `localSigningKeyPem` — the private key the
12
+ `MeshRpcClient` signs every outbound machine-to-machine command with — hard-coded
13
+ the filename `signing-private.pem`. But `MachineIdentity` writes the signing key
14
+ as `signing-key.pem` (its `SIGNING_KEY_FILE`). So a normally-created install had
15
+ `signing-private.pem` ABSENT → `localSigningKeyPem` stayed `''` → the
16
+ `MeshRpcClient` signed with an empty key → the send threw → the machine could NOT
17
+ pull presence from / deliver to / transfer to any peer. It still RECEIVED fine,
18
+ because the receive path verifies the OTHER machine's key (which it has), not its
19
+ own. That produced the exact asymmetry observed: the mini (propagated with the
20
+ non-canonical `signing-private.pem`) could send; the laptop (canonical
21
+ `signing-key.pem`) could not — two filename bugs lining up.
22
+
23
+ Confirmed by giving the laptop a `signing-private.pem` copy of its own key +
24
+ restarting: the laptop immediately began recording the mini online over HTTP, and
25
+ the pool showed both machines online.
26
+
27
+ ## The fix
28
+
29
+ - **`src/commands/server.ts`** — the loader now reads the CANONICAL
30
+ `signing-key.pem` first, falling back to the legacy/propagated
31
+ `signing-private.pem`, so BOTH layouts load a key. (The lease transport already
32
+ used `idMgr.loadSigningKey()`, which reads the canonical name — only this
33
+ MeshRpcClient loader was on the wrong filename.)
34
+
35
+ ## Blast radius
36
+
37
+ - **Pure fix to a boot-time read on the multi-machine path.** A single-machine
38
+ agent never constructs a MeshRpcClient, so it is unaffected. A machine that
39
+ already had `signing-private.pem` (propagated) keeps working via the fallback.
40
+ A machine with the canonical `signing-key.pem` (the common case) now loads its
41
+ key and can SEND cross-machine commands — the bug fix.
42
+ - **No new config / route / schema / hook / skill → no migration.** Server code;
43
+ existing agents pick it up on the next release. This is the SEND-side companion
44
+ to the /mesh/rpc auth exemption (the receive-side fix) — both were needed for
45
+ the cross-machine pool to function over the wire.
46
+ - **Separately, the propagation path should write the canonical `signing-key.pem`**
47
+ (it currently writes `signing-private.pem`); that is a propagation-side cleanup
48
+ tracked separately. This fix makes the loader tolerant of both regardless.
49
+
50
+ ## Tests
51
+
52
+ - `tests/unit/mesh-signing-key-resolution.test.ts` — the loader reads the
53
+ canonical `signing-key.pem`, tries it BEFORE the legacy `signing-private.pem`
54
+ (fallback order), and the canonical name matches `MachineIdentity.SIGNING_KEY_FILE`.