instar 1.3.70 → 1.3.72

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-28T22:59:01.360Z",
5
- "instarVersion": "1.3.70",
4
+ "generatedAt": "2026-05-29T01:04:44.834Z",
5
+ "instarVersion": "1.3.72",
6
6
  "entryCount": 193,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -11,7 +11,7 @@
11
11
  "domain": "identity",
12
12
  "sourcePath": "src/core/PostUpdateMigrator.ts",
13
13
  "installedPath": ".instar/hooks/instar/session-start.sh",
14
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
14
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
15
15
  "since": "2025-01-01"
16
16
  },
17
17
  "hook:dangerous-command-guard": {
@@ -20,7 +20,7 @@
20
20
  "domain": "safety",
21
21
  "sourcePath": "src/core/PostUpdateMigrator.ts",
22
22
  "installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
23
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
23
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
24
24
  "since": "2025-01-01"
25
25
  },
26
26
  "hook:grounding-before-messaging": {
@@ -29,7 +29,7 @@
29
29
  "domain": "safety",
30
30
  "sourcePath": "src/core/PostUpdateMigrator.ts",
31
31
  "installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
32
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
32
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
33
33
  "since": "2025-01-01"
34
34
  },
35
35
  "hook:compaction-recovery": {
@@ -38,7 +38,7 @@
38
38
  "domain": "identity",
39
39
  "sourcePath": "src/core/PostUpdateMigrator.ts",
40
40
  "installedPath": ".instar/hooks/instar/compaction-recovery.sh",
41
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
41
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
42
42
  "since": "2025-01-01"
43
43
  },
44
44
  "hook:external-operation-gate": {
@@ -47,7 +47,7 @@
47
47
  "domain": "safety",
48
48
  "sourcePath": "src/core/PostUpdateMigrator.ts",
49
49
  "installedPath": ".instar/hooks/instar/external-operation-gate.js",
50
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
50
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
51
51
  "since": "2025-01-01"
52
52
  },
53
53
  "hook:deferral-detector": {
@@ -56,7 +56,7 @@
56
56
  "domain": "safety",
57
57
  "sourcePath": "src/core/PostUpdateMigrator.ts",
58
58
  "installedPath": ".instar/hooks/instar/deferral-detector.js",
59
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
59
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:post-action-reflection": {
@@ -65,7 +65,7 @@
65
65
  "domain": "evolution",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
68
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
68
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:external-communication-guard": {
@@ -74,7 +74,7 @@
74
74
  "domain": "safety",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
77
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
77
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:scope-coherence-collector": {
@@ -83,7 +83,7 @@
83
83
  "domain": "coherence",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
86
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
86
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-checkpoint": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
95
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
95
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:free-text-guard": {
@@ -101,7 +101,7 @@
101
101
  "domain": "safety",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
104
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
104
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:claim-intercept": {
@@ -110,7 +110,7 @@
110
110
  "domain": "coherence",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
113
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
113
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept-response": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
122
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
122
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:stop-gate-router": {
@@ -128,7 +128,7 @@
128
128
  "domain": "safety",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
131
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
131
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:auto-approve-permissions": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
140
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
140
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1457,7 +1457,7 @@
1457
1457
  "type": "subsystem",
1458
1458
  "domain": "server",
1459
1459
  "sourcePath": "src/server/AgentServer.ts",
1460
- "contentHash": "fb958a979cb6284188fa299d4e3dcc347a7d752a07a5cfe181f7919e18e0b1c0",
1460
+ "contentHash": "88cfc6c3397a04f59444a78fe95cd23a3d1805a64476f175123fbb59373c1a07",
1461
1461
  "since": "2025-01-01"
1462
1462
  },
1463
1463
  "subsystem:session-manager": {
@@ -1489,7 +1489,7 @@
1489
1489
  "type": "subsystem",
1490
1490
  "domain": "updates",
1491
1491
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1492
- "contentHash": "c410faa704330138b3bb15f71c78506ee86da3b1a7838328dea59888cd7f451a",
1492
+ "contentHash": "cf0c9bfcfa78083bb0a5f6cd2e0f1f175b16204178f373c95cca674e33eae52a",
1493
1493
  "since": "2025-01-01"
1494
1494
  },
