instar 1.3.143 → 1.3.145

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-31T04:44:08.984Z",
5
- "instarVersion": "1.3.143",
4
+ "generatedAt": "2026-05-31T05:20:35.246Z",
5
+ "instarVersion": "1.3.145",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
14
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
23
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
32
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
41
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
50
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
59
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
68
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
77
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
86
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
95
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
104
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
113
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
122
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
131
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
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": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
140
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1489,7 +1489,7 @@
1489
1489
  "type": "subsystem",
1490
1490
  "domain": "updates",
1491
1491
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1492
- "contentHash": "41126b695ee560ee55679931cb2ab2f98e0b2bcdce2195d6cfaf57ef395a3cd4",
1492
+ "contentHash": "670633a7fa7c3dbb761a454ff75d87c85aaff82eae0fcc824f15011be032705b",
1493
1493
  "since": "2025-01-01"
1494
1494
  },
1495
1495
  "subsystem:scheduler": {
@@ -1521,7 +1521,7 @@
1521
1521
  "type": "subsystem",
1522
1522
  "domain": "communication",
1523
1523
  "sourcePath": "src/lifeline/TelegramLifeline.ts",
1524
- "contentHash": "03f9fc147f3e520e114e32df64fe6ba2a69f58664c3cb94f495532d47f5bcded",
1524
+ "contentHash": "6105f8a126654011c81e900be2c049ff52dc1b9b369831432ad910a9650dc84e",
1525
1525
  "since": "2025-01-01"
1526
1526
  },
