instar 1.3.1069 → 1.3.1071
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/core/ConvergenceChecker.js +1 -1
- package/dist/core/ConvergenceChecker.js.map +1 -1
- package/dist/core/PostUpdateMigrator.js +1 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +22 -1
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/data/standards-registry.meta.json +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +21 -21
- package/src/data/standards-registry.meta.json +1 -1
- package/src/templates/scripts/convergence-check.sh +1 -1
- package/upgrades/1.3.1070.md +61 -0
- package/upgrades/1.3.1071.md +26 -0
- package/upgrades/side-effects/convergence-check-settling-word-boundary.md +103 -0
- package/upgrades/side-effects/spawn-postdelivery-bookkeeping-truth.md +135 -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-30T11:46:31.561Z",
|
|
5
|
+
"instarVersion": "1.3.1071",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
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": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1138,7 +1138,7 @@
|
|
|
1138
1138
|
"type": "template",
|
|
1139
1139
|
"domain": "operations",
|
|
1140
1140
|
"sourcePath": "src/templates/scripts/convergence-check.sh",
|
|
1141
|
-
"contentHash": "
|
|
1141
|
+
"contentHash": "d63901cf7110861b2a65a40b5c876902bd972b53e6a4c7ca4bf55db76e8e640a",
|
|
1142
1142
|
"since": "2025-01-01"
|
|
1143
1143
|
},
|
|
1144
1144
|
"template:emit-session-clock.sh": {
|
|
@@ -1538,7 +1538,7 @@
|
|
|
1538
1538
|
"type": "subsystem",
|
|
1539
1539
|
"domain": "sessions",
|
|
1540
1540
|
"sourcePath": "src/core/SessionManager.ts",
|
|
1541
|
-
"contentHash": "
|
|
1541
|
+
"contentHash": "61e806044da971ee96b84a1e3a540fe37527f1cb38a5a072323a90fd0041f1c1",
|
|
1542
1542
|
"since": "2025-01-01"
|
|
1543
1543
|
},
|
|
1544
1544
|
"subsystem:auto-updater": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -39,7 +39,7 @@ if echo "$CONTENT" | grep -qiE "(^|[^a-zA-Z])i.ll (make sure|ensure|guarantee|al
|
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
41
|
# 3. SETTLING — Accepting empty results without digging deeper
|
|
42
|
-
if echo "$CONTENT" | grep -qiE "(no (data|results|information) (available|found|exists)|nothing (to report|happened|was found)|there (is|are) no|could(n.t| not) find (any|the)|appears to be empty|no (relevant|matching|applicable))"; then
|
|
42
|
+
if echo "$CONTENT" | grep -qiE "(no (data|results|information) (available|found|exists)|nothing (to report|happened|was found)|there (is|are) no([^a-zA-Z]|$)|could(n.t| not) find (any|the)|appears to be empty|no (relevant|matching|applicable))"; then
|
|
43
43
|
ISSUES+=("SETTLING: You're reporting nothing found. Did you check multiple sources? Could the data source be stale or the search terms wrong? Empty results deserve investigation, not acceptance.")
|
|
44
44
|
ISSUE_COUNT=$((ISSUE_COUNT + 1))
|
|
45
45
|
fi
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The pre-messaging quality gate (`convergence-check`) has a **settling** rule that
|
|
9
|
+
catches an agent reporting an empty result without digging — "there is no data",
|
|
10
|
+
"nothing was found". It searched for the phrase `there (is|are) no` with no trailing
|
|
11
|
+
word boundary, so it also matched inside longer words that merely begin with "no":
|
|
12
|
+
**nothing**, **none**, **nobody**.
|
|
13
|
+
|
|
14
|
+
The result was that ordinary English got blocked. "There is nothing pathological
|
|
15
|
+
required" was treated identically to "There is no data available."
|
|
16
|
+
|
|
17
|
+
The rule now requires `no` to be a whole word, using the same guard idiom the adjacent
|
|
18
|
+
commitment rule already applies to `promise` (`([^a-zA-Z]|$)`).
|
|
19
|
+
|
|
20
|
+
**The same regex lived in three places** — the shell template, its TypeScript port
|
|
21
|
+
(`ConvergenceChecker`), and an inline fallback in `PostUpdateMigrator` used when the
|
|
22
|
+
template cannot be loaded. All three are corrected, and a new drift guard fails the
|
|
23
|
+
build if they ever stop matching each other. Fixing only the template would have left
|
|
24
|
+
the port broken and shipped the bug to any agent whose template load failed.
|
|
25
|
+
|
|
26
|
+
The genuine catch is preserved by a **different** branch of the same rule: "there is
|
|
27
|
+
nothing to report" still blocks, via `nothing (to report|happened|was found)`. That is
|
|
28
|
+
asserted by an explicit test, because it was the main risk of narrowing the pattern.
|
|
29
|
+
|
|
30
|
+
## What to Tell Your User
|
|
31
|
+
|
|
32
|
+
Nothing is required of you, and nothing changes in how you talk to your agent.
|
|
33
|
+
|
|
34
|
+
Your agent runs a quality check on its own messages before sending them, to catch
|
|
35
|
+
habits like giving up after one empty search. That check was mis-firing on the word
|
|
36
|
+
"nothing" — so an agent writing a perfectly good sentence like "there is nothing
|
|
37
|
+
unusual here" could be stopped and made to rewrite it for no reason.
|
|
38
|
+
|
|
39
|
+
That mis-fire is fixed. The check still catches the real thing it was built for; it
|
|
40
|
+
just no longer trips over three common words. You may notice slightly fewer moments
|
|
41
|
+
where your agent pauses and rephrases itself for no visible reason.
|
|
42
|
+
|
|
43
|
+
## Summary of New Capabilities
|
|
44
|
+
|
|
45
|
+
None — this is a correctness fix to an existing check. No new endpoints, config keys,
|
|
46
|
+
commands, or agent-facing surfaces. The only addition is an internal drift guard that
|
|
47
|
+
keeps the three copies of the rule in sync.
|
|
48
|
+
|
|
49
|
+
## Evidence
|
|
50
|
+
|
|
51
|
+
- 22 new unit tests. Run against the **unfixed** source first: **12 failed / 10 passed**
|
|
52
|
+
there. So 12 assertions genuinely discriminate (6 false-positive cases across the
|
|
53
|
+
shell and TypeScript implementations, plus 6 drift-guard assertions); the other 10
|
|
54
|
+
pass either way and are labelled `CONTROL` in the test file rather than counted as
|
|
55
|
+
evidence.
|
|
56
|
+
- Reproduced from a real incident: a second session of this agent, running on another
|
|
57
|
+
machine, had a correct message blocked twice on "there is nothing pathological
|
|
58
|
+
required" and reported it instead of rewording until something got through.
|
|
59
|
+
- Migration parity verified rather than assumed: `PostUpdateMigrator` writes
|
|
60
|
+
`scripts/convergence-check.sh` unconditionally on every migration run, and the
|
|
61
|
+
template was confirmed byte-identical to the installed copy before editing.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
A headless session launch creates the live terminal process with its full instruction first, then writes a session record for monitoring. If that record write failed, the launch method used to reject even though the new agent was already running the instruction. Callers could then retry the same work into a second live session.
|
|
9
|
+
|
|
10
|
+
The post-launch record write is now classified as degraded bookkeeping: it is logged and structurally reported with the exact session identity, but the already-live session is returned as success. Real failures before the terminal session exists still reject exactly as before. The interactive reroute path is unchanged because it injects its instruction only after persistence, so a write failure there does not prove delivery.
|
|
11
|
+
|
|
12
|
+
The tradeoff is explicit: if the first record write fails, the live terminal session is outside normal monitoring and automatic reaping and may survive restart until manually cleaned up. Reporting the launch as failed would not fix that orphan; it would additionally invite a second agent to run the same instruction. The identified orphan is the safer and observable outcome.
|
|
13
|
+
|
|
14
|
+
## What to Tell Your User
|
|
15
|
+
|
|
16
|
+
If one agent starts work handed to it by another agent, it will no longer claim the start failed merely because the monitoring record could not be saved afterward. That prevents the sender from launching the same instruction twice while keeping the bookkeeping problem visible for diagnosis.
|
|
17
|
+
|
|
18
|
+
## Summary of New Capabilities
|
|
19
|
+
|
|
20
|
+
No new capability, endpoint, setting, or operator action. This corrects the success/failure boundary of existing headless session launches. Rollback is a single revert with no state migration.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
The new regression forces the session-state write to throw after one successful captured tmux launch. Against the unfixed source, the suite produced 25 passes and one failure: the simulated bookkeeping exception escaped. With the fix, all 26 tests pass, and the regression proves one launch, the exact prompt in that launch, a live session, and a resolved result.
|
|
25
|
+
|
|
26
|
+
Adjacent verification passed 131/131 assertions across headless spawn behavior, spawn-request queue behavior, framework portability, interactive session caps, and silent-fallback enforcement. `npm run lint` also passed, including TypeScript checking and the complete repository lint chain.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Side-effects review — SETTLING rule word boundary
|
|
2
|
+
|
|
3
|
+
**Change.** The pre-messaging quality gate's SETTLING rule contained the fragment
|
|
4
|
+
`there (is|are) no` with no trailing boundary. It matched inside longer words that
|
|
5
|
+
merely begin with "no" — `nothing`, `none`, `nobody` — so ordinary descriptive
|
|
6
|
+
English was blocked as if the agent were settling for an empty search result. The
|
|
7
|
+
fragment is now `there (is|are) no([^a-zA-Z]|$)`, using the same guard idiom the
|
|
8
|
+
adjacent commitment rule already applied to `promise`.
|
|
9
|
+
|
|
10
|
+
**How it was found.** A second session of this agent, running on the Laptop, had a
|
|
11
|
+
correct message blocked twice on the phrase "there is nothing pathological
|
|
12
|
+
required" and reported it rather than working around it.
|
|
13
|
+
|
|
14
|
+
**The defect was larger than the one character.** The identical regex lives in
|
|
15
|
+
THREE places: the shell template, its TypeScript port (`ConvergenceChecker.ts`),
|
|
16
|
+
and an inline fallback in `PostUpdateMigrator` used when the template cannot be
|
|
17
|
+
loaded. All three are fixed, and a drift guard now asserts they stay identical.
|
|
18
|
+
Fixing only the template would have left the port broken and shipped the bug to
|
|
19
|
+
any agent whose template load failed.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
|
|
24
|
+
|
|
25
|
+
This change **removes** over-blocking; it adds none. Three realistic sentences that
|
|
26
|
+
were blocked now pass, each verified against the unfixed source first:
|
|
27
|
+
"There is nothing pathological required." / "I checked and there are none of those
|
|
28
|
+
left." / "There is nobody else on that machine."
|
|
29
|
+
|
|
30
|
+
## 2. Under-block — what failure modes does this still miss?
|
|
31
|
+
|
|
32
|
+
The rule now misses `there is no<word>` constructions where the longer word really
|
|
33
|
+
is a settling claim. I could not construct a realistic one — "nothing", "none" and
|
|
34
|
+
"nobody" are the only common continuations, and the genuinely-settling forms among
|
|
35
|
+
them ("there is nothing to report") are caught by a **different** alternative in the
|
|
36
|
+
same rule, `nothing (to report|happened|was found)`. That is asserted explicitly by
|
|
37
|
+
test rather than assumed, because it is the whole reason this narrowing is safe.
|
|
38
|
+
|
|
39
|
+
More broadly, the SETTLING rule remains a heuristic keyword matcher and always was.
|
|
40
|
+
This change does not make it smarter, only less wrong on one fragment.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
Correct layer. The bug is in the pattern itself, not in the hook that invokes it or
|
|
45
|
+
the gate that consumes the verdict. No caller changes. The deeper question — whether
|
|
46
|
+
a keyword matcher should hold blocking authority at all — is a real one but is out
|
|
47
|
+
of scope here and is not made worse by this change.
|
|
48
|
+
|
|
49
|
+
## 4. Signal vs authority compliance
|
|
50
|
+
|
|
51
|
+
This gate **does** hold blocking authority with brittle logic, which is in tension
|
|
52
|
+
with `docs/signal-vs-authority.md`. That tension pre-exists this change and is
|
|
53
|
+
unchanged by it: the fix strictly narrows a false-positive branch, so it moves the
|
|
54
|
+
brittle authority in the safer direction (fewer wrong blocks) without expanding what
|
|
55
|
+
the check can block. No new authority is introduced, and no rule gains reach.
|
|
56
|
+
|
|
57
|
+
## 5. Interactions
|
|
58
|
+
|
|
59
|
+
None found. The fragment is one alternative inside one alternation in one rule. The
|
|
60
|
+
other six rule categories are untouched. The one interaction that matters is
|
|
61
|
+
intra-rule and is deliberate: `nothing (to report|...)` still catches the true
|
|
62
|
+
positive the narrowed fragment gives up, so the two alternatives now divide the work
|
|
63
|
+
cleanly instead of overlapping.
|
|
64
|
+
|
|
65
|
+
## 6. External surfaces
|
|
66
|
+
|
|
67
|
+
Agent-facing only. No end user sees this gate. Agents will see strictly fewer
|
|
68
|
+
spurious "MESSAGE BLOCKED" refusals on outbound messaging. No API, config key, or
|
|
69
|
+
schema changes. No timing or conversation-state dependence.
|
|
70
|
+
|
|
71
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
72
|
+
|
|
73
|
+
**Machine-local by design, and correctly so** — no `machine-local-justification`
|
|
74
|
+
marker is required because this introduces no new state or feature surface. The
|
|
75
|
+
script is a per-machine installed file, already written to every machine by the
|
|
76
|
+
existing always-overwrite migration path
|
|
77
|
+
(`PostUpdateMigrator` writes `scripts/convergence-check.sh` unconditionally). Each
|
|
78
|
+
machine therefore converges on the fixed copy independently at its next update;
|
|
79
|
+
there is nothing to replicate, merge, or proxy. Migration parity was **verified, not
|
|
80
|
+
assumed**: the write at `PostUpdateMigrator.ts:9943` is unconditional, and the
|
|
81
|
+
template and the installed copy were confirmed byte-identical before editing.
|
|
82
|
+
|
|
83
|
+
Note the asymmetry this creates in the interim: the Laptop, which reported the bug,
|
|
84
|
+
keeps the broken copy until it updates. That is the normal update lag, not a
|
|
85
|
+
coherence defect.
|
|
86
|
+
|
|
87
|
+
## 8. Rollback cost
|
|
88
|
+
|
|
89
|
+
Trivial. Revert one commit; the migration overwrites the script back to the previous
|
|
90
|
+
content on the next update. No data migration, no state repair, no agent-side
|
|
91
|
+
cleanup. The change is additive to a regex and carries no persisted footprint.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Testing
|
|
96
|
+
|
|
97
|
+
22 unit tests. Run against the **unfixed** source first: **12 failed / 10 passed**
|
|
98
|
+
there, so 12 assertions genuinely discriminate (6 false-positive cases + 6 drift-guard
|
|
99
|
+
assertions) and the other 10 are labelled CONTROL in the file because they pass either
|
|
100
|
+
way and are not evidence for this change.
|
|
101
|
+
|
|
102
|
+
The drift guard is the structural half: without it, the next person fixes one copy of
|
|
103
|
+
three and the bug survives in the other two exactly as it did here.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Side-Effects Review — a delivered headless spawn must not report failure
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `spawn-postdelivery-bookkeeping-truth`
|
|
4
|
+
**Date:** `2026-07-29`
|
|
5
|
+
**Author:** `instar-codey`
|
|
6
|
+
**Second-pass reviewer:** `spawn-truth-review (independent Codex reviewer)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`SessionManager.spawnSession()` creates a headless tmux session with the full prompt in its launch argv, then persists the session record. The persistence call was outside the launch `try/catch`, so a state-write exception rejected the method after delivery had already occurred. This change catches only that post-delivery bookkeeping exception, reports the degradation, and returns the live `Session`. The real tmux-creation failure boundary is unchanged. The runtime change is in `src/core/SessionManager.ts`; the discriminating regression is in `tests/unit/headless-spawn-reroute.test.ts`.
|
|
11
|
+
|
|
12
|
+
Build location was re-grounded before editing: the branch starts at current `upstream/main` `30189100f` (`v1.3.1068`), with `upstream` resolving to `https://github.com/JKHeadley/instar.git` and `package.json` version `1.3.1068`. The worktree helper refused because this running legacy project-bound agent is absent from the new global registry, so an existing clean Instar-owned worktree under this agent's home was switched to a fresh branch at that exact upstream head; no stale branch content or unrelated changes were carried forward.
|
|
13
|
+
|
|
14
|
+
## Decision-point inventory
|
|
15
|
+
|
|
16
|
+
- `SessionManager.spawnSession()` post-launch persistence result — **modified** — a state-write exception after successful headless launch now reports degraded bookkeeping and resolves with the already-live session.
|
|
17
|
+
- `SessionManager.spawnSession()` tmux launch result — **pass-through** — failure before the terminal session exists still rejects through the existing launch `try/catch`.
|
|
18
|
+
- `SpawnRequestManager` spawn-failure retry decision — **interaction only** — it no longer receives a false rejection for an already-delivered headless prompt, preventing an unsafe redrive.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. Over-block
|
|
23
|
+
|
|
24
|
+
**No new rejection surface.** The change removes one false rejection after delivery. Legitimate launch failures, session-cap refusals, worktree-lock failures, and tmux-name collisions remain byte-for-byte on their existing rejecting paths.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Under-block
|
|
29
|
+
|
|
30
|
+
Framed as under-fix because this change does not block:
|
|
31
|
+
|
|
32
|
+
1. A live headless session whose first state write fails is absent from persisted monitoring, session counts, dashboard reads, kill-by-id, and normal cleanup. `OrphanProcessReaper` classifies the unknown tmux name as external rather than adopting it, so there is no later automatic reconciliation: it can survive restart indefinitely until manually cleaned up. The structured degradation report includes the exact tmux name and session id and states this consequence. Returning failure would not repair the missing state; it would add duplicate-delivery risk, so this orphan posture is the explicitly accepted lower-risk outcome.
|
|
33
|
+
2. The rerouted-interactive path is intentionally unchanged. It creates a live REPL, saves state, and only then injects the prompt asynchronously. A state-write failure there does **not** prove prompt delivery, so the headless truth rule cannot honestly be copied to that path.
|
|
34
|
+
3. `StateManager.saveSession()` can mutate older duplicate-name records through best-effort stale-record supersession before the new record's atomic write fails. It does not publish a partially written new record: the new file is written through a temp-file rename, lifecycle-journal emission is non-throwing, and cache invalidation follows only after the atomic write returns. The accepted degraded shape is therefore an untracked live process, potentially alongside already-superseded older records.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3. Level-of-abstraction fit
|
|
39
|
+
|
|
40
|
+
This is the correct layer. `SessionManager.spawnSession()` is the only function that knows the ordering of the two events: successful tmux creation with the prompt, then state persistence. `SpawnRequestManager` cannot safely reconstruct that truth from a generic exception or from persisted state, because persisted state is the operation that failed. Fixing the return contract at the source lets every caller distinguish actual launch failure from post-delivery bookkeeping failure without new error-string matching, probing, or duplicate recovery machinery.
|
|
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
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
49
|
+
|
|
50
|
+
- [x] No — this change has no block/allow surface.
|
|
51
|
+
|
|
52
|
+
The boundary is an enumerable ordering invariant, not a judgment about message meaning: after `execFileSync(tmux new-session …prompt)` returns successfully, delivery has occurred; before it returns, delivery is not claimed. No detector, heuristic, threshold, or conversational authority is added. The existing structured degradation reporter receives the bookkeeping failure as an observation.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 4b. Judgment-point check (Judgment Within Floors standard)
|
|
57
|
+
|
|
58
|
+
**No new static heuristic at a competing-signals decision point.** The choice depends on a single causal boundary in one synchronous function: whether the tmux creation call already returned successfully. There are no competing liveness, ownership, urgency, or conversation signals to arbitrate.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 5. Interactions
|
|
63
|
+
|
|
64
|
+
- **Shadowing:** the new catch surrounds only `state.saveSession(session)` after the headless launch block. It cannot shadow tmux creation errors, worktree resolution errors, reroute admission refusals, or pre-launch validation.
|
|
65
|
+
- **Double-fire:** this closes a double-fire hazard. A caller that receives success does not enter its spawn-failure retry path, so one delivered prompt stays one delivery.
|
|
66
|
+
- **Races:** no shared state, timer, or asynchronous step is added. The tmux launch and state write remain synchronous and ordered.
|
|
67
|
+
- **Adjacent retry path:** `SpawnRequestManager` still treats genuine `spawnSession` rejection as non-delivery. This fix makes that assumption true for the headless post-launch state-write case that previously violated it.
|
|
68
|
+
- **Monitoring:** a failed first state write leaves an untracked live process outside caps, dashboard reads, kill-by-id, `SessionManager` monitoring, and `OrphanProcessReaper` reconciliation. It may survive restart indefinitely. The change does not conceal that; it emits a direct error plus `DegradationReporter` containing the exact tmux name and session id. No blind retry is attempted because the original failure may be a durable write guard or filesystem failure, while stale-record supersession may already have changed older records.
|
|
69
|
+
- **Interactive reroute:** deliberately not covered. That path has not injected the prompt at its save boundary, so preserving its current rejection avoids falsely claiming delivery.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 6. External surfaces
|
|
74
|
+
|
|
75
|
+
- **Other agents and callers:** they now receive a successful spawn result when their headless prompt is already running, instead of an exception that encourages unsafe retry.
|
|
76
|
+
- **Install base:** every updated agent receives the corrected return semantics. There is no config flag because reporting an already-delivered operation as failed is never a desirable mode.
|
|
77
|
+
- **External systems:** no API, Telegram, Slack, GitHub, Cloudflare, or protocol shape changes.
|
|
78
|
+
- **Persistent state:** no new schema or store. The handled failure is specifically a failure to write the existing session record.
|
|
79
|
+
- **Timing/runtime conditions:** no new timing dependency. The boundary is the synchronous return of tmux session creation.
|
|
80
|
+
- **Operator surface:** no operator-facing action is added or changed.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
85
|
+
|
|
86
|
+
**No operator surface — not applicable.**
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
91
|
+
|
|
92
|
+
**Machine-local BY DESIGN.** A tmux session and its state file are facts about the machine that launched the process. The corrected result applies independently on whichever pool machine owns and executes the spawn; it does not create replicated state or authorize another machine to retry the same prompt.
|
|
93
|
+
|
|
94
|
+
- **User-facing notices:** none; only existing machine-local logs and degradation reporting, so no one-voice user notice is added.
|
|
95
|
+
- **Durable state/topic transfer:** no new durable state. A failed first session-state write leaves the process machine-local, untracked, and not automatically reconciled even after restart; the identified degradation event is the only action surface. This change prevents that condition from also producing a cross-machine duplicate delivery.
|
|
96
|
+
- **Generated URLs:** none.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 8. Rollback cost
|
|
101
|
+
|
|
102
|
+
- **Hot-fix release:** revert the catch/report block and the regression test, then ship the next patch.
|
|
103
|
+
- **Data migration:** none.
|
|
104
|
+
- **Agent state repair:** none introduced. Any session record that failed before or after this fix follows the existing machine-local recovery/cleanup posture.
|
|
105
|
+
- **User visibility:** rollback would restore the rare false-failure/duplicate-redrive risk during the propagation window.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Conclusion
|
|
110
|
+
|
|
111
|
+
The change is narrow and matches the actual delivery boundary: successful headless tmux creation means the prompt is already running, while earlier failures remain failures. Review preserved two explicit tradeoffs: the interactive reroute path cannot use the same rule because its prompt injection happens after persistence, and a first-write failure leaves an indefinitely untracked machine-local process. The latter is now identified by session id and tmux name in the degradation report and is accepted as less dangerous than duplicating an already-running instruction.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Second-pass review (required)
|
|
116
|
+
|
|
117
|
+
**Reviewer:** spawn-truth-review (independent Codex reviewer)
|
|
118
|
+
**Independent read of the artifact:** concur
|
|
119
|
+
|
|
120
|
+
The reviewer confirmed the synchronous headless delivery boundary, unchanged pre-delivery rejection path, duplicate-redrive prevention, and deliberate exclusion of rerouted-interactive. Its first read raised the indefinite-orphan and actionability gaps; after the artifact and degradation event were corrected to name the session and accept the true cleanup posture, the reviewer concurred.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Evidence pointers
|
|
125
|
+
|
|
126
|
+
- Pre-fix focused run: 25 passed, 1 failed; the new regression alone failed on the escaped simulated state-write exception.
|
|
127
|
+
- Post-fix focused run: 26/26 passed.
|
|
128
|
+
- Adjacent verification: 131/131 passed across headless spawn, spawn request management, framework portability, interactive cap, and no-silent-fallback suites.
|
|
129
|
+
- `npm run lint`: passed, including `tsc --noEmit` and the complete lint chain.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Class-Closure Declaration (display-only mirror)
|
|
134
|
+
|
|
135
|
+
No agent-authored-artifact defect and no self-triggered controller added or modified — not applicable.
|