instar 1.3.206 → 1.3.208
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/dist/commands/init.js +4 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +10 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +22 -5
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +23 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +134 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/UpgradeGuideProcessor.d.ts.map +1 -1
- package/dist/core/UpgradeGuideProcessor.js +13 -3
- package/dist/core/UpgradeGuideProcessor.js.map +1 -1
- package/dist/core/UpgradeNotifyManager.d.ts.map +1 -1
- package/dist/core/UpgradeNotifyManager.js +49 -24
- package/dist/core/UpgradeNotifyManager.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/upgradeAnnouncement.d.ts +66 -0
- package/dist/core/upgradeAnnouncement.d.ts.map +1 -0
- package/dist/core/upgradeAnnouncement.js +143 -0
- package/dist/core/upgradeAnnouncement.js.map +1 -0
- package/dist/monitoring/sentinelWiring.d.ts +13 -0
- package/dist/monitoring/sentinelWiring.d.ts.map +1 -1
- package/dist/monitoring/sentinelWiring.js +44 -1
- package/dist/monitoring/sentinelWiring.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/scripts/analyze-release.js +59 -1
- package/src/data/builtin-manifest.json +20 -20
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.207.md +47 -0
- package/upgrades/1.3.208.md +54 -0
- package/upgrades/side-effects/mature-update-announcements.md +149 -0
- package/upgrades/side-effects/spinner-immune-liveness.md +122 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-02T21:56:02.195Z",
|
|
5
|
+
"instarVersion": "1.3.208",
|
|
6
6
|
"entryCount": 195,
|
|
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": "
|
|
14
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
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": "
|
|
23
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
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": "
|
|
32
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
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": "
|
|
41
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
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": "
|
|
50
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
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": "
|
|
59
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1490,7 +1490,7 @@
|
|
|
1490
1490
|
"type": "subsystem",
|
|
1491
1491
|
"domain": "updates",
|
|
1492
1492
|
"sourcePath": "src/core/AutoUpdater.ts",
|
|
1493
|
-
"contentHash": "
|
|
1493
|
+
"contentHash": "60802cc5aad744cad139c5f5c0b1e4b27468c0a27b38384804ed09a6aa4e081c",
|
|
1494
1494
|
"since": "2025-01-01"
|
|
1495
1495
|
},
|
|
1496
1496
|
"subsystem:auto-dispatcher": {
|
|
@@ -1506,7 +1506,7 @@
|
|
|
1506
1506
|
"type": "subsystem",
|
|
1507
1507
|
"domain": "updates",
|
|
1508
1508
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1509
|
-
"contentHash": "
|
|
1509
|
+
"contentHash": "623bae8fdc8451b34fca0ac1d83fb255d3e8c449d8bf923b0997357b383a0173",
|
|
1510
1510
|
"since": "2025-01-01"
|
|
1511
1511
|
},
|
|
1512
1512
|
"subsystem:scheduler": {
|
|
@@ -466,6 +466,7 @@ This routes feedback to the Instar maintainers automatically. Valid types: \`bug
|
|
|
466
466
|
- The endpoint resolves the Updates topic from state server-side; you cannot specify a topic and you should not try to.
|
|
467
467
|
- If Updates is not configured, the endpoint returns 400 — do NOT fall back to sending in the active topic. Update-class messages belong in Updates or they don't go out at all.
|
|
468
468
|
- **When to use** (PROACTIVE — this is the trigger): the moment I am about to author a conversational message whose subject is *me* shipping, updating, or restarting — including post-restart "I'm back" confirmations — I use this endpoint. Authoring such a message via the standard Telegram reply path puts release chatter into whatever conversation the user was last in, which is the bug this routing closes.
|
|
469
|
+
- **Maturity honesty (silent-by-default user announcements)**: user-facing update announcements are *opt-in and maturity-tagged*, authored in the release's upgrade guide (\`user_announcement\` front-matter: each change is \`audience: user|agent-only\` + \`maturity: experimental|preview|stable\`). The post-update notifier stays SILENT unless a change was explicitly promoted to \`audience: user\`, and experimental/preview features are announced as such (⚗️ Experimental / 🧪 Preview) — never implied to be finished. When I narrate my own ship here, I mirror that honesty: I do NOT announce a feature that ships dark/disabled as if it works, and I don't dress up an infra change as a finished capability. Patch-level "restarting…" notices are suppressed (only deferral warnings — "your work is holding a restart" — still surface).
|
|
469
470
|
|
|
470
471
|
**Secret Drop** — Securely collect secrets (API keys, passwords, tokens) from users without exposing them in chat history.
|
|
471
472
|
- Request a secret: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/secrets/request -H 'Content-Type: application/json' -d '{"label":"OpenAI API Key","description":"Needed for GPT integration","topicId":TOPIC_ID}'\`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
User-facing update announcements are now **opt-in and maturity-tagged**. A new
|
|
9
|
+
pure helper (`src/core/upgradeAnnouncement.ts`) parses a `user_announcement`
|
|
10
|
+
front-matter block in the release upgrade guide — each notable change carries an
|
|
11
|
+
`audience` (`user` or `agent-only`) and a `maturity` (`experimental` / `preview`
|
|
12
|
+
/ `stable`).
|
|
13
|
+
|
|
14
|
+
- **UpgradeNotifyManager** is now silent-by-default: with no `audience: user`
|
|
15
|
+
entry it skips the user message entirely (still updating MEMORY.md and
|
|
16
|
+
acknowledging), and with entries it composes only from them using honest
|
|
17
|
+
maturity framing + badges (⚗️ Experimental / 🧪 Preview). The old "lead with
|
|
18
|
+
the biggest user-visible feature" instruction is gone.
|
|
19
|
+
- **UpgradeGuideProcessor** hoists and merges each guide's `user_announcement`
|
|
20
|
+
block to the top of the concatenated pending guide, so the decision survives
|
|
21
|
+
multi-guide batches (a no-op when no guide carries a block).
|
|
22
|
+
- **AutoUpdater + the restart handshake** suppress the bare patch-level "Just
|
|
23
|
+
updated… restarting" narration while keeping the restart-hold warnings;
|
|
24
|
+
restart verification is preserved via an empty deferred notification.
|
|
25
|
+
- **analyze-release.js** scaffolds the block (defaulting every entry to
|
|
26
|
+
`agent-only`) on `--draft-guide`, and warns at authoring time when a
|
|
27
|
+
user-relevant release carries no block at all.
|
|
28
|
+
- Existing agents receive the maturity-honesty guidance through a
|
|
29
|
+
`PostUpdateMigrator` CLAUDE.md migration (new agents get it from the template).
|
|
30
|
+
|
|
31
|
+
Parent principle: **Near-Silent Notifications**. Spec:
|
|
32
|
+
`docs/specs/mature-update-announcements.md`.
|
|
33
|
+
|
|
34
|
+
## What to Tell Your User
|
|
35
|
+
|
|
36
|
+
- **Quieter, more honest updates**: "I'll only message you about an update when
|
|
37
|
+
it's genuinely relevant and ready for you, and I'll be upfront when something
|
|
38
|
+
is still experimental instead of making it sound finished. You'll get fewer
|
|
39
|
+
pings and clearer ones."
|
|
40
|
+
|
|
41
|
+
## Summary of New Capabilities
|
|
42
|
+
|
|
43
|
+
| Capability | How to Use |
|
|
44
|
+
|-----------|-----------|
|
|
45
|
+
| Maturity-tagged, opt-in update announcements | Authored in the release upgrade guide's `user_announcement` front-matter; the notifier stays silent unless a change is promoted to the user |
|
|
46
|
+
| Honest maturity framing | Experimental and preview features are labeled as such, never narrated as finished |
|
|
47
|
+
| Quieter restarts | Patch-level "restarting" notices are suppressed; restart-hold warnings still surface |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
`OutputActivityTracker` (the per-session change-detector that feeds
|
|
9
|
+
`ActiveWorkSilenceSentinel`) now hashes a **spinner-immune** view of the captured
|
|
10
|
+
pane. The host's "working" spinner (e.g. Claude's `✻ Sautéed for 26m 16s ·
|
|
11
|
+
(esc to interrupt)`) ticks its elapsed-time counter every second, so the previous
|
|
12
|
+
raw hash changed on every poll even when the turn had produced no real output for
|
|
13
|
+
many minutes. That kept the session's `lastChangeAt` perpetually fresh and blinded
|
|
14
|
+
the silence sentinel to a stalled-but-spinning turn — the cause of a ~26-minute
|
|
15
|
+
unrecovered API-stall where a wedged session looked fully alive. A new
|
|
16
|
+
`stripVolatileStatus` removes the volatile status region (the rotating glyph, the
|
|
17
|
+
elapsed-time and token/context counters, and the `esc to interrupt` footer) before
|
|
18
|
+
hashing, so only REAL scrollback changes refresh the activity timestamp. A
|
|
19
|
+
stalled-but-spinning turn now correctly accrues idle time and becomes eligible for
|
|
20
|
+
the sentinel's gentle nudge.
|
|
21
|
+
|
|
22
|
+
This is safe: the silence sentinel's nudge is a non-destructive `Enter` keystroke,
|
|
23
|
+
so a false-positive on a genuinely long turn is harmless. The only destructive
|
|
24
|
+
recovery (Ctrl-C) lives in `SocketDisconnectSentinel`, which fires only on a
|
|
25
|
+
positive connection-error marker — unchanged.
|
|
26
|
+
|
|
27
|
+
## What to Tell Your User
|
|
28
|
+
|
|
29
|
+
- **Better stuck-session detection**: "If one of my sessions ever freezes mid-task
|
|
30
|
+
while still showing a 'working' spinner, I can now actually notice it and try to
|
|
31
|
+
wake it, instead of being fooled into thinking it's busy because the spinner's
|
|
32
|
+
clock keeps ticking. It's a gentle nudge only — it never interrupts real work."
|
|
33
|
+
|
|
34
|
+
## Summary of New Capabilities
|
|
35
|
+
|
|
36
|
+
| Capability | How to Use |
|
|
37
|
+
|-----------|-----------|
|
|
38
|
+
| Spinner-immune stall detection | Automatic (ActiveWorkSilenceSentinel) |
|
|
39
|
+
|
|
40
|
+
## Evidence
|
|
41
|
+
|
|
42
|
+
- **Live before:** 2026-06-02, a session hung ~26 min on an Anthropic API socket
|
|
43
|
+
drop while showing `Sautéed for 26m 16s`; `ActiveWorkSilenceSentinel` never fired
|
|
44
|
+
(it flagged a *different* session idle at a static prompt in the same window), and
|
|
45
|
+
the turn only re-engaged when an inbound user message arrived. Confirmed in
|
|
46
|
+
`logs/server.log` + `logs/sentinel-events.jsonl`.
|
|
47
|
+
- **After (code path):** `tests/unit/spinner-immune-liveness.test.ts` drives
|
|
48
|
+
`OutputActivityTracker` with ticking-spinner-only frames and asserts `lastOutputAt`
|
|
49
|
+
HOLDS across them (idle accrues) while genuine new output still advances it; plus
|
|
50
|
+
`stripVolatileStatus` unit cases for Claude/codex spinner shapes and a guard that
|
|
51
|
+
benign content (e.g. "completed in 5s") is not over-stripped.
|
|
52
|
+
- **Cross-model reviewed** by the codex-based agent (task #63): confirmed the silence
|
|
53
|
+
nudge is `Enter`-only (non-destructive), which is why the spinner-immune hash alone
|
|
54
|
+
is sufficient and safe.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Side-Effects Review — Maturity-aware, silent-by-default user update announcements
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `mature-update-announcements`
|
|
4
|
+
**Date:** `2026-06-02`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `3 independent adversarial reviewers (design / correctness / integration) — internal convergence`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Stops the post-update notifier from overselling unfinished features (the trigger:
|
|
11
|
+
infancy-stage Gemini CLI support narrated as "just got more reliable"). User-facing
|
|
12
|
+
update announcements become OPT-IN + maturity-tagged, authored in the release's
|
|
13
|
+
upgrade guide (`user_announcement` front-matter). Touches: `src/core/upgradeAnnouncement.ts`
|
|
14
|
+
(new pure helper), `src/core/UpgradeNotifyManager.ts` (silent-by-default compose),
|
|
15
|
+
`src/core/UpgradeGuideProcessor.ts` (hoist/merge blocks to byte 0), `src/core/AutoUpdater.ts`
|
|
16
|
+
+ `src/commands/server.ts` (Fork 3: suppress patch-level restart narration, preserve
|
|
17
|
+
handshake verification), `scripts/analyze-release.js` (draft block + forgot-block advisory),
|
|
18
|
+
`src/scaffold/templates.ts` + `src/core/PostUpdateMigrator.ts` (agent-awareness guidance).
|
|
19
|
+
Decision points touched: the post-update *announce/skip* decision and the *restart-narrate/suppress*
|
|
20
|
+
decision.
|
|
21
|
+
|
|
22
|
+
## Decision-point inventory
|
|
23
|
+
|
|
24
|
+
- `UpgradeNotifyManager.buildPrompt announce-vs-skip` — **modify** — was "always compose, lead with the biggest feature"; now composes only from `audience: user` entries, skips entirely when there are none.
|
|
25
|
+
- `AutoUpdater restart-narration` — **modify** — was "narrate on every active-session restart"; now suppresses the bare narration for patch-only bumps (deferral warnings unchanged).
|
|
26
|
+
- `analyze-release forgot-block advisory` — **add** — a non-blocking author-facing warning when a user-relevant release carries no announcement block.
|
|
27
|
+
- `UpgradeGuideProcessor block hoist` — **add** — assembles a merged front-matter block at byte 0 (no decision authority; pure data-flow plumbing).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 1. Over-block
|
|
32
|
+
|
|
33
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
34
|
+
|
|
35
|
+
The "block" surface here is the announce/skip decision. Silent-by-default means a
|
|
36
|
+
*genuinely user-ready* feature is "over-blocked" (not announced) if the release author
|
|
37
|
+
forgets to add a `user_announcement` block. This is the one real over-block risk and was
|
|
38
|
+
raised by the design reviewer. Mitigations: (a) `analyze-release --draft-guide` scaffolds
|
|
39
|
+
the block for every feature/enhancement, so the author is structurally prompted to promote
|
|
40
|
+
the user-facing ones; (b) the main-path forgot-block advisory fires when a user-relevant
|
|
41
|
+
release has no block at all. The fail-safe direction is deliberate: silence (recoverable
|
|
42
|
+
with a follow-up note) is preferable to today's failure of a misleading announcement.
|
|
43
|
+
|
|
44
|
+
## 2. Under-block
|
|
45
|
+
|
|
46
|
+
**What failure modes does this still miss?**
|
|
47
|
+
|
|
48
|
+
An author can still *mislabel* maturity — mark an experimental feature `stable, audience: user` —
|
|
49
|
+
and the system will compose a confident message. The forgot-block advisory only catches total
|
|
50
|
+
absence of the block, not a wrong maturity inside it. This is accepted: maturity is a human
|
|
51
|
+
judgment authored with the release notes (the Body-and-Mind "author decides" layer); we
|
|
52
|
+
deliberately did NOT add a brittle headline→rollout-stage map in CI to police it (it would be
|
|
53
|
+
fragile and is the wrong layer). The restart-suppression "under-block": a patch bump that is
|
|
54
|
+
secretly disruptive gets no narration — but the deferral warnings and the failed-restart
|
|
55
|
+
escalation still fire, so a genuinely stuck restart is still surfaced.
|
|
56
|
+
|
|
57
|
+
## 3. Level-of-abstraction fit
|
|
58
|
+
|
|
59
|
+
**Is this at the right layer?**
|
|
60
|
+
|
|
61
|
+
Yes. The announce decision is authored at release time (in the upgrade guide), not improvised
|
|
62
|
+
by a throwaway notify session — that was the core design move. The notify-side helper
|
|
63
|
+
(`upgradeAnnouncement.ts`) is a pure renderer/parser (a low-level primitive). The forgot-block
|
|
64
|
+
guard lives in the release analyzer (the only layer with the "brand-new feature" signal and the
|
|
65
|
+
author's attention) — during convergence we *moved* the coherence check there and removed an
|
|
66
|
+
orphaned TS function that would have been a parallel implementation. The hoist lives in the
|
|
67
|
+
processor (the layer that already assembles the pending guide). No re-implementation of an
|
|
68
|
+
existing primitive; no gate running parallel to a smarter one.
|
|
69
|
+
|
|
70
|
+
## 4. Signal vs authority compliance
|
|
71
|
+
|
|
72
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
73
|
+
|
|
74
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
75
|
+
|
|
76
|
+
- [x] No — the brittle pieces are signals; the only authorities are deterministic, fully-enumerable policy.
|
|
77
|
+
|
|
78
|
+
The forgot-block advisory is a **signal** (a non-blocking `log()` line; the author decides). The
|
|
79
|
+
announce/skip decision is a deterministic policy over a fully-enumerable input ("are there
|
|
80
|
+
`audience: user` entries?") — not brittle pattern-matching. The restart-suppression decision is
|
|
81
|
+
deterministic over `crossesBreaking(prev, next)` (a pure semver observation that fails safe to
|
|
82
|
+
"narrate" on malformed input). No LLM-free brittle check holds blocking authority. All three
|
|
83
|
+
adversarial reviewers independently affirmed signal-vs-authority compliance.
|
|
84
|
+
|
|
85
|
+
## 5. Interactions
|
|
86
|
+
|
|
87
|
+
- **Shadowing:** the announcement front-matter is hoisted to byte 0 *above* the existing
|
|
88
|
+
`# Instar Upgrade Guide` header and `<!-- bump: -->` marker. Verified the bump-marker regex,
|
|
89
|
+
the publish-gate validator (`validateGuideContent` uses `includes`, no first-line requirement),
|
|
90
|
+
`assemble-next-md.mjs`, and `check-upgrade-guide.js` all tolerate leading front-matter.
|
|
91
|
+
- **Double-fire:** the notify session's Step-1 send and the AutoUpdater restart narration are
|
|
92
|
+
distinct topics/paths; no overlap. The handshake is written once per version (existing dedup
|
|
93
|
+
guard unchanged).
|
|
94
|
+
- **Races:** none introduced — the processor assembly is single-pass synchronous; no shared
|
|
95
|
+
mutable state added.
|
|
96
|
+
- **Feedback loops:** none.
|
|
97
|
+
|
|
98
|
+
## 6. External surfaces
|
|
99
|
+
|
|
100
|
+
- **Other agents:** the CLAUDE.md template + `migrateClaudeMd` change is the only agent-visible
|
|
101
|
+
surface; it adds guidance, removes nothing.
|
|
102
|
+
- **Users:** fewer/quieter update messages (the intended UX change). Patch-level "restarting…"
|
|
103
|
+
messages disappear; deferral warnings remain.
|
|
104
|
+
- **External systems (Telegram):** the post-update topic receives fewer messages; format of any
|
|
105
|
+
message that DOES send is unchanged except for the experimental/preview badges.
|
|
106
|
+
- **Persistent state:** none. No new files/columns/ledgers. The handshake file is written exactly
|
|
107
|
+
as before (only its `deferredNotification` may now be empty for patch bumps; the server skips
|
|
108
|
+
empty emits and still clears the file).
|
|
109
|
+
- **Timing:** none beyond the existing restart timing.
|
|
110
|
+
|
|
111
|
+
## 7. Rollback cost
|
|
112
|
+
|
|
113
|
+
**Pure code change — revert the commit, ship as next patch.** No persistent state, no data
|
|
114
|
+
migration, no agent-state repair. The `migrateClaudeMd` addition is idempotent and additive
|
|
115
|
+
(reverting simply stops adding the section to new updates; already-patched CLAUDE.md files keep a
|
|
116
|
+
harmless guidance paragraph). No user-visible regression during the rollback window — worst case
|
|
117
|
+
reverts to the prior (louder, less honest) announcement behavior.
|
|
118
|
+
|
|
119
|
+
## Conclusion
|
|
120
|
+
|
|
121
|
+
The review (three independent adversarial passes — design, correctness, integration) returned a
|
|
122
|
+
unanimous SHIP-WITH-MINOR-FIXES with no blockers. Three findings were acted on before commit:
|
|
123
|
+
(1) added the required `migrateClaudeMd` migration parity for the maturity-honesty guidance;
|
|
124
|
+
(2) added the author-facing forgot-block advisory to mitigate the silent-by-default over-block
|
|
125
|
+
risk; (3) removed the orphaned `announcementCoherenceWarnings` TS helper (un-wired dead code) and
|
|
126
|
+
relocated the coherence concern to the release analyzer where the signal actually exists — keeping
|
|
127
|
+
`upgradeAnnouncement.ts` focused on the notify path. A correctness reviewer's serialize↔parse
|
|
128
|
+
round-trip gap was closed with an explicit unit test. The change is clear to ship.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Second-pass review (if required)
|
|
133
|
+
|
|
134
|
+
**Reviewer:** internal multi-reviewer convergence (design / correctness / integration)
|
|
135
|
+
**Independent read of the artifact: concur**
|
|
136
|
+
|
|
137
|
+
All three reviewers concurred the design is sound (signal-vs-authority + Body-and-Mind compliant,
|
|
138
|
+
correct level of abstraction) and the implementation is correct and alive end-to-end. The minor
|
|
139
|
+
fixes they raised are incorporated above; no residual concerns block the ship.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Evidence pointers
|
|
144
|
+
|
|
145
|
+
- Affected-set test run: 11 files / 134 tests green (unit helper, compose branches, processor
|
|
146
|
+
hoist + no-op, analyze-release round-trip + forgot-block advisory, migration parity, AutoUpdater
|
|
147
|
+
Fork-3 patch-suppression + minor-narrates, e2e upgrade-guide lifecycle).
|
|
148
|
+
- Spec: `docs/specs/mature-update-announcements.md` (`review-convergence: internal-multi-reviewer-2026-06-02`, `approved: true`).
|
|
149
|
+
- ELI16: `docs/specs/mature-update-announcements.eli16.md`.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Side-Effects Review — spinner-immune liveness signal
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `spinner-immune-liveness`
|
|
4
|
+
**Date:** `2026-06-02`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `instar-codey (codex) — cross-model review completed; verdict incorporated`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`src/monitoring/sentinelWiring.ts`: `OutputActivityTracker.snapshot()` now hashes
|
|
11
|
+
`stripVolatileStatus(output, framework)` instead of the raw pane. `stripVolatileStatus`
|
|
12
|
+
strips the host's animated working-status region (Braille spinner glyphs, the
|
|
13
|
+
`Nm Ns` / `(Ns` elapsed timers, token/context counters, and the `esc to interrupt`
|
|
14
|
+
footer line) so only real scrollback changes refresh `lastChangeAt`. Fixes the
|
|
15
|
+
silence sentinel being permanently blinded by the spinner's ticking clock.
|
|
16
|
+
|
|
17
|
+
## Decision-point inventory
|
|
18
|
+
|
|
19
|
+
- `OutputActivityTracker: did this frame change?` (sentinelWiring.ts:~215) — **modify**
|
|
20
|
+
— the hashed input is now spinner-normalized. The downstream "observed-change
|
|
21
|
+
requirement" + `looksActivelyWorking` + silence-threshold logic are unchanged.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Over-block
|
|
26
|
+
|
|
27
|
+
**No block/allow surface — over-block not applicable.** The change cannot reject or
|
|
28
|
+
block anything. The only effect is that the silence sentinel correctly recognizes a
|
|
29
|
+
stalled-but-spinning turn as idle (rather than perpetually "active") and may issue
|
|
30
|
+
its existing non-destructive `Enter` nudge.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Under-block
|
|
35
|
+
|
|
36
|
+
**What does it still miss?** It strips a conservative, anchored set of volatile
|
|
37
|
+
tokens; an exotic future spinner format could still leak a changing token into the
|
|
38
|
+
hash (degrading gracefully back to the old "always active" behavior for that host —
|
|
39
|
+
no worse than today). It does not change the silence THRESHOLD; a true stall is
|
|
40
|
+
detected at the existing ~16-min mark.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 3. Level-of-abstraction fit
|
|
45
|
+
|
|
46
|
+
Correct layer — a normalization at the change-detector's input boundary, reusing the
|
|
47
|
+
existing per-framework `getActivitySignal` patterns rather than inventing new ones.
|
|
48
|
+
It feeds the existing smart silence sentinel; it does not add a parallel detector or
|
|
49
|
+
hold any authority.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 4. Signal vs authority compliance
|
|
54
|
+
|
|
55
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
56
|
+
|
|
57
|
+
- [x] No — this change produces a signal (a fresher/stale activity timestamp)
|
|
58
|
+
consumed by the existing `ActiveWorkSilenceSentinel`, which owns the (non-destructive)
|
|
59
|
+
recovery decision.
|
|
60
|
+
|
|
61
|
+
The cross-model review (Codey) specifically affirmed the signal-vs-authority shape:
|
|
62
|
+
absence-of-activity must NOT authorize destructive recovery; here it only authorizes a
|
|
63
|
+
non-destructive `Enter` nudge, and the destructive Ctrl-C path (SocketDisconnectSentinel)
|
|
64
|
+
remains gated on a POSITIVE error-string marker. No change weakens that.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 5. Interactions
|
|
69
|
+
|
|
70
|
+
- **Shadowing:** none — same single hash site; downstream logic unchanged.
|
|
71
|
+
- **Double-fire:** none — `SocketDisconnectSentinel` (error-string → Ctrl-C) and
|
|
72
|
+
`ActiveWorkSilenceSentinel` (silence → Enter) remain independent; this only un-blinds
|
|
73
|
+
the latter.
|
|
74
|
+
- **Races:** none — pure synchronous string transform inside the existing snapshot tick.
|
|
75
|
+
- **Feedback loops:** the Enter nudge could, on a genuinely-long generation, submit a
|
|
76
|
+
stray blank line; the pane is mid-turn (spinner present) so it is queued/ignored, not
|
|
77
|
+
destructive. Verified the nudge is `sendKey(session, 'Enter')` (sentinelWiring.ts:255),
|
|
78
|
+
not Ctrl-C.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 6. External surfaces
|
|
83
|
+
|
|
84
|
+
- Persistent state: none.
|
|
85
|
+
- External systems: none.
|
|
86
|
+
- Behavior visible to operators: a stalled-but-spinning session now appears in
|
|
87
|
+
`logs/sentinel-events.jsonl` as detected/nudged where before it was invisible. The
|
|
88
|
+
Telegram escalation default (off) is unchanged.
|
|
89
|
+
- Runtime: one extra string transform per session per silence tick — negligible.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 7. Rollback cost
|
|
94
|
+
|
|
95
|
+
Pure code change — revert `stripVolatileStatus` + restore `cheapHash(output)`, ship a
|
|
96
|
+
patch. No persistent state, no migration, no user-visible regression (it would simply
|
|
97
|
+
return to the prior "spinner fools the detector" behavior).
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Conclusion
|
|
102
|
+
|
|
103
|
+
The cross-model review materially improved this change: it began as a multi-state
|
|
104
|
+
process-activity gate to protect long generations from a *destructive* Ctrl-C, but the
|
|
105
|
+
reviewer's grounded catch — the silence nudge is `Enter`-only, not Ctrl-C — collapsed it
|
|
106
|
+
to a single safe normalization, with the destructive path already correctly gated on a
|
|
107
|
+
positive error marker elsewhere. Clear to ship as a Tier-1 monitoring reliability fix.
|
|
108
|
+
|
|
109
|
+
## Second-pass review (if required)
|
|
110
|
+
|
|
111
|
+
**Reviewer:** instar-codey (codex)
|
|
112
|
+
**Independent read: concur (after the implementation correction).** The reviewer's
|
|
113
|
+
verdict drove the final design: do not let absence-of-activity authorize destructive
|
|
114
|
+
recovery (already satisfied — the silence path is non-destructive `Enter`; Ctrl-C stays
|
|
115
|
+
behind the SocketDisconnectSentinel error marker). No residual concern on the shipped
|
|
116
|
+
shape.
|
|
117
|
+
|
|
118
|
+
## Evidence pointers
|
|
119
|
+
|
|
120
|
+
- `tests/unit/spinner-immune-liveness.test.ts` — 7 tests (normalizer + tracker behavior).
|
|
121
|
+
- `src/monitoring/sentinelWiring.ts:255` — nudge is `sendKey(session, 'Enter')`.
|
|
122
|
+
- Origin incident: `logs/server.log` + `logs/sentinel-events.jsonl`, 2026-06-02 26-min stall.
|