1495
1495
  "subsystem:scheduler": {
@@ -0,0 +1,58 @@
1
+ # Instar Upgrade Guide — NEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Mentor cycle: Stage-A prompt capture no longer silently returns empty.**
8
+
9
+ The mentor tick's Stage-A step spawns a short haiku session to *generate* the
10
+ mentor prompt, then reads that session's output before delivering it to the
11
+ mentee. Two bugs made that capture silently return empty — so a tick "ran" but
12
+ delivered nothing (`delivered:false`, `stageAMsgLen:0`):
13
+
14
+ 1. **Transcript lookup got lost in a huge directory tree.** The Claude path of
15
+ `extractMenteeReplyFromTranscript` located the session transcript with a
16
+ *recursive* walk of `~/.claude/projects` bounded by a 10,000-step guard. On
17
+ a busy agent that folder holds far more than 10k nested directories, so the
18
+ depth-first walk burned its whole budget inside an unrelated subtree and gave
19
+ up *before reaching the right file* — even though the file sits exactly one
20
+ level down. Replaced with `findClaudeTranscriptShallow`, which scans only the
21
+ immediate children (the layout Claude actually uses:
22
+ `<projects>/<encoded-cwd>/<sessionId>.jsonl`). Finds it in milliseconds.
23
+
24
+ 2. **Capture raced the session lifecycle.** `spawnStageA` read the tmux pane
25
+ *after* the session completed (the reaper had already torn it down → empty),
26
+ and `claudeSessionId` is only flushed to the session record at completion —
27
+ a beat after the session leaves the running list. Now Stage-A (a) keeps the
28
+ last non-empty pane snapshot *while the session is alive* as a fallback, and
29
+ (b) prefers the persisted transcript, polling for `claudeSessionId` to land
30
+ and the final assistant block to be written (up to the session's own 5-min
31
+ max). It uses the *exact* `<claudeSessionId>.jsonl` — never newest-by-mtime,
32
+ since the agent runs many concurrent Claude sessions and mtime would grab an
33
+ unrelated transcript.
34
+
35
+ ## What to Tell Your User
36
+
37
+ The scheduled mentor tick now reliably generates and delivers its prompt — the
38
+ last gap that made the tick "fire but deliver nothing" is closed. No config
39
+ changes; behavior is automatic.
40
+
41
+ ## Summary of New Capabilities
42
+
43
+ | Capability | How to Use |
44
+ |-----------|-----------|
45
+ | Robust Stage-A prompt capture | Automatic — the generated mentor prompt is read from the session transcript (exact `<claudeSessionId>.jsonl`, depth-1 scan) with a capture-while-alive pane fallback. |
46
+ | `findClaudeTranscriptShallow` | Pure, exported helper in `SessionReplyExtractor` — locates a Claude transcript by session id at its real depth-1 layout (no recursive walk). Unit-tested incl. a regression for the deep-subtree case. |
47
+
48
+ ## Evidence
49
+
50
+ - Unit: `tests/unit/SessionReplyExtractor.test.ts` — 5 new cases for
51
+ `findClaudeTranscriptShallow`, including a regression that builds a 50-deep
52
+ unrelated subtree + 30 sibling encoded-cwd dirs and proves the depth-1 file is
53
+ still found (the exact shape the old recursive walk failed on).
54
+ - Integration: `tests/integration/mentor-routes.test.ts` (the `/mentor/tick`
55
+ route) green; full mentor/mentee suite (63 tests) green.
56
+ - Live: tick-driven round-trip verified — `/mentor/tick` → Stage-A generates
57
+ the prompt → delivered to the mentee → a clean-prose reply persisted to
58
+ `mentor-replies.jsonl` (evidence captured in the PR).
@@ -0,0 +1,65 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Topic-spam, locked down structurally.** A second Telegram topic-flood (after
8
+ the 2026-05-22 sentinel flood) hit a live agent: a housekeeping feature raised one
9
+ attention item per failed peer-resolution every sweep, and because
10
+ `createAttentionItem` spawns a brand-new forum topic per item, that became a wall
11
+ of topics. PR #495 already fixed *that feature* (a per-peer escalation cooldown).
12
+ This change adds the **structural backstop** so the *class* of bug can't recur no
13
+ matter which feature misbehaves next.
14
+
15
+ **`AttentionTopicGuard` — a per-source + global circuit breaker** at the one
16
+ chokepoint, `TelegramAdapter.createAttentionItem`. If a single attention
17
+ `sourceContext` exceeds its topic budget within a rolling window (default 3 / 10
18
+ min) — or, via a **global ceiling**, all sources collectively exceed it — further
19
+ **non-critical** items are COALESCED into ONE reused "notices coalesced" topic and
20
+ recorded in `state/attention-suppressed.jsonl` — never a wall. Invariants:
21
+ HIGH/URGENT/CRITICAL items are **never** coalesced (critical messages always get
22
+ their own topic), and **no item is dropped** — only its per-item topic is withheld;
23
+ the item is still in the attention store. The global ceiling makes the protection
24
+ hold even when a feature varies its `sourceContext` per item to dodge a per-source
25
+ budget. Ships **enabled by default in code**, so every fleet agent is protected on
26
+ the dist update with zero config.
27
+
28
+ ## What to Tell Your User
29
+
30
+ - The "wall of topics popping up out of nowhere" problem is now fixed at its root.
31
+ If any background feature ever tries to flood the chat again, it's capped
32
+ automatically: a few topics at most, then everything folds into one quiet
33
+ "notices coalesced" topic, with the detail kept in the logs. Genuinely critical
34
+ alerts (HIGH/URGENT) are never affected — they always come through on their own.
35
+ - No action or config needed; it's on by default.
36
+
37
+ ## Summary of New Capabilities
38
+
39
+ | Capability | How to Use |
40
+ |-----------|-----------|
41
+ | Attention topic-flood circuit breaker | Automatic, on by default. Tune via `messaging[].config.attentionTopicGuard` = `{ "enabled": true, "windowMs": 600000, "maxTopicsPerSource": 3, "maxTopicsGlobal": 8 }`. |
42
+ | Suppressed-notice audit trail | When a source is coalesced, each item is logged to `state/attention-suppressed.jsonl` (size-capped). Read it to answer "why are my notices grouped / where did topic X go?" |
43
+
44
+ ## Migration Notes
45
+
46
+ Pure `src/` logic, **default-ON in code** (`AttentionTopicGuard` +
47
+ `TelegramAdapter`) — no agent-installed file changed, so every agent receives the
48
+ protection through the normal dist update with nothing to patch. A
49
+ `migrateClaudeMd()` entry backfills the **Topic-Flood Guard** awareness section
50
+ into existing agents' CLAUDE.md (idempotent). The redrive-specific offender fix
51
+ shipped separately in PR #495.
52
+
53
+ ## Evidence
54
+
55
+ - Unit: `tests/unit/AttentionTopicGuard.test.ts` (11) — budget / global-cap /
56
+ critical-bypass (case-insensitive) / per-source isolation / sustained-flood
57
+ single episode / post-silence reset / config-validation / key-eviction;
58
+ `tests/unit/PostUpdateMigrator-topicFloodGuard.test.ts` (2) — migrator backfill
59
+ + idempotency.
60
+ - Integration: `tests/integration/attention-topic-flood-guard.test.ts` (4) — REAL
61
+ `TelegramAdapter`: a flooding source is capped at budget+1 topics, HIGH
62
+ bypasses, concurrent coalesced items create ONE topic, a different source is
63
+ unaffected, no item dropped, audit log populated.
64
+ - E2E: `tests/e2e/attention-topic-flood-guard-lifecycle.test.ts` (1) — fleet
65
+ default (NO config) still caps a flood exactly (migration-parity guarantee).
@@ -0,0 +1,74 @@
1
+ # Side-Effects Review — Attention Topic-Flood Guard
2
+
3
+ **Spec:** `docs/specs/attention-topic-flood-guard.md` (converged, 4-reviewer panel)
4
+ **Change:** per-source + global forum-topic circuit breaker at
5
+ `TelegramAdapter.createAttentionItem` (the structural backstop). The redrive
6
+ offender fix shipped separately in PR #495 (merged); the redrive edits originally
7
+ bundled here were dropped to defer to it.
8
+ **Files:** `src/messaging/AttentionTopicGuard.ts` (new),
9
+ `src/messaging/TelegramAdapter.ts`, `src/core/PostUpdateMigrator.ts`, tests,
10
+ `tests/unit/feature-delivery-completeness.test.ts`, `upgrades/NEXT.md`.
11
+
12
+ ## Phase 4 — the seven questions
13
+
14
+ 1. **Over-block.** The guard never blocks an agent action or drops an item — it
15
+ only changes *delivery form* (own topic vs. one coalesced topic + a log line)
16
+ for **non-critical** items. The one legitimate behavior it changes: a
17
+ non-critical source raising >3 items / 10 min has the surplus coalesced. That
18
+ is the *intended* behavior (the operator asked for exactly this) and is tunable
19
+ per adapter. HIGH/URGENT are never affected. No agent capability is rejected.
20
+
21
+ 2. **Under-block.** Before the global ceiling was added, a source varying its
22
+ `sourceContext` per item would have dodged the per-source budget — the review
23
+ caught this; the global cap + key eviction now bound it regardless of source
24
+ cardinality. Remaining under-block: a flood composed entirely of HIGH/URGENT
25
+ items is not coalesced (by design — critical items must always be visible). A
26
+ feature mis-marking routine noise as HIGH would still flood; that is a
27
+ mis-classification bug in that feature, not in the guard, and is the correct
28
+ place to fix it.
29
+
30
+ 3. **Level-of-abstraction fit.** Correct layer: the breaker sits at the single
31
+ chokepoint (`createAttentionItem`) where every attention item becomes a topic,
32
+ so it covers all current and future callers. It is deliberately *below* the
33
+ tone-gate authority on the `/attention` route (a transport-mechanics
34
+ rate-counter, not a content-judgment filter). The per-feature redrive fix
35
+ (PR #495) is at the feature layer; the guard is the substrate backstop — two
36
+ layers, by intent.
37
+
38
+ 4. **Signal vs authority.** Compliant. The guard holds no blocking authority over
39
+ agent behavior or information flow; it is a delivery *shaper* (the rate-counter
40
+ / transport-dedup carve-out in `docs/signal-vs-authority.md`), the same class as
41
+ `SentinelNotifier`. It never withholds a critical notice and never drops an
42
+ item.
43
+
44
+ 5. **Interactions.** Audited against `SentinelNotifier` (separate path → the
45
+ single reused system topic; no collision — it never flows through the guard) and
46
+ the `agent-attention-topic` (not registered in the attention maps, untouched).
47
+ Restart interaction (coalesced items vs. `loadAttentionItems` reverse-map
48
+ corruption) and concurrency (double-create race) were both found by review and
49
+ fixed. `updateAttentionStatus` no longer closes a shared topic when one
50
+ coalesced sibling resolves (coalesced items aren't in the per-item maps).
51
+
52
+ 6. **External surfaces.** User-visible change: a flooding source now produces one
53
+ "🔁 …: notices coalesced (flood guard)" topic instead of a wall; the rest goes
54
+ to `state/attention-suppressed.jsonl`. Fleet-wide via dist update, default-ON,
55
+ no config. No new network calls; no new external dependency. The CLAUDE.md
56
+ awareness section is backfilled to existing agents (idempotent migrator).
57
+
58
+ 7. **Rollback cost.** Cheap. Single config flag:
59
+ `messaging[].config.attentionTopicGuard.enabled = false` restores per-item-topic
60
+ behavior. No data migration, no agent-state repair (the guard holds only
61
+ in-memory counters). Worst case is a `src/` revert + patch release.
62
+
63
+ ## Phase 5 — second-pass review (REQUIRED: touches a "guard" + outbound messaging)
64
+
65
+ An independent four-reviewer convergence panel (adversarial/security, integration,
66
+ architecture, scalability) audited the change against the diff. It raised 7
67
+ material concerns (global-cap dodge, `/ack`+shared-topic-close, case-sensitive
68
+ critical bypass, double-create race, hex plaintext to unverified address, NaN
69
+ config disable, unbounded audit log) plus framing notes. **6 of the 7 were fixed
70
+ in code this iteration; the 7th (hex plaintext) was a finding on the redrive edits
71
+ that are now deferred to the merged PR #495.** Full record:
72
+ `docs/specs/attention-topic-flood-guard.convergence.md`. **Concur with the review —
73
+ the design after this iteration addresses every raised concern; no blocking
74
+ authority on brittle logic remains.**
@@ -0,0 +1,76 @@
1
+ # Side-effects review — Stage-A transcript capture (mentor cycle)
2
+
3
+ **Scope**: Close the last known mentor-cycle gap. The tick-driven path "ran but
4
+ delivered nothing" because the Stage-A prompt-generation session's output was
5
+ captured by (1) a recursive `~/.claude/projects` walk that exhausted its 10k
6
+ guard before reaching the file, and (2) a post-reap tmux read that raced the
7
+ session lifecycle. Make Stage-A capture deterministic and clean-prose.
8
+
9
+ **Files touched**:
10
+ - `src/monitoring/SessionReplyExtractor.ts` — new pure exported
11
+ `findClaudeTranscriptShallow(projectsDir, claudeSessionId)`: scans the
12
+ immediate children of `projects/` (+ root, defensively) for the exact
13
+ `<id>.jsonl`. Replaces the inline recursive walk. Adds `fs`/`path` imports.
14
+ - `src/server/AgentServer.ts` —
15
+ - `extractMenteeReplyFromTranscript` is now `async`; its Claude path polls
16
+ up to ~240s for `claudeSessionId` (re-fetched from `state.getSession`) and
17
+ the final assistant block, using `findClaudeTranscriptShallow`. Codex path
18
+ unchanged (rollout-by-mtime, returns before the poll).
19
+ Caller (mentee role-handler) now `await`s it.
20
+ - `spawnStageA` keeps the last non-empty pane snapshot while the session is
21
+ alive (fallback), then prefers the transcript via the same helper; falls
22
+ back to the snapshot/pane only if no transcript is found. Adds two
23
+ `[stage-a-diag]` log lines (low frequency — once per tick).
24
+ - `tests/unit/SessionReplyExtractor.test.ts` — 5 new cases for
25
+ `findClaudeTranscriptShallow` (depth-1 hit, root hit, deep-subtree
26
+ regression, no-descent guarantee, null-safety).
27
+ - `upgrades/NEXT.md` — release note (patch bump).
28
+
29
+ **Under-block (false negatives — does it ever now FAIL to capture a real reply?)**:
30
+ - The Claude path requires the exact `<claudeSessionId>.jsonl`. If a session
31
+ never flushes `claudeSessionId` (e.g. killed before completion), the poll
32
+ returns null after ~240s — but `spawnStageA` then falls back to the
33
+ capture-while-alive pane snapshot, so a completed session still yields its
34
+ text. Net: strictly more robust than before (which had only the post-reap
35
+ read). The mentee role-handler retains its own tmux-capture fallback too.
36
+ - The 240s poll only runs on the Claude branch. A codex mentee (Codey) returns
37
+ from the codex branch immediately — no added latency to the proven reply leg.
38
+
39
+ **Over-block (false positives — does it capture the WRONG thing?)**:
40
+ - Deliberately does NOT use newest-by-mtime for Claude: the agent runs many
41
+ concurrent Claude sessions, so mtime would grab an unrelated transcript. The
42
+ exact-id lookup is the correct disambiguator. This is the key reason the
43
+ helper is not symmetric with the codex (mtime) path.
44
+ - `findClaudeTranscriptShallow` intentionally does NOT recurse: a transcript
45
+ buried >1 level deep is not matched. Claude never writes there, so this is
46
+ correct, not a gap (unit-tested: `does NOT descend into nested subdirs`).
47
+
48
+ **Level-of-abstraction fit**: The transcript-locating logic moved OUT of an
49
+ inline closure in `AgentServer` INTO the `SessionReplyExtractor` module, which
50
+ already owns transcript reading. `AgentServer` keeps only the I/O orchestration
51
+ (poll + fall back). The pure locator is now independently unit-testable — the
52
+ root-cause fix has a real regression test instead of being trapped in a private
53
+ method.
54
+
55
+ **Signal vs authority**: No authority change. Stage-A capture is signal-only
56
+ into the mentor tick; it gates nothing else. `isMenteeBusy` /
57
+ OutstandingPromptTracker (the actual tick safe-window) is untouched.
58
+
59
+ **Interactions**:
60
+ - `extractMenteeReplyFromTranscript` becoming async ripples to exactly one
61
+ caller (the mentee role-handler), which now awaits it — verified by tsc.
62
+ - The mentee codex reply leg (the proven `FULL-…` round-trip path) is behavior-
63
+ unchanged: same codex rollout extraction, no poll.
64
+ - Adds two `console.log('[stage-a-diag] …')` lines in `spawnStageA`. They fire
65
+ once per mentor tick (low frequency) and carry only session id / framework /
66
+ lengths — no transcript content (no Stage-A leak surface).
67
+
68
+ **Migration parity**: Pure code change in shipped `dist/` — no agent-installed
69
+ files (settings hooks, config defaults, CLAUDE.md template, hook scripts,
70
+ skills) touched. Existing agents pick it up via the normal auto-update of the
71
+ `instar` package. No `PostUpdateMigrator` entry required.
72
+
73
+ **Spec**: MENTOR-LIVE-READINESS-SPEC §Recipient side (Stage-A is the
74
+ prompt-generation step that feeds `deliverToMentee`). This fix is the third
75
+ instance of the same capture-race class (#480 fixed the mentee-reply and
76
+ isMenteeBusy instances); it completes the §Recipient-side capture story.