instar 1.3.200 → 1.3.201

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.200",
3
+ "version": "1.3.201",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-02T07:00:19.416Z",
5
- "instarVersion": "1.3.200",
6
- "entryCount": 193,
4
+ "generatedAt": "2026-06-02T07:52:52.700Z",
5
+ "instarVersion": "1.3.201",
6
+ "entryCount": 194,
7
7
  "entries": {
8
8
  "hook:session-start": {
9
9
  "id": "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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
14
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
23
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
32
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
41
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
50
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
59
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
68
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
77
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
86
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
95
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
104
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
113
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
122
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
131
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
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": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
140
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1092,6 +1092,14 @@
1092
1092
  "contentHash": "b0475554bde6729d2d9a1f8005dcada6a8e4c767203d8293c09a212f9bf0a196",
1093
1093
  "since": "2025-01-01"
1094
1094
  },
1095
+ "template:emit-session-clock.sh": {
1096
+ "id": "template:emit-session-clock.sh",
1097
+ "type": "template",
1098
+ "domain": "operations",
1099
+ "sourcePath": "src/templates/scripts/emit-session-clock.sh",
1100
+ "contentHash": "c83cff5728bef544c20ca4a8bce432981175161a4ac2272e112edc612949806d",
1101
+ "since": "2025-01-01"
1102
+ },
1095
1103
  "template:git-sync-gate.sh": {
1096
1104
  "id": "template:git-sync-gate.sh",
1097
1105
  "type": "template",
@@ -1489,7 +1497,7 @@
1489
1497
  "type": "subsystem",
1490
1498
  "domain": "updates",
1491
1499
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1492
- "contentHash": "b180f4f8bc074315fa049333883cb6defef23774007b7165a0aec6cae9be67f4",
1500
+ "contentHash": "e8270e118c93a86da1982ba20f6432a566e59f6ae77c3e04c05d81fb3d79b386",
1493
1501
  "since": "2025-01-01"
1494
1502
  },
1495
1503
  "subsystem:scheduler": {
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+ # emit-session-clock.sh — render the SESSION CLOCK line for an active time-boxed
3
+ # session, so an agent always sees how much of a timed run remains and never
4
+ # winds down early. Signal-only: pure stdout, never blocks.
5
+ #
6
+ # Spec: docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md (Component 2). The math
7
+ # of record (clock-skew clamping, status) lives in SessionClock.compute() (TS);
8
+ # this script only FORMATS values it is given (render) or queries the route.
9
+ #
10
+ # Modes:
11
+ # render <startedISO> <durationSec> <elapsedSec> <remainingSec> <label>
12
+ # Prints ONE SESSION CLOCK line from the given (already-sanitized, already-
13
+ # computed) values. The caller (autonomous-stop-hook) has already resolved
14
+ # the record + computed elapsed/remaining, so there is NO re-resolution —
15
+ # the injected clock can never disagree with the hook's expiry verdict.
16
+ # query <topic> <port> <auth>
17
+ # Curls GET /session/clock?topic=<topic> and prints the SESSION CLOCK line
18
+ # for the first active session (or nothing if none / server unreachable).
19
+
20
+ humanize() { # seconds -> "Xh Ym" / "Ym" / "Xs"
21
+ local s="${1:-0}"
22
+ case "$s" in (*[!0-9-]*) s=0 ;; esac
23
+ [ "$s" -lt 0 ] 2>/dev/null && s=0
24
+ local h=$((s / 3600)) m=$(((s % 3600) / 60))
25
+ if [ "$h" -gt 0 ]; then echo "${h}h ${m}m"; elif [ "$m" -gt 0 ]; then echo "${m}m"; else echo "${s}s"; fi
26
+ }
27
+
28
+ render_line() { # startedISO durationSec elapsedSec remainingSec label
29
+ local dur="$2" el="${3:-0}" rem="$4" label="$5"
30
+ local pct="" remstr="" lbl=""
31
+ if [ -n "$dur" ] && [ "$dur" -gt 0 ] 2>/dev/null; then pct=" ($((el * 100 / dur))% elapsed)"; fi
32
+ if [ -n "$rem" ]; then remstr=" · $(humanize "$rem") remaining"; fi
33
+ if [ -n "$label" ]; then lbl=" [$label]"; fi
34
+ echo "⏱ SESSION CLOCK${lbl}: $(humanize "$el") elapsed${remstr}${pct}. Do NOT conclude the session is over while remaining is large."
35
+ }
36
+
37
+ MODE="${1:-}"
38
+ shift 2>/dev/null || true
39
+
40
+ case "$MODE" in
41
+ render)
42
+ render_line "${1:-}" "${2:-}" "${3:-0}" "${4:-}" "${5:-}"
43
+ ;;
44
+ query)
45
+ TOPIC="${1:-}"; PORT="${2:-}"; AUTH="${3:-}"
46
+ [ -z "$PORT" ] && exit 0
47
+ URL="http://localhost:${PORT}/session/clock"
48
+ [ -n "$TOPIC" ] && URL="${URL}?topic=${TOPIC}"
49
+ RESP=$(curl -s --max-time 3 -H "Authorization: Bearer ${AUTH}" "$URL" 2>/dev/null)
50
+ [ -z "$RESP" ] && exit 0
51
+ printf '%s' "$RESP" | python3 -c "
52
+ import sys, json
53
+ try:
54
+ d = json.load(sys.stdin)
55
+ except Exception:
56
+ sys.exit(0)
57
+ sessions = d.get('sessions') or []
58
+ if not sessions:
59
+ sys.exit(0)
60
+ c = sessions[0]
61
+ label = c.get('label') or ''
62
+ el = c.get('elapsedHuman') or '?'
63
+ rem = c.get('remainingHuman')
64
+ pct = c.get('percentElapsed')
65
+ line = '⏱ SESSION CLOCK' + ((' [' + label + ']') if label else '') + ': ' + el + ' elapsed'
66
+ if rem is not None:
67
+ line += ' · ' + rem + ' remaining'
68
+ if pct is not None:
69
+ line += ' (' + str(pct) + '% elapsed)'
70
+ line += '. Do NOT conclude the session is over while remaining is large.'
71
+ print(line)
72
+ " 2>/dev/null
73
+ ;;
74
+ *)
75
+ exit 0
76
+ ;;
77
+ esac
78
+ exit 0
@@ -0,0 +1,46 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Ships the shared routine that renders your session clock, and installs it on every agent.
9
+
10
+ Step 1 added the `/session/clock` endpoint (how long have I been running / how much is left). This adds `emit-session-clock.sh` — the small routine that turns that into a one-line "⏱ SESSION CLOCK: Nh elapsed · Mh remaining (NN%)" string. It has a render mode (format already-computed values) and a query mode (ask the endpoint). The hooks that call it on every turn land next (tracked in issue #682).
11
+
12
+ Your autonomous agent now sees how much of its time window is left — on every turn.
13
+
14
+ This is the fix for the failure where an agent in a long autonomous run wound down far too early because it had lost track of time. The autonomous stop-hook now feeds back a rich `⏱ SESSION CLOCK: Nh elapsed · Mh remaining (NN%)` line on every continuation, rendered from the hook's own elapsed/remaining numbers, so the agent always knows where it is in the window before deciding anything.
15
+
16
+ The session clock now shows on your own messages too, not just autonomous turns.
17
+
18
+ Step 2b wired the clock into autonomous continuations; this wires the second site: when you message the agent, the UserPromptSubmit hook now also surfaces "⏱ SESSION CLOCK: Nh elapsed · Mh remaining (NN%)" if a time-boxed session is active. Both call sites of the shared routine (render + query) are now live — completing the per-turn, every-turn time awareness.
19
+
20
+ ## What to Tell Your User
21
+
22
+ Nothing to do. This is plumbing for the time-awareness feature — a small script that gets installed automatically. You'll see its effect once the per-turn hooks are wired (next step): your agent will quote real elapsed/remaining time instead of guessing.
23
+
24
+ Nothing to do. If you run timed autonomous sessions, the agent will now quote accurate elapsed/remaining time as it works instead of guessing — and won't conclude a session is "done" with hours left. Existing agents get the updated hook automatically on update.
25
+
26
+ Nothing to do. If a timed session is running, your agent now sees its elapsed/remaining time on your turns too, so "how far along are we?" always has a real answer.
27
+
28
+ ## Summary of New Capabilities
29
+
30
+ - New `.instar/scripts/emit-session-clock.sh` — renders the SESSION CLOCK line. `render` mode formats values the caller already computed (no re-resolution); `query` mode curls `GET /session/clock`. Signal-only (pure stdout, never blocks), and only ever echoes the already-sanitized label, never a raw goal.
31
+ - Installed on new agents (init) and existing agents (always-overwrite migration), so the whole fleet has the routine ready for the per-turn wiring.
32
+
33
+ - The autonomous stop-hook injects a SESSION CLOCK line into every blocked continuation, rendered by `emit-session-clock.sh` from the hook's own computed numbers (no re-resolution → it can never disagree with the hook's own duration-expiry check). Additive and fail-safe: unbounded runs or a missing script simply omit the segment.
34
+ - Delivered to existing agents via the autonomous-skill re-deploy migration (marker bumped to `CLOCK_SEG`); customized hooks are left untouched.
35
+
36
+ - `telegram-topic-context.sh` (built-in UserPromptSubmit hook) now calls `emit-session-clock.sh query` after resolving port/auth/topic, surfacing the SESSION CLOCK line on user turns. Signal-only; emits nothing when no time-boxed session is active or the server is unreachable. Delivered to existing agents via the always-overwrite built-in-hook migration.
37
+
38
+ ## Evidence
39
+
40
+ - 7 golden tests (render/percent/clamp/formatting/unbounded/query-unreachable/no-op) + 3 migration tests (installs, always-overwrite, executable) green; secret-drop migration regression (13) green; `tsc --noEmit` clean.
41
+ - Spec: `docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md` (Component 2). Side-effects: `upgrades/side-effects/session-clock-step2a.md`. Call-site wiring: #682.
42
+
43
+ - Functional wiring test (`autonomous-stop-hook-session-clock.test.ts`): a blocked continuation of a timed run feeds back the SESSION CLOCK line (runs the real hook); fail-safe path with the script absent verified. `bash -n` clean; autonomous-stop-hook regression suites (P13/completion/topic-keyed, 23) green; `tsc --noEmit` clean.
44
+ - Spec: `docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md` (Component 2). Side-effects: `upgrades/side-effects/session-clock-step2b.md`. The user-turn injection site: #682.
45
+
46
+ - `telegram-topic-context-session-clock.test.ts` (2): generated hook carries the query call + passes `bash -n` (escaping guard). Regression (12) green; lint clean. Spec: Component 2.
@@ -0,0 +1,52 @@
1
+ # Side-Effects Review — Session Clock Step 2a (emit-session-clock.sh shared routine + delivery)
2
+
3
+ **Slug:** `session-clock-step2a`
4
+ **Date:** `2026-06-02`
5
+ **Author:** `echo`
6
+ **Tier:** 2 (driven by the converged + approved `ROBUST-SESSION-TIME-AWARENESS-SPEC.md`)
7
+ **Spec:** `docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md` (Component 2 — the shared routine)
8
+
9
+ ## Summary of the change
10
+
11
+ Ships the **shared time-awareness routine** and its delivery to every agent. Step 1 (merged #683) added the compute + `/session/clock` query surface; this adds the bash routine that formats the SESSION CLOCK line, plus install-everywhere. Its call sites (the two hooks) are the next sequenced step, tracked in #682.
12
+
13
+ - `src/templates/scripts/emit-session-clock.sh` — the routine (a real template file, like `telegram-reply.sh`/`secret-drop-retrieve.mjs`; NOT an inline TS string, so no escaping risk). Two modes:
14
+ - **render** `<startedISO> <durationSec> <elapsedSec> <remainingSec> <label>` — formats ONE SESSION CLOCK line from values the caller already computed (no re-resolution → can never disagree with the caller's own clock).
15
+ - **query** `<topic> <port> <auth>` — curls `GET /session/clock` and formats the first active session; prints nothing if none / server unreachable.
16
+ - `src/core/PostUpdateMigrator.ts` — `migrateScripts` installs it to `.instar/scripts/` always-overwrite (existing agents, mirrors the secret-drop-retrieve.mjs block).
17
+ - `src/commands/init.ts` — `installEmitSessionClock` installs it at scaffold time (new agents).
18
+
19
+ ## Decision-point inventory
20
+ - install policy: install-if-missing vs always-overwrite → always-overwrite (new, non-user-customizable shared routine; must stay current). Custom forks live elsewhere and are untouched.
21
+ - routine value source: render (caller's numbers) vs re-resolve → render uses the caller's already-computed values (no double-resolution; adversarial round-2 fix in the spec).
22
+
23
+ ## 1. Over-correction risk
24
+ None — purely additive. The routine is a standalone script; until a hook calls it (the next step, #682), it has zero runtime effect. Installing it changes no behavior.
25
+
26
+ ## 2. Under-correction risk
27
+ The routine is shipped + delivered + tested but its call sites (the two hooks) land with #682; that is the sequenced wiring step. The routine is complete and independently testable.
28
+
29
+ ## 3. Level-of-abstraction fit
30
+ A single shared routine both call sites will use; render math lives in it, the compute-of-record stays in `SessionClock.compute()` (TS). The bash routine only formats.
31
+
32
+ ## 4. Signal vs Authority
33
+ Tier0, signal-only: pure stdout, never blocks, never mutates. Appropriate.
34
+
35
+ ## 5. External surfaces
36
+ None new. query mode calls the existing Bearer-gated `/session/clock`. The routine echoes only the already-sanitized `label` the route/caller supplies (never a raw goal).
37
+
38
+ ## 6. Interactions with existing primitives
39
+ The `migrateScripts` addition mirrors the secret-drop-retrieve.mjs install exactly (verified: that test suite stays green). No change to existing scripts/hooks.
40
+
41
+ ## 7. Rollback cost
42
+ Trivial: remove the script + the two install calls. No state, no data migration.
43
+
44
+ ## Migration parity
45
+ - New agents: `installEmitSessionClock` (init.ts).
46
+ - Existing agents: `migrateScripts` always-overwrite install on the normal dist update.
47
+ - Both verified by tests.
48
+
49
+ ## Tests
50
+ - Unit/golden (`emit-session-clock.test.ts`, 7): render formats elapsed/remaining/percent; omits the remaining clause when unbounded; clamps negative elapsed to 0s; minutes/seconds formatting; no-label; query-unreachable → empty exit 0; unknown mode → no-op.
51
+ - Migration (`PostUpdateMigrator-emitSessionClock.test.ts`, 3): migrateScripts installs the script, always-overwrite (restores a stale copy, idempotent), mode 0o755.
52
+ - Regression: secret-drop migration (13) stays green; `tsc --noEmit` clean.
@@ -0,0 +1,49 @@
1
+ # Side-Effects Review — Session Clock Step 2b (autonomous-continuation injection)
2
+
3
+ **Slug:** `session-clock-step2b`
4
+ **Date:** `2026-06-02`
5
+ **Author:** `echo`
6
+ **Tier:** 2 (converged+approved `ROBUST-SESSION-TIME-AWARENESS-SPEC.md`)
7
+ **Spec:** `docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md` (Component 2 — the stop-hook render call site)
8
+
9
+ ## Summary of the change
10
+
11
+ Wires the time-awareness injection into the **autonomous stop-hook** — the actual fix for the wind-down-early incident. On every blocked continuation of a time-boxed run, the hook now feeds back a rich `⏱ SESSION CLOCK: Nh elapsed · Mh remaining (NN%)` line so the autonomous agent always SEES how much of its window remains.
12
+
13
+ - `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh`: after the existing `(${REMAINING_MIN}m remaining)` slot (kept untouched), an additive `${CLOCK_SEG}` segment is rendered by `emit-session-clock.sh render` **from the hook's OWN already-computed `STARTED_AT`/`DURATION_SECONDS`/`ELAPSED`/`REMAINING`** — no re-resolution, so the injected clock can never disagree with the hook's own duration-expiry verdict (adversarial round-2 concern). Fail-safe: if the script is absent or the run is unbounded (no `ELAPSED`), the segment is simply omitted.
14
+ - `src/core/PostUpdateMigrator.ts`: the existing `migrateAutonomousStopHookTopicKeyed` marker is bumped `p13_stop_allowed → CLOCK_SEG`, re-deploying the wired hook to existing agents (Migration Parity; customized hooks with no stock fingerprint untouched).
15
+
16
+ The query-mode call site (user-turn injection via `telegram-topic-context.sh`) is the second, separate injection site, tracked in #682; the routine's query mode is shipped + tested and ready for it.
17
+
18
+ ## Decision-point inventory
19
+ - placement: enrich the `(...)` slot vs add a segment → add a separate `${CLOCK_SEG}` segment (the existing slot is untouched = lowest risk).
20
+ - value source: render from the hook's own numbers vs re-resolve → render (no double-resolution).
21
+
22
+ ## 1. Over-correction risk
23
+ None — additive. The existing `(${REMAINING_MIN}m remaining)` slot and the expiry logic are unchanged; only an extra informational segment is appended.
24
+
25
+ ## 2. Under-correction risk
26
+ The user-turn (query-mode) site is the separate injection point tracked in #682; this change covers the autonomous-continuation site, which is the incident's actual failure mode.
27
+
28
+ ## 3. Level-of-abstraction fit
29
+ The hook calls the single shared `emit-session-clock.sh render`; the format lives there, the numbers come from the hook. No duplication.
30
+
31
+ ## 4. Signal vs Authority
32
+ Signal-only: the injected line is informational; it never changes the block/exit decision (which is the unchanged expiry logic).
33
+
34
+ ## 5. External surfaces
35
+ None. The hook reads the locally-installed script; nothing new is exposed.
36
+
37
+ ## 6. Interactions with existing primitives
38
+ Reuses the established `migrateAutonomousStopHookTopicKeyed` re-deploy mechanism (marker bump) and the hook's own computed values. `bash -n` clean; the existing autonomous-stop-hook tests (P13, completion-condition, topic-keyed) stay green.
39
+
40
+ ## 7. Rollback cost
41
+ Trivial: remove the `CLOCK_SEG` block + the two `${CLOCK_SEG}` insertions + revert the marker. No state.
42
+
43
+ ## Migration parity
44
+ - New agents: `installBuiltinSkills`/`init` ship the wired hook.
45
+ - Existing agents: `migrateAutonomousStopHookTopicKeyed` (marker `CLOCK_SEG`) re-deploys the wired hook on update; customized hooks untouched.
46
+
47
+ ## Tests
48
+ - `autonomous-stop-hook-session-clock.test.ts` (2): a blocked continuation of a timed run feeds back the SESSION CLOCK line (functional, runs the real hook with the installed script); fail-safe with the script absent (continuation still fires, segment omitted).
49
+ - Regression: `bash -n` clean; PostUpdateMigrator-autonomousStopHook (8), autonomous-completion-condition + evaluate-stop (15) green; `tsc --noEmit` clean.
@@ -0,0 +1,32 @@
1
+ # Side-Effects Review — Session Clock Step 2c (user-turn query injection)
2
+
3
+ **Slug:** `session-clock-step2c` · **Date:** `2026-06-02` · **Author:** `echo` · **Tier:** 2
4
+ **Spec:** `docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md` (Component 2 — the query call site)
5
+
6
+ ## Summary
7
+ Completes the time-awareness injection's SECOND call site: the `telegram-topic-context.sh`
8
+ UserPromptSubmit hook now, after resolving PORT + AUTH_TOKEN + TOPIC_ID, calls
9
+ `emit-session-clock.sh query` so the SESSION CLOCK line surfaces on the user's OWN turns too
10
+ (not just autonomous continuations). With Step 2b (the stop-hook render site), both call sites
11
+ of Component 2 are now wired — the routine's render AND query modes are both consumed.
12
+
13
+ - `src/core/PostUpdateMigrator.ts` `getTelegramTopicContextHook()`: an additive query call after
14
+ the existing CURRENT TIME block + AUTH resolution. Signal-only: emits nothing when no time-boxed
15
+ session is active or the server is unreachable. Guarded on the script existing.
16
+
17
+ ## 1-7 (brief)
18
+ Over/under-correction: additive; the CURRENT TIME block + history fetch are unchanged; emits only
19
+ when a clock exists. Abstraction: reuses the shared routine. Signal vs Authority: pure stdout, no
20
+ gate. External surfaces: calls the existing Bearer-gated `/session/clock`; only the already-sanitized
21
+ label is surfaced. Interactions: composes with the hook's existing AUTH/PORT/TOPIC resolution.
22
+ Rollback: remove the ~3-line block.
23
+
24
+ ## Migration parity
25
+ `telegram-topic-context.sh` is a BUILT-IN `instar/` hook → always-overwritten on every migration
26
+ (migrateHooks), so existing agents receive the query injection automatically. No new migration needed.
27
+
28
+ ## Tests
29
+ `telegram-topic-context-session-clock.test.ts` (2): the generated hook contains the
30
+ `query "$TOPIC_ID" "$PORT" "$AUTH_TOKEN"` call AND is syntactically valid bash (`bash -n` — guards
31
+ the TS-template escaping). Regression: migration-parity-hooks + neutralRelayPath + emit-session-clock
32
+ migration (12) green; `npm run lint` (tsc + custom linters) clean.