instar 1.3.813 → 1.3.815
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/dashboard/glance.js +161 -8
- package/dashboard/index.html +41 -75
- package/dashboard/subscriptions.js +24 -4
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +4 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +20 -20
- package/src/templates/scripts/telegram-reply.sh +26 -7
- package/upgrades/1.3.814.md +24 -0
- package/upgrades/1.3.815.md +57 -0
- package/upgrades/eli16/telegram-reply-worktree-home.md +19 -0
- package/upgrades/side-effects/glance-p2-commitments-blockers.md +118 -0
- package/upgrades/side-effects/telegram-reply-worktree-home.md +77 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-11T04:54:52.556Z",
|
|
5
|
+
"instarVersion": "1.3.815",
|
|
6
6
|
"entryCount": 202,
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
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": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1242,7 +1242,7 @@
|
|
|
1242
1242
|
"type": "template",
|
|
1243
1243
|
"domain": "operations",
|
|
1244
1244
|
"sourcePath": "src/templates/scripts/telegram-reply.sh",
|
|
1245
|
-
"contentHash": "
|
|
1245
|
+
"contentHash": "24a638766fc8a2473e23e032dde39ff7ef046c37e893ff878323d18d9dad2d52",
|
|
1246
1246
|
"since": "2025-01-01"
|
|
1247
1247
|
},
|
|
1248
1248
|
"template:whatsapp-reply.sh": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "93f638d73e5fd2f3b55f893c91ad3358c2124721ca6c7222841d3e3b67260de7",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -133,7 +133,21 @@ sys.stdout.write(base64.b64decode(raw, validate=True).decode("utf-8"))
|
|
|
133
133
|
MSG="$DECODED_MSG"
|
|
134
134
|
fi
|
|
135
135
|
|
|
136
|
-
# Resolve
|
|
136
|
+
# Resolve the owning agent home before reading config or recovery state.
|
|
137
|
+
# Explicit launcher context wins. Otherwise, ONLY the structural .worktrees
|
|
138
|
+
# marker may move us upward; a general config search could cross tenant roots
|
|
139
|
+
# on a multi-agent host. The ordinary agent-home cwd remains unchanged.
|
|
140
|
+
if [ -n "${INSTAR_AGENT_HOME:-}" ]; then
|
|
141
|
+
AGENT_HOME="$INSTAR_AGENT_HOME"
|
|
142
|
+
else
|
|
143
|
+
case "$PWD" in
|
|
144
|
+
*/.worktrees/*) AGENT_HOME="${PWD%%/.worktrees/*}" ;;
|
|
145
|
+
*) AGENT_HOME="$PWD" ;;
|
|
146
|
+
esac
|
|
147
|
+
fi
|
|
148
|
+
CONFIG_PATH="$AGENT_HOME/.instar/config.json"
|
|
149
|
+
|
|
150
|
+
# Resolve config-derived values from the owning agent's config (single python3
|
|
137
151
|
# invocation). Env > config > 4040-warn for port. Auth: INSTAR_AUTH_TOKEN env
|
|
138
152
|
# first (SessionManager injects it per spawned session; survives the
|
|
139
153
|
# secret-externalization refactor that moved authToken out of config.json into
|
|
@@ -143,11 +157,11 @@ fi
|
|
|
143
157
|
AUTH_TOKEN="${INSTAR_AUTH_TOKEN:-}"
|
|
144
158
|
AGENT_ID=""
|
|
145
159
|
CONFIG_PORT=""
|
|
146
|
-
if [ -f "
|
|
160
|
+
if [ -f "$CONFIG_PATH" ]; then
|
|
147
161
|
CONFIG_VALUES=$(python3 -c "
|
|
148
162
|
import json, sys
|
|
149
163
|
try:
|
|
150
|
-
c = json.load(open(
|
|
164
|
+
c = json.load(open(sys.argv[1]))
|
|
151
165
|
except Exception:
|
|
152
166
|
sys.exit(0)
|
|
153
167
|
v = c.get('authToken', '')
|
|
@@ -156,7 +170,7 @@ print(c.get('projectName', ''))
|
|
|
156
170
|
print(c.get('port', ''))
|
|
157
171
|
t = (((c.get('messaging') or {}).get('outboundAdvisory') or {}).get('timeoutMs', ''))
|
|
158
172
|
print(t if isinstance(t, (int, float)) else '')
|
|
159
|
-
" 2>/dev/null)
|
|
173
|
+
" "$CONFIG_PATH" 2>/dev/null)
|
|
160
174
|
CONFIG_AUTH=$(printf '%s\n' "$CONFIG_VALUES" | sed -n '1p')
|
|
161
175
|
[ -z "$AUTH_TOKEN" ] && AUTH_TOKEN="$CONFIG_AUTH"
|
|
162
176
|
AGENT_ID=$(printf '%s\n' "$CONFIG_VALUES" | sed -n '2p')
|
|
@@ -170,7 +184,7 @@ elif [ -n "$CONFIG_PORT" ]; then
|
|
|
170
184
|
PORT="$CONFIG_PORT"
|
|
171
185
|
else
|
|
172
186
|
PORT=4040
|
|
173
|
-
echo "WARN: telegram-reply.sh — no INSTAR_PORT env and no port in
|
|
187
|
+
echo "WARN: telegram-reply.sh — no INSTAR_PORT env and no port in $CONFIG_PATH; falling back to 4040" >&2
|
|
174
188
|
fi
|
|
175
189
|
|
|
176
190
|
# ── Outbound advisory preflight (inform-only; spec outbound-jargon-filepath-gap §2.4) ──
|
|
@@ -464,10 +478,15 @@ except Exception:
|
|
|
464
478
|
# Enqueue (spec § Layer 2b). Path: <stateDir>/state/pending-relay.<agentId>.sqlite
|
|
465
479
|
# Mode 0600 enforced by the Node-side store; the CLI inherits umask, so
|
|
466
480
|
# we explicitly chmod after first create as well.
|
|
467
|
-
QUEUE_DIR=".instar/state"
|
|
468
|
-
mkdir -p "$QUEUE_DIR" 2>/dev/null
|
|
469
481
|
# Sanitize agent-id for filename (mirrors src/messaging/pending-relay-store.ts).
|
|
470
482
|
SAFE_AGENT_ID=$(printf '%s' "${AGENT_ID:-unknown}" | tr -c 'A-Za-z0-9._-' '_')
|
|
483
|
+
if [ "$SAFE_AGENT_ID" = "unknown" ]; then
|
|
484
|
+
echo "Failed (HTTP $HTTP_CODE): $BODY" >&2
|
|
485
|
+
echo " (also: agent id is unknown; refusing to create an undrainable pending-relay.unknown.sqlite store)" >&2
|
|
486
|
+
exit 1
|
|
487
|
+
fi
|
|
488
|
+
QUEUE_DIR="$AGENT_HOME/.instar/state"
|
|
489
|
+
mkdir -p "$QUEUE_DIR" 2>/dev/null
|
|
471
490
|
QUEUE_DB="${QUEUE_DIR}/pending-relay.${SAFE_AGENT_ID}.sqlite"
|
|
472
491
|
|
|
473
492
|
# delivery_id — the id was minted BEFORE the initial POST and sent on it
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Telegram replies launched from inside an Instar worktree now resolve the owning agent home before reading relay configuration or writing recovery state. Recovery also refuses loudly when the agent identity is unavailable instead of creating an orphan queue that a server cannot drain.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
Replies sent while development is running inside a worktree now use the same live relay configuration as the owning agent. If identity is genuinely unavailable, the send fails visibly in the session instead of leaving a hidden message that might reappear days later.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
- Honors `INSTAR_AGENT_HOME` as the authoritative relay home.
|
|
17
|
+
- Constrains automatic walk-up to the structural `/.worktrees/` marker.
|
|
18
|
+
- Writes recoverable messages into the owning agent's drainable queue.
|
|
19
|
+
- Refuses `pending-relay.unknown.sqlite` creation with a clear non-zero failure.
|
|
20
|
+
- Re-stamps the corrected script onto existing unmodified installs during update.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
Real-script integration coverage proves normal agent-home behavior, worktree resolution, explicit-home precedence, and loud unknown-id refusal. Migration tests and the shipped-template SHA ratchet prove fresh installs and existing agents receive the same corrected script.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Phase 2 of the operator-approved four-phase glance rollout (topic 29836). The two worst
|
|
9
|
+
"wall of raw records" tabs are rebuilt on the shared glance component (F10/F11), and two
|
|
10
|
+
filed issues fold in:
|
|
11
|
+
|
|
12
|
+
- **Commitments — full rebuild + issue #1435.** The headline now has a matching tile for
|
|
13
|
+
every number it states: an **Overdue** tile joins Open / Due soon / Waiting on you /
|
|
14
|
+
Quiet. Grammar is count-aware ("1 needs attention soon", "1 is overdue"). And the
|
|
15
|
+
classification is fixed: a promise whose **hard deadline has already passed is Overdue,
|
|
16
|
+
never "Due soon"** — a stale record a month past its deadline is no longer mislabelled.
|
|
17
|
+
- **Blockers — rebuilt on the template.** The old ~7,000-word raw table that filled the
|
|
18
|
+
whole page is replaced by a one-sentence headline ("N things are truly stuck; K being
|
|
19
|
+
worked") over three tiles — **Truly stuck · Being worked · Resolved**. Tap a tile to see
|
|
20
|
+
which blockers (in plain sentences); tap one for the full record (state, id, timestamps,
|
|
21
|
+
recheck date). Nothing is lost — every column moved down a layer. The "decaying
|
|
22
|
+
hypothesis, not a wall" framing is preserved: a truly-stuck blocker is "the best current
|
|
23
|
+
understanding with a recheck date", never "give up".
|
|
24
|
+
- **Subscriptions — issue #1428.** When you cancel an in-progress sign-in, the cell now
|
|
25
|
+
clears **immediately** instead of showing the stale sign-in flow for ~40 seconds until
|
|
26
|
+
the next refresh. If the cancel actually failed server-side, the next poll still corrects
|
|
27
|
+
it (the poll stays the authority).
|
|
28
|
+
- Both rebuilt tabs left the grandfathered list (the ratchet ceiling dropped 25 → 24), so
|
|
29
|
+
they are now held to the same glance floor as every new view.
|
|
30
|
+
|
|
31
|
+
## What to Tell Your User
|
|
32
|
+
|
|
33
|
+
Two of your busiest dashboard tabs are now readable at a glance. **Commitments** leads with
|
|
34
|
+
a plain sentence about where your promises stand, with a big tile for each state — including
|
|
35
|
+
a new **Overdue** tile — and correctly separates "overdue" from "due soon" (a promise past
|
|
36
|
+
its deadline is now shown as overdue). **Blockers** no longer dumps a giant table on the
|
|
37
|
+
page: you get a one-line summary and a few tiles (Truly stuck / Being worked / Resolved), and
|
|
38
|
+
you tap to drill into the details. And when you cancel a subscription sign-in, the tile
|
|
39
|
+
resets right away instead of lingering for half a minute. Tap any tile to see which items are
|
|
40
|
+
behind that number, and tap one for its full record — nothing was removed, it just moved a
|
|
41
|
+
tap or two down.
|
|
42
|
+
|
|
43
|
+
## Summary of New Capabilities
|
|
44
|
+
|
|
45
|
+
- The **Commitments** dashboard tab now has an Overdue tile, count-aware grammar, and a
|
|
46
|
+
corrected overdue-vs-due-soon classification (issue #1435).
|
|
47
|
+
- The **Blockers** dashboard tab is rebuilt as a glance (headline + Truly stuck / Being
|
|
48
|
+
worked / Resolved tiles → filtered rows → full record), replacing the old raw table.
|
|
49
|
+
- A cancelled subscription sign-in now resets its cell immediately (issue #1428).
|
|
50
|
+
|
|
51
|
+
## Evidence
|
|
52
|
+
|
|
53
|
+
- Unit: `tests/unit/dashboard-glance-word-budget.test.ts` (43), `tests/unit/dashboard-glance-drilldown.test.ts` (13), `tests/unit/subscriptions-render.test.ts`, `tests/unit/follow-me-controller-wiring.test.ts`.
|
|
54
|
+
- Integration: `tests/integration/glance-blockers-tab.test.ts` (real `/blockers` + BlockerLedger), `tests/integration/glance-commitments-tab.test.ts`.
|
|
55
|
+
- E2E: `tests/e2e/glance-blockers-tab-lifecycle.test.ts`, `tests/e2e/glance-commitments-tab-lifecycle.test.ts` (feature-alive: 200 with the feature on, honest empty/503 with it off, no XSS survives).
|
|
56
|
+
- Live browser render (Playwright): both tabs rendered end-to-end against realistic payloads — Commitments "I'm carrying 5 open promises; 1 needs attention soon, 1 is overdue." with the Overdue tile drilling into a past-deadline promise's record; Blockers "1 thing is truly stuck right now; 2 are being worked." drilling into the true-blocker's full record.
|
|
57
|
+
- Spec: `docs/specs/dashboard-ux-standard.md` (F10/F11, conformance table updated — Commitments + Blockers now on the floor).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ELI16 — Worktree replies return to the owning agent
|
|
2
|
+
|
|
3
|
+
## What Changed
|
|
4
|
+
|
|
5
|
+
Instar agents use a small relay script to send a conversational response back to Telegram. That script normally runs from the agent's home directory, where it can read the live server port and agent identity. During development, however, a session can run from a nested Git worktree. The old script treated that worktree as if it were a separate agent home. It could miss the real port, fail its HTTP request, and then save the reply into a database named for an `unknown` agent inside the worktree. No live server owns or drains that database, so the message becomes a ghost row that could surface much later as a duplicate.
|
|
6
|
+
|
|
7
|
+
The relay now chooses its home conservatively. An explicit `INSTAR_AGENT_HOME` from the launcher wins. Without that variable, the script walks upward only when the current path contains the exact `/.worktrees/` structural marker, stopping immediately before that marker. In every ordinary directory it keeps today's behavior and uses the current directory. It never performs a broad upward search, because that could select another agent's configuration on a shared machine.
|
|
8
|
+
|
|
9
|
+
## Safety Behavior
|
|
10
|
+
|
|
11
|
+
Recovery still queues a transiently failed reply when the owning agent identity is known. If the identity is missing and would become `unknown`, the script prints a clear reason and exits non-zero without creating a database. The original message remains visible in the caller's transcript, making the failure recoverable and observable instead of silently stranded.
|
|
12
|
+
|
|
13
|
+
## Deployment
|
|
14
|
+
|
|
15
|
+
Fresh installs already source the canonical relay template. Existing agents receive the same template through PostUpdateMigrator: the v1.3.813 shipped-template hash is registered as a known safe predecessor, so an unmodified installed script is backed up and replaced during update while customized scripts retain the existing non-destructive `.new` behavior.
|
|
16
|
+
|
|
17
|
+
## Evidence
|
|
18
|
+
|
|
19
|
+
Integration tests execute the real Bash template against a real local HTTP server and SQLite recovery store. They cover worktree cwd resolution, explicit-home precedence, ordinary agent-home behavior, and unknown-id refusal with no orphan database. Unit tests verify migration output contains both safeguards and that historical shipped-template hashes remain complete.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Side-Effects Review — Dashboard glance Phase 2: Commitments rebuild + Blockers + #1428
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `glance-p2-commitments-blockers`
|
|
4
|
+
**Date:** `2026-07-10`
|
|
5
|
+
**Author:** `echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `not required` (no block/allow, session-lifecycle, gate/sentinel/watchdog, or compaction surface — a client-side dashboard render change)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Phase 2 of the operator-approved glance rollout (topic 29836, spec `docs/specs/dashboard-ux-standard.md`, F10/F11). Three view-layer changes, all in `dashboard/*.js` + `dashboard/index.html`, plus tests — **no `src/*.ts` runtime code is touched**:
|
|
11
|
+
|
|
12
|
+
1. **Commitments full rebuild** — `buildCommitmentsGlance`/`commitmentsGlanceSpec` in `dashboard/glance.js` now fold issue #1435: an **Overdue tile** (so every headline number drills down), **count-aware pluralization** ("1 needs" / "2 need", "1 is overdue"), and a **classification fix** — a promise whose HARD deadline is past is **overdue, never "due soon"** (overdue is computed first; due-soon is taken over the remainder, so a stale beacon record a month past its deadline is no longer double-counted). Five tiles now: Open · Due soon · Overdue · Waiting on you · Quiet.
|
|
13
|
+
2. **Blockers rebuild** — the old ~7,000-word raw table (built with `escapeHtml`-into-`innerHTML`) is replaced by the shared glance component. New pure builders `buildBlockersGlance` / `blockerRowText` / `blockerRecordNode` / `blockersGlanceSpec`. Headline ("N things are truly stuck; K being worked") + three tiles (Truly stuck / Being worked / Resolved) that partition the ledger population; each tile drills to plain-sentence rows; each row opens the full record (state, id, origin, timestamps, terminal detail) at Layer 3. `loadBlockers()` in `index.html` rewired onto it.
|
|
14
|
+
3. **Subscriptions optimistic cancel (issue #1428)** — a confirmed cancel (2xx) now drops a short-lived `cancelled` transient that suppresses the still-cached pending-login and rebuilds the cell AT CLICK TIME (no ~40s stale window). `purgeTransients()` clears it on the very next poll, so the poll stays authoritative if the cancel actually failed.
|
|
15
|
+
4. **Conformance ratchet** — `blockers` moved from `GLANCE_GRANDFATHERED` to `GLANCE_ADOPTED_TABS`; `GLANCE_GRANDFATHERED_CEILING` lowered 25 → 24. The ratchet only shrinks.
|
|
16
|
+
|
|
17
|
+
## Decision-point inventory
|
|
18
|
+
|
|
19
|
+
- **Commitments overdue-vs-due-soon classification** (`buildCommitmentsGlance`) — *modify* — overdue now takes precedence; pure derivation from existing server fields, no new authority.
|
|
20
|
+
- **Blockers state → tile bucketing** (`buildBlockersGlance`) — *add* — pure classification of the `/blockers` ledger population into working / stuck / resolved; no new authority, no new endpoint.
|
|
21
|
+
- **Subscriptions cell state derivation** (`buildMatrixModel`) — *modify* — a `cancelled` transient suppresses a stale cached pending-login; a display-only override cleared each poll.
|
|
22
|
+
- No block/allow, message-filter, or dispatch decision point is touched.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. Over-block
|
|
27
|
+
|
|
28
|
+
No block/allow surface — over-block not applicable. The classification changes decide which *tile* a record appears under (display grouping), never whether a record is admitted or an action allowed. A commitment/blocker is never dropped: the Commitments population is unchanged (`beaconEnabled && status==='pending'`), and the Blockers tiles partition the full `/blockers` population (a test asserts the tile counts sum to the population length — nothing is filtered out).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Under-block
|
|
33
|
+
|
|
34
|
+
No block/allow surface — under-block not applicable. Worst case for the #1428 optimistic reset is a *display* lag, not a missed block: if a cancel POST returns 2xx but the server actually failed to cancel, the `cancelled` transient hides the flow for at most one poll cycle, then `purgeTransients` clears it and the fresh pending-login re-renders the in-flight flow (poll is authority). A pasted code during that window still hits the submit route's existing pane-liveness guard (the dangerous half was already closed per #1428) — this change does not touch that guard.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3. Level-of-abstraction fit
|
|
39
|
+
|
|
40
|
+
Correct layer. All logic is a **stateless client-side renderer** deriving display from data the tab already fetches — the lowest-risk layer for a UX change. It reuses the shared `dashboard/glance.js` component (built in Phase 1) rather than re-implementing per-tab markup, and reuses the shipped `sanitizeForDisplay` + `hasOpenInteraction` primitives. No server route, config, or gate is added or changed; the Blockers glance drills into the existing `GET /blockers`, the Commitments glance into the existing `GET /commitments`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 4. Signal vs authority compliance
|
|
45
|
+
|
|
46
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
47
|
+
|
|
48
|
+
- [x] No — this change has no block/allow surface.
|
|
49
|
+
|
|
50
|
+
It is pure presentation: it renders records into a headline + tiles + drill-downs. It holds no authority over any action, message, or session. The `cancelled` transient is a display hint that self-clears on the next authoritative poll — it never decides an outcome.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 5. Interactions
|
|
55
|
+
|
|
56
|
+
- **Shadowing:** The Commitments/Blockers glances replace the tabs' own prior renderers (the old blockers table + its now-removed `blockerStateBadge`/`blockerTerminalLine` helpers were deleted; no other caller referenced them — verified by grep). No server-side check is shadowed.
|
|
57
|
+
- **Double-fire:** None. `loadBlockers`/`loadCommitments` are idempotent renders triggered by tab activation + Refresh; `renderGlance` replaces (never appends) the DOM, so repeated renders can't leak detached nodes/listeners.
|
|
58
|
+
- **Races:** The `cancelled` transient shares `state.matrixTransient` with the existing poll loop. It is set after the cancel POST resolves (server has processed the cancel), read by `buildMatrixModel`, and cleared in `purgeTransients` — which runs inside `render()` only after a *fresh* `/pending-logins` fetch succeeds (a fetch failure early-returns before `render()`), so clearing it always hands authority to real server state. The F9 hold (`data-interaction-open`) is respected: the optimistic rebuild removes the cell's own hold first, and `rerenderMatrixFromCache` still skips while any OTHER interaction is open (the status line is the fallback there, and the next poll catches up).
|
|
59
|
+
- **Feedback loops:** None.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 6. External surfaces
|
|
64
|
+
|
|
65
|
+
- **Other agents / other users:** None — a client-side render change shipped in the package `dashboard/` directory (served via `express.static`), reaching deployed agents on the normal update path (no `PostUpdateMigrator` entry needed, same as Phase 1).
|
|
66
|
+
- **External systems:** None.
|
|
67
|
+
- **Persistent state:** None — the glance persists nothing; `matrixTransient` is in-memory dashboard state only.
|
|
68
|
+
- **Operator surface (Mobile-Complete):** The Commitments "Mark delivered" action is preserved and phone-completable at Layer 3. The Blockers tab is read-only (as before). The Subscriptions cancel/sign-in flow is unchanged except that a confirmed cancel now resets the cell faster. No new operator action is introduced without a surface.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
73
|
+
|
|
74
|
+
Touches `dashboard/glance.js`, `dashboard/index.html`, `dashboard/subscriptions.js` — REQUIRED, and it is the whole point of the change.
|
|
75
|
+
|
|
76
|
+
1. **Leads with the primary action?** Yes. Both tabs now open on a one-sentence plain-English headline + big labeled tiles — the answer ("where do my promises / blockers stand?") is the first thing rendered, not a wall of records. The old Blockers page-one was a ~7,000-word raw table; it is gone from Layer 1.
|
|
77
|
+
2. **Zero raw internals as primary content?** Yes, and enforced. The F10 validator scans the concatenated headline + every tile label + value and refuses to build a glance carrying internal IDs, cadences, config keys, or insider terms. Raw internals (id `BLK-004`, `cadence 1800s`, state slugs, recheck timestamps) live only at Layer 3, one or two taps down. Verified live in-browser: the Commitments headline reads "I'm carrying 5 open promises; 1 needs attention soon, 1 is overdue."; the Blockers headline reads "1 thing is truly stuck right now; 2 are being worked."
|
|
78
|
+
3. **Destructive actions de-emphasized?** No destructive action is added. "Mark delivered" (Commitments) is a constructive Layer-3 action; the Blockers tab is read-only. The Subscriptions Cancel affordance is unchanged in prominence.
|
|
79
|
+
4. **Plain language + phone width?** Labels read the way a non-engineer speaks ("Truly stuck", "Being worked", "Due soon", "Overdue"). The glance uses the shared responsive `.glance-*` CSS shipped + browser-verified in Phase 1 (flex tiles, `overflow-x` contained). No new bespoke inline styles. State words at Layer 3 are humanized ("Truly stuck for now (recheck scheduled)", never a raw `true-blocker` slug at the glance). The decaying-hypothesis framing is preserved — a true-blocker is "best current understanding … not 'give up'", never "stop trying".
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
84
|
+
|
|
85
|
+
**Machine-local BY DESIGN — pure client-side renderer, no machine-divergent state.** `dashboard/glance.js` persists nothing, reads no config, and holds no server state; it renders whatever data the adopting tab already fetches and inherits that endpoint's posture. The Blockers glance drills into `GET /blockers` (a per-machine ledger read, unchanged by this PR); the Commitments glance into `GET /commitments` (posture unchanged). It emits no user-facing notices (no one-voice concern), holds no durable state (nothing to strand on topic transfer), and generates no URLs. The Subscriptions matrix already reads pool-scope (`?scope=pool`) so a login started on another machine surfaces here; the `cancelled` transient is per-dashboard-session in-memory display state that self-clears on the next poll — it introduces no new cross-machine surface.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 8. Rollback cost
|
|
90
|
+
|
|
91
|
+
Pure client-side code change — revert the `dashboard/*` files and ship a patch. No persistent state, no data migration, no agent-state repair. During the rollback window a user would see the previous glance/table render; no functional regression (the underlying routes are untouched). The conformance-ratchet constants revert with the file.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Conclusion
|
|
96
|
+
|
|
97
|
+
The review produced no design changes and flags no concerns. The change is confined to the presentation layer, reuses the Phase-1 component and its safety contract (`sanitizeForDisplay` + `textContent`, no `innerHTML`) — which is a security *improvement* for Blockers over the old `escapeHtml`-into-`innerHTML` table — and is fully enforced by the F10 word-budget and F11 drill-down ratchets now covering both rebuilt tabs across all three test tiers, plus a live in-browser render of both tabs. Clear to ship.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Second-pass review (if required)
|
|
102
|
+
|
|
103
|
+
Not required — no block/allow, session-lifecycle, gate/sentinel/watchdog, coherence, or compaction surface is touched. (This is a client-side dashboard render change; the qualifying triggers in `/instar-dev` Phase 5 do not apply.)
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Evidence pointers
|
|
108
|
+
|
|
109
|
+
- Unit: `tests/unit/dashboard-glance-word-budget.test.ts` (43), `tests/unit/dashboard-glance-drilldown.test.ts` (13), `tests/unit/subscriptions-render.test.ts` (+2 for #1428), `tests/unit/follow-me-controller-wiring.test.ts` (+2 for #1428 both sides of the boundary).
|
|
110
|
+
- Integration: `tests/integration/glance-blockers-tab.test.ts` (real `/blockers` route + BlockerLedger), `tests/integration/glance-commitments-tab.test.ts`.
|
|
111
|
+
- E2E: `tests/e2e/glance-blockers-tab-lifecycle.test.ts` (feature ON 200 / dark 503 / shipped-file check), `tests/e2e/glance-commitments-tab-lifecycle.test.ts`.
|
|
112
|
+
- Live browser render (Playwright, stubbed-route harness): Commitments headline "I'm carrying 5 open promises; 1 needs attention soon, 1 is overdue." with an Overdue tile drilling to CMT-101 (past hard deadline, classified overdue not due-soon) → Layer-3 record with cadence 1800s + Mark-delivered; Blockers headline "1 thing is truly stuck right now; 2 are being worked." with Truly-stuck drilling to BLK-004 → Layer-3 record showing "recheck after" and "not 'give up'".
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Class-Closure Declaration (display-only mirror)
|
|
117
|
+
|
|
118
|
+
No agent-authored-artifact defect — not applicable. The #1435 (overdue misclassification / missing tile / grammar) and #1428 (stale cancel window) fixes are defects in runtime client-side dashboard code, not in an LLM prompt, hook, config, skill, or standards text. This change adds no self-triggered controller (the `cancelled` transient is a display hint cleared each poll, not a loop/monitor/sentinel/reaper/scheduler/recovery path that fires a restart/swap/respawn/spawn/notify/retry/kill). The recurrence of an over-budget or jargon-carrying or dead-end glance for these two tabs is now structurally refused by `validateGlanceSpec` + the F10/F11 ratchets (`tests/unit/dashboard-glance-word-budget.test.ts`, `tests/unit/dashboard-glance-drilldown.test.ts`), which render both rebuilt tabs' real builders under adversarial fixtures.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Side-Effects Review — Worktree-safe Telegram relay recovery
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `telegram-reply-worktree-home`
|
|
4
|
+
**Date:** `2026-07-10`
|
|
5
|
+
**Author:** `instar-codey`
|
|
6
|
+
**Second-pass reviewer:** `framework_guard_review`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
The canonical `telegram-reply.sh` now resolves its owning agent home from explicit `INSTAR_AGENT_HOME`, otherwise from the exact `/.worktrees/` path marker, otherwise from the unchanged current directory. Config reads and recovery queue writes use that home. Recovery refuses an `unknown` agent id loudly before creating state. PostUpdateMigrator registers the v1.3.813 template SHA so existing unmodified installs receive the fix. Integration and migration tests cover all boundaries.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- Agent-home resolution — modify — explicit launcher state wins; only a structural worktree marker permits walk-up.
|
|
15
|
+
- Recoverable relay enqueue — modify — known agents retain durable enqueue; unknown identity exits non-zero without an undrainable store.
|
|
16
|
+
- Existing-script migration — modify — the current shipped SHA becomes an allowed predecessor for safe backup-and-replace.
|
|
17
|
+
|
|
18
|
+
## 1. Over-block
|
|
19
|
+
|
|
20
|
+
A recoverable relay attempt with a genuinely missing `projectName` exits non-zero rather than queueing locally. This is intentional because a live server cannot drain an `unknown`-keyed queue. The message remains in the calling transcript and the stderr reason is explicit. Agent ids literally named `unknown` are also refused; that reserved value is already the absence fallback and cannot safely identify a drain owner.
|
|
21
|
+
|
|
22
|
+
## 2. Under-block
|
|
23
|
+
|
|
24
|
+
An incorrectly supplied but non-empty `INSTAR_AGENT_HOME` can still point at the wrong agent; launcher-provided identity is authoritative and this script cannot independently authenticate filesystem ownership. A cwd with a nonstandard worktree layout lacking the exact marker remains unchanged rather than guessed upward. Customized deployed relay scripts are preserved and receive a `.new` candidate, so their operator must reconcile the update through the existing degradation path.
|
|
25
|
+
|
|
26
|
+
## 3. Level-of-abstraction fit
|
|
27
|
+
|
|
28
|
+
The fix lives in the installed relay template because both the config lookup and recovery path are shell-side before the server can participate. It reuses the existing PostUpdateMigrator hash-safe deployment authority rather than adding another installer. The narrow structural marker is the least-powerful resolver that covers the defined worktree convention without cross-tenant discovery.
|
|
29
|
+
|
|
30
|
+
## 4. Signal vs authority compliance
|
|
31
|
+
|
|
32
|
+
Required reference: [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
33
|
+
|
|
34
|
+
- [x] This is hard-invariant validation at a transport boundary, not a judgment about message meaning.
|
|
35
|
+
|
|
36
|
+
The unknown-id refusal has deterministic blocking authority because a queue keyed `unknown` is mechanically undrainable. It does not classify conversational content or intent. The worktree resolver likewise selects from explicit structural ownership signals and performs no semantic judgment.
|
|
37
|
+
|
|
38
|
+
## 5. Interactions
|
|
39
|
+
|
|
40
|
+
- **Shadowing:** home resolution runs before config-derived port/auth/id values, so all later paths share one owner. Explicit port/auth environment overrides keep their existing precedence.
|
|
41
|
+
- **Double-fire:** the change prevents an orphan queue from later double-firing; known-id enqueue and delivery-failed emission retain their existing single path and delivery id.
|
|
42
|
+
- **Races:** the change introduces zero shared mutable structures. The queue retains its existing SQLite concurrency and permission behavior.
|
|
43
|
+
- **Feedback loops:** a refused unknown-id attempt cannot enter the redrive loop; it remains loud in the caller transcript instead.
|
|
44
|
+
|
|
45
|
+
## 6. External surfaces
|
|
46
|
+
|
|
47
|
+
Worktree-launched Telegram replies now reach the correct local server and recovery database. Unknown-id failures gain a clearer stderr explanation and non-zero status; they create zero persistent database files. Existing unmodified agent scripts are backed up and replaced on update. Routes, credentials, network destinations, and operator actions remain unchanged.
|
|
48
|
+
|
|
49
|
+
## 6b. Operator-surface quality
|
|
50
|
+
|
|
51
|
+
Operator surface unchanged; this criterion is not applicable.
|
|
52
|
+
|
|
53
|
+
## 7. Multi-machine posture
|
|
54
|
+
|
|
55
|
+
**Machine-local by design:** an agent home, server port, auth injection, and pending-relay database belong to the machine executing the script. The message route continues to use the local owning server, whose existing messaging layer handles user delivery. The change emits zero additional notices, creates zero cross-machine durable records, leaves topic transfer unchanged, and generates zero URLs.
|
|
56
|
+
|
|
57
|
+
## 8. Rollback cost
|
|
58
|
+
|
|
59
|
+
Pure script/template and migration-allowlist rollback: revert and ship a patch. Correctly queued rows remain compatible with the existing drain path. Schema and state repair are unnecessary. A rollback would reintroduce the worktree orphan risk until deployed scripts update again.
|
|
60
|
+
|
|
61
|
+
## Conclusion
|
|
62
|
+
|
|
63
|
+
The fix closes both halves of issue #1086 without broad filesystem discovery: worktree sessions resolve the owning agent home, and identity-less recovery fails visibly instead of persisting an undrainable ghost. Normal cwd behavior and customized-script preservation remain intact. Clear to ship after independent messaging-path review and CI.
|
|
64
|
+
|
|
65
|
+
## Second-pass review
|
|
66
|
+
|
|
67
|
+
**Reviewer:** framework_guard_review
|
|
68
|
+
**Independent read of the artifact:** concur
|
|
69
|
+
|
|
70
|
+
Home resolution is conservative and correctly ordered: explicit launcher home, exact `/.worktrees/` structural owner, then unchanged cwd. Config and recovery state share that owner; unknown identity stays transcript-visible, exits non-zero, and creates no undrainable queue. The v1.3.813 template SHA matches the migration allowlist entry, customized scripts retain the `.new` path, and the reviewer's focused suite passed 26/26.
|
|
71
|
+
|
|
72
|
+
## Evidence pointers
|
|
73
|
+
|
|
74
|
+
- `tests/integration/telegram-reply-end-to-end.test.ts`
|
|
75
|
+
- `tests/unit/PostUpdateMigrator-telegramReply.test.ts`
|
|
76
|
+
- `tests/unit/lint-template-sha-history.test.ts`
|
|
77
|
+
- `tests/unit/migration-relay-script-hash.test.ts`
|