1527
1527
  "subsystem:orphan-process-reaper": {
@@ -0,0 +1,53 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Agent hard-sleep can now actually stop and wake the server (dark + dry-run).**
8
+ The previous step taught an idle agent how to DECIDE whether it is safe to sleep but
9
+ never acted. This step builds the mechanism that acts on that decision: when sleep is
10
+ enabled and the agent is deeply idle and safe, its heavy background server is
11
+ stopped to save the machine's resources, and it wakes the instant a message arrives.
12
+
13
+ The handshake reuses the proven restart lifecycle: the decision layer writes a
14
+ sleep-request file, the supervisor stops the server and marks it intentionally
15
+ asleep (so it is NOT treated as a crash and is NOT auto-restarted), and an inbound
16
+ message makes the lifeline write a wake-request that the supervisor honors by
17
+ respawning the server. The waiting message is held in the existing durable queue and
18
+ delivered once the server is healthy, so nothing is lost. A safety marker means that
19
+ even if an outside watchdog force-restarts the agent mid-sleep, it reads the marker
20
+ on startup and stays asleep instead of fighting it. The existing crash-recovery
21
+ behavior is unchanged except in the one case where the agent chose to sleep, proven
22
+ by the existing supervisor tests staying green.
23
+
24
+ ## What to Tell Your User
25
+
26
+ Nothing changes on any normal agent — this ships off by default, and the file that
27
+ triggers a sleep is only ever written when sleep is explicitly turned on. The future
28
+ ability it builds toward: a completely idle agent quiets its server to save your
29
+ machine, then wakes the moment you message it, holding your message until it is back
30
+ up. Turning it on is a deliberate step we would validate on one test agent first.
31
+
32
+ ## Summary of New Capabilities
33
+
34
+ - ServerSupervisor honors a sleep-request by stopping the server and entering an
35
+ intentionally-asleep state that suppresses auto-respawn; honors a wake-request by
36
+ respawning. A slept-marker keeps a rebooted (or watchdog-bounced) supervisor asleep.
37
+ - The decision layer writes a TTL-stamped sleep-request only in live mode.
38
+ - The lifeline writes a wake-request on the next inbound message when the server is
39
+ asleep; the existing forward-retry queue replays the held message after wake.
40
+ - All gated by the existing monitoring.agentSleep config — OFF + dry-run by default.
41
+
42
+ ## Evidence
43
+
44
+ - `tests/unit/ServerSupervisor-sleep-wake.test.ts` — sleep stops + marks + enters
45
+ slept; no-request no-op; expired-request ignored; wake respawns + clears; wake-when-
46
+ not-slept no-op; idempotent re-sleep; boot-marker signal.
47
+ - `tests/unit/agentSleepWake.test.ts` — marker present writes the wake-request; no
48
+ marker is a no-op (steady-state awake never writes).
49
+ - `tests/unit/SleepController.test.ts` — sleepRequestWriter writes the TTL-stamped flag.
50
+ - Regression: the existing ServerSupervisor-handshake / supervisor-health-check /
51
+ supervisor-cpu-starvation suites stay green — the slept short-circuit is the only
52
+ loop-flow change and is a no-op until a live sleep-request is honored.
53
+ - Side-effects: `upgrades/side-effects/agent-hard-sleep-mechanism.md`.
@@ -0,0 +1,54 @@
1
+ ---
2
+ review-convergence: complete
3
+ approved: true
4
+ approved-by: echo (standing 12h deploy mandate, topic 13435; live-test-found bug blocking #28 enablement — Justin watching)
5
+ ---
6
+
7
+ # Upgrade Guide — vNEXT
8
+
9
+ <!-- bump: patch -->
10
+
11
+ ## What Changed — codex autonomous-loop Stop hook no longer fails on approve (stdout must be JSON)
12
+
13
+ Live-test finding (2026-05-31): when the codex autonomous-loop driver (#28) flag is on and a
14
+ codex session reaches a terminal stop (e.g. it finished the task and met its completion
15
+ promise), the shared `autonomous-stop-hook.sh` printed a human-readable approve message
16
+ ("✅ Autonomous mode: Completion promise detected…") to **stdout**. Codex treats ANY non-JSON
17
+ on a Stop hook's stdout as a failure and logged "Stop hook (failed) — invalid stop hook JSON
18
+ output" on every such stop. The unit test missed it because it only exercised the flag-on path
19
+ WITH an active job that BLOCKS (the one path that legitimately prints JSON) — never an
20
+ approve/exit path.
21
+
22
+ The fix: in codex mode the hook routes all human-facing approve/status text to **stderr**
23
+ (new `emit()` helper); the only thing it ever writes to stdout is the `{"decision":"block"…}`
24
+ JSON. Claude behavior is unchanged (it still surfaces those messages on stdout to the user).
25
+
26
+ ## Summary of New Capabilities
27
+
28
+ - `emit()` in `autonomous-stop-hook.sh`: codex mode → stderr, Claude mode → stdout. Applied to
29
+ the duration-expired, emergency-stop, completion-condition, and completion-promise approve
30
+ paths (the 4 places that wrote plain text to stdout).
31
+ - Migration marker bumped `CODEX_LOOP_ENABLED` → `codex-stdout-json-safe` so existing #28
32
+ installs (which already carry `CODEX_LOOP_ENABLED`) re-deploy the FIXED hook. Customized
33
+ hooks (no stock fingerprint) are still left untouched.
34
+
35
+ ## What to Tell Your User
36
+
37
+ If you run a codex agent with the autonomous-loop driver turned on, it will no longer log a
38
+ failed Stop hook when a run finishes — the hook now keeps its codex output strictly to the
39
+ machine-readable form codex expects. Nothing to configure.
40
+
41
+ ## Evidence
42
+
43
+ - Repro (live): a codex session completed a 3-step task, hit its completion promise, and the
44
+ pane showed "Stop hook (failed) — error: hook returned invalid stop hook JSON output".
45
+ - Fix: `emit()` routes approve/status text to stderr in codex mode; stdout carries only the
46
+ block-decision JSON.
47
+ - Regression test: `tests/unit/autonomous-stop-hook-codex-gate.test.ts` — codex approve-path
48
+ (emergency stop) emits NO non-JSON to stdout; Claude approve-path STILL writes it to stdout
49
+ (both sides; 6 tests pass).
50
+ - Migration parity: `tests/unit/PostUpdateMigrator-autonomousStopHook.test.ts` (7 tests pass);
51
+ marker bump re-deploys the fixed hook to existing codex agents.
52
+ - `tsc --noEmit` + `npm run lint` clean.
53
+ - Spec: `docs/specs/codex-stop-hook-stdout-json-safe.md` (+ `.eli16.md`).
54
+ - Side-effects: `upgrades/side-effects/codex-stop-hook-stdout-json-safe.md`.
@@ -0,0 +1,115 @@
1
+ # Side-Effects Review — Agent hard-sleep stop+wake mechanism (Stage B, slice 2)
2
+
3
+ **Version / slug:** `agent-hard-sleep-mechanism`
4
+ **Date:** `2026-05-31`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `adversarial review performed — found a critical brick (wake-trigger gated on health) + a broken manual escape hatch; BOTH fixed + tested before commit. Concurs the dark code is inert. Enablement remains the reviewed gate (live on a test agent with Justin watching).`
7
+
8
+ ## Summary of the change
9
+
10
+ Acts on the slice-1 SleepController verdict: in live mode it writes
11
+ `state/sleep-requested.json`; the ServerSupervisor honors it by stopping the server
12
+ tmux session and entering a `slept` state; the health loop then short-circuits
13
+ (suppressing auto-respawn) and only watches for `state/wake-requested.json`, which
14
+ the TelegramLifeline writes on the next inbound message → the supervisor respawns
15
+ the server and the existing forward-retry queue replays the buffered message. Files:
16
+ `SleepController.ts` (+`sleepRequestWriter`), `ServerSupervisor.ts`
17
+ (checkSleepRequest/checkWakeRequest/`slept` guard/boot-marker), `TelegramLifeline.ts`
18
+ (+`requestWakeIfSlept` via the pure `agentSleepWake.ts` helper). No new config (the
19
+ slice-1 `monitoring.agentSleep` gates it), no new route, no agent-installed file.
20
+
21
+ ## Decision-point inventory
22
+
23
+ - `ServerSupervisor` health-loop control flow — modify — adds ONE short-circuit: `if (slept) { checkWakeRequest(); return; }` at the loop top.
24
+ - `ServerSupervisor.checkSleepRequest` / `checkWakeRequest` — add — the stop/respawn handlers (mirror checkRestartRequest).
25
+ - `ServerSupervisor` boot — modify — reads `slept-marker.json` to stay asleep across a supervisor reboot.
26
+ - `SleepController.requestSleep` consumer — add — `sleepRequestWriter` (live-mode only).
27
+ - `TelegramLifeline.forwardToServer` — pass-through — prepends a non-blocking `requestWakeIfSlept()` side-call.
28
+
29
+ ---
30
+
31
+ ## 1. Over-block
32
+
33
+ No block/allow surface over messages. The only "blocking-like" behavior is the
34
+ `slept` short-circuit suppressing auto-respawn — which is exactly intended and gated
35
+ on `slept`, set only after a live sleep-request is honored. A genuine crash (slept
36
+ === false) still flows through the unchanged `evaluateUnhealthyServer` path.
37
+
38
+ ## 2. Under-block
39
+
40
+ Could the server fail to wake (the brick risk)? **The adversarial second-pass review
41
+ caught a real brick in the first cut and it was fixed:** the wake-trigger was
42
+ originally placed inside `forwardToServer()`, which is gated on `supervisor.healthy`
43
+ — but a slept server is not healthy, so an inbound message took the "server down →
44
+ queue" path and never wrote `wake-requested.json` → the server would never wake.
45
+ **Fix:** `requestWakeIfSlept()` now runs at the TOP of `processUpdate()`, before any
46
+ health gate, so EVERY inbound update writes the wake flag when slept (covered by the
47
+ helper test + the corrected call-site). The review also caught that `/lifeline
48
+ restart` (the manual escape hatch) didn't clear the slept-marker, so a manual restart
49
+ re-read the marker and re-slept — **fixed** via `ServerSupervisor.wakeFromSleep()`,
50
+ called from both `/lifeline restart` and `/lifeline reset` (tested).
51
+
52
+ Remaining wake robustness: the supervisor checks the wake flag every 10s while slept;
53
+ a single missed flag write self-heals on the next inbound; a fleet-watchdog mid-sleep
54
+ bounce stays asleep via the boot-marker AND wakes on the next inbound. The
55
+ scheduler-wake (a cron job due during sleep) is the one tracked gap (conservative-null
56
+ for now), enablement-gated — dark default means no real agent is affected.
57
+
58
+ ## 3. Level-of-abstraction fit
59
+
60
+ Correct. The mechanism reuses the proven `restart-requested.json` lifecycle shape
61
+ (file flag consumed in the health loop) rather than inventing a new lifecycle; the
62
+ stop is the existing tmux `kill-session`, the wake is the existing `spawnServer()`.
63
+ The lifeline wake-trigger is extracted into a pure, unit-tested helper
64
+ (`writeWakeRequestIfSlept`) rather than buried in the un-testable heavy class.
65
+
66
+ ## 4. Signal vs authority compliance
67
+
68
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
69
+
70
+ - [x] No — the SleepController remains the decision-maker (signal, with all guards);
71
+ this slice is the mechanism it drives. The stop IS an authority (stops a process),
72
+ but it is gated exactly like a planned restart: positive proof (the slice-1
73
+ would-sleep verdict with every guard), ships OFF + dry-run, never fires unless
74
+ enabled && !dryRun, and KEEPs-awake on any ambiguity upstream.
75
+
76
+ ## 5. Interactions
77
+
78
+ - **Shadowing:** the `slept` short-circuit runs before the health/restart logic; it
79
+ is a no-op when `slept===false` (always, until a live sleep-request lands), so it
80
+ cannot shadow the existing restart/health paths in normal operation. Verified by
81
+ the unchanged `ServerSupervisor-handshake` + `supervisor-health-check` suites.
82
+ - **Double-fire:** sleep is idempotent (`checkSleepRequest` early-returns when
83
+ already slept); wake is idempotent (early-returns when not slept). The
84
+ `sleep-requested`/`wake-requested` flags are consumed on read.
85
+ - **Races:** the fleet watchdog vs sleep — resolved by the boot-marker (reboot stays
86
+ asleep). The lifeline writing a wake flag while the supervisor is mid-respawn —
87
+ harmless (the supervisor clears `slept` first, so a redundant wake flag is consumed
88
+ next tick as a no-op).
89
+ - **Feedback loops:** none — the flags are one-shot, consumed by the consumer.
90
+
91
+ ## 6. External surfaces
92
+
93
+ - **Install base:** pure additive source, no config/route/agent-installed-file
94
+ change → every agent picks it up on update; behavior is byte-identical until
95
+ `monitoring.agentSleep` is enabled live. The slice-1 dark default holds.
96
+ - **Other agents / external systems:** none.
97
+ - **Persistent state:** three transient `state/*.json` flags + one `slept-marker.json`
98
+ (best-effort, consumed/removed by the supervisor).
99
+ - **Timing:** the existing 10s health tick; no new timer.
100
+
101
+ ## 7. Rollback cost
102
+
103
+ Pure additive code, no config/migration. Revert → `checkSleepRequest` /
104
+ `checkWakeRequest` / the `slept` guard / the lifeline side-call disappear; the
105
+ supervisor + lifeline behave exactly as before. No persistent state needs cleanup
106
+ (the flags are transient). No user-visible change during rollback (dark by default).
107
+
108
+ ## Conclusion
109
+
110
+ This review confirmed the mechanism is a tightly-scoped, additive layer on the
111
+ proven restart lifecycle, with ONE load-bearing change (the `slept` short-circuit)
112
+ that is a no-op until a live sleep-request is honored and is regression-covered by
113
+ the existing supervisor suites. The brick risk is closed at multiple layers
114
+ (per-forward wake re-write, 10s wake poll, boot-stay-asleep marker). It ships dark;
115
+ the enablement (turning sleep ON) is the reviewed validation step on a test agent.
@@ -0,0 +1,40 @@
1
+ # Side-effects — codex Stop hook stdout JSON-safe (emit())
2
+
3
+ ## 1. What files/state does this change touch at runtime?
4
+ The shared `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh` (a built-in skill hook).
5
+ On update, `migrateAutonomousStopHookTopicKeyed` re-writes the deployed copy for stock agents.
6
+ No config keys, no new files, no new state.
7
+
8
+ ## 2. Does it change any decision (block vs approve)?
9
+ No. The hook's keep-going-vs-stop logic is untouched. Only the CHANNEL of human-facing
10
+ approve/status text changes, and only in codex mode (stdout → stderr). The block-decision JSON
11
+ is unchanged and still on stdout.
12
+
13
+ ## 3. Does it affect the Claude path?
14
+ No — Claude behavior is byte-for-byte preserved (approve messages still print to stdout). A
15
+ dedicated test asserts the Claude approve-path still writes "Emergency stop detected" to stdout.
16
+
17
+ ## 4. Migration parity — do existing agents get it?
18
+ Yes — that's the point. The marker is bumped `CODEX_LOOP_ENABLED` → `codex-stdout-json-safe`,
19
+ so every existing #28 install re-deploys the fixed hook on its next update (the old marker
20
+ would have skipped them since they already carry it). Customized hooks (no stock fingerprint)
21
+ are left untouched. Idempotent (re-running sees the new marker → skips). Covered by 7 migration
22
+ tests.
23
+
24
+ ## 5. Could it spam / flood / burn resources?
25
+ No. The change is a stdout→stderr redirect of a few echo lines in approve paths. No loops, no
26
+ network, no new process, no message send.
27
+
28
+ ## 6. Rollback / off-switch?
29
+ The #28 driver itself remains flag-gated (`autonomousSessions.codexLoopDriver`, default off) —
30
+ this fix just makes the hook safe to run when that flag IS on. Reverting this PR restores the
31
+ prior hook (and the bug); no residual state. The fix is inert for Claude agents and for codex
32
+ agents with the flag off (they hit the early `exit 0` dark-gate before any approve path).
33
+
34
+ ## 7. Concurrency / ordering?
35
+ None — the hook is a short-lived per-stop process; no shared state introduced.
36
+
37
+ ## Blast radius
38
+ Minimal + targeted. One built-in hook script (4 approve paths: stdout→stderr in codex mode) +
39
+ one migration-marker bump + test coverage. No runtime route, sentinel, schema, or decision
40
+ path changes. Unblocks safe enablement of the already-shipped (dark) #28 driver.