instar 1.3.104 → 1.3.105

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,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`.