instar 0.28.35 → 0.28.41
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/index.html +40 -30
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +105 -46
- package/dist/commands/server.js.map +1 -1
- package/dist/core/CoherenceGate.d.ts +7 -0
- package/dist/core/CoherenceGate.d.ts.map +1 -1
- package/dist/core/CoherenceGate.js +7 -6
- package/dist/core/CoherenceGate.js.map +1 -1
- package/dist/core/CoherenceReviewer.d.ts +20 -5
- package/dist/core/CoherenceReviewer.d.ts.map +1 -1
- package/dist/core/CoherenceReviewer.js +36 -6
- package/dist/core/CoherenceReviewer.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +0 -14
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +2 -123
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +9 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +18 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/monitoring/CompactionSentinel.d.ts +143 -0
- package/dist/monitoring/CompactionSentinel.d.ts.map +1 -0
- package/dist/monitoring/CompactionSentinel.js +262 -0
- package/dist/monitoring/CompactionSentinel.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +0 -40
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +4 -156
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +0 -75
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/threadline/adapters/RESTServer.d.ts +7 -1
- package/dist/threadline/adapters/RESTServer.d.ts.map +1 -1
- package/dist/threadline/adapters/RESTServer.js +62 -1
- package/dist/threadline/adapters/RESTServer.js.map +1 -1
- package/dist/threadline/relay/OfflineQueue.js +1 -1
- package/dist/threadline/relay/OfflineQueue.js.map +1 -1
- package/package.json +1 -1
- package/scripts/check-upgrade-guide.js +9 -115
- package/scripts/collect-metrics.py +25 -120
- package/scripts/upgrade-guide-validator.mjs +221 -0
- package/src/data/builtin-manifest.json +18 -18
- package/upgrades/0.28.36.md +40 -0
- package/upgrades/0.28.37.md +69 -0
- package/upgrades/0.28.38.md +41 -0
- package/upgrades/0.28.39.md +29 -0
- package/upgrades/0.28.40.md +32 -0
- package/upgrades/0.28.41.md +41 -0
- package/upgrades/NEXT.md +18 -0
- package/upgrades/0.28.10.md +0 -19
- package/upgrades/0.28.11.md +0 -19
- package/upgrades/0.28.13.md +0 -11
- package/upgrades/0.28.15.md +0 -11
- package/upgrades/0.28.18.md +0 -12
- package/upgrades/0.28.20.md +0 -19
- package/upgrades/0.28.21.md +0 -23
- package/upgrades/0.28.22.md +0 -23
- package/upgrades/0.28.23.md +0 -19
- package/upgrades/0.28.24.md +0 -18
- package/upgrades/0.28.25.md +0 -21
- package/upgrades/0.28.26.md +0 -20
- package/upgrades/0.28.27.md +0 -20
- package/upgrades/0.28.28.md +0 -20
- package/upgrades/0.28.30.md +0 -25
- package/upgrades/0.28.31.md +0 -38
- package/upgrades/0.28.32.md +0 -22
- package/upgrades/0.28.33.md +0 -22
- package/upgrades/0.28.35.md +0 -37
- package/upgrades/0.28.5.md +0 -17
- package/upgrades/0.28.7.md +0 -24
- package/upgrades/0.28.8.md +0 -21
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// Pure validation logic for upgrade guides — no filesystem side effects.
|
|
2
|
+
// The CLI entry point (check-upgrade-guide.js) wraps this with I/O.
|
|
3
|
+
// Extracted for unit testability.
|
|
4
|
+
|
|
5
|
+
export const REQUIRED_SECTIONS = [
|
|
6
|
+
'## What Changed',
|
|
7
|
+
'## What to Tell Your User',
|
|
8
|
+
'## Summary of New Capabilities',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export const MIN_LENGTH = 200;
|
|
12
|
+
|
|
13
|
+
// Keywords that indicate the guide is claiming to fix a bug. When any of these
|
|
14
|
+
// appear inside "## What Changed", the Evidence section becomes mandatory.
|
|
15
|
+
// Limited to "What Changed" (not the whole doc) to reduce false positives from
|
|
16
|
+
// marketing language in "What to Tell Your User".
|
|
17
|
+
const FIX_PATTERNS = [
|
|
18
|
+
/\bfix(es|ed|ing)?\b/i,
|
|
19
|
+
/\bbug(fix)?\b/i,
|
|
20
|
+
/\bregression\b/i,
|
|
21
|
+
/\bresolves?\b/i,
|
|
22
|
+
/\bresolved\b/i,
|
|
23
|
+
/\bcrashes?\b/i,
|
|
24
|
+
/\bcrashed\b/i,
|
|
25
|
+
/\bcrashing\b/i,
|
|
26
|
+
/\bbroken\b/i,
|
|
27
|
+
/\bstall(s|ed|ing)?\b/i,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export const NEXT_TEMPLATE = `# Upgrade Guide — vNEXT
|
|
31
|
+
|
|
32
|
+
<!-- bump: patch -->
|
|
33
|
+
<!-- Valid values: patch, minor, major -->
|
|
34
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
35
|
+
<!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
|
|
36
|
+
<!-- major = breaking changes to existing APIs or behavior -->
|
|
37
|
+
|
|
38
|
+
## What Changed
|
|
39
|
+
|
|
40
|
+
<!-- Describe what changed technically. What new features, APIs, behavioral changes? -->
|
|
41
|
+
<!-- Write this for the AGENT — they need to understand the system deeply. -->
|
|
42
|
+
|
|
43
|
+
## What to Tell Your User
|
|
44
|
+
|
|
45
|
+
<!-- Write talking points the agent should relay to their user. -->
|
|
46
|
+
<!-- This should be warm, conversational, user-facing — not a changelog. -->
|
|
47
|
+
<!-- Focus on what THEY can now do, not internal plumbing. -->
|
|
48
|
+
<!-- -->
|
|
49
|
+
<!-- PROHIBITED in this section (will fail validation): -->
|
|
50
|
+
<!-- camelCase config keys: silentReject, maxRetries, telegramNotify -->
|
|
51
|
+
<!-- Inline code backtick references like silentReject: false -->
|
|
52
|
+
<!-- Fenced code blocks -->
|
|
53
|
+
<!-- Instructions to edit files or run commands -->
|
|
54
|
+
<!-- -->
|
|
55
|
+
<!-- CORRECT style: "I can turn that on for you" not "set X to false" -->
|
|
56
|
+
<!-- The agent relays this to their user — keep it human. -->
|
|
57
|
+
|
|
58
|
+
- **[Feature name]**: "[Brief, friendly description of what this means for the user]"
|
|
59
|
+
|
|
60
|
+
## Summary of New Capabilities
|
|
61
|
+
|
|
62
|
+
| Capability | How to Use |
|
|
63
|
+
|-----------|-----------|
|
|
64
|
+
| [Capability] | [Endpoint, command, or "automatic"] |
|
|
65
|
+
|
|
66
|
+
## Evidence
|
|
67
|
+
|
|
68
|
+
<!-- REQUIRED if this release claims to fix a bug. -->
|
|
69
|
+
<!-- Unit tests passing is NOT evidence. Provide ONE of: -->
|
|
70
|
+
<!-- (a) Reproduction steps + observed before/after on a live system. -->
|
|
71
|
+
<!-- Include log excerpts, observed command output, or behavior -->
|
|
72
|
+
<!-- description. Make it specific enough that a future reader can -->
|
|
73
|
+
<!-- re-run it and see the same thing. -->
|
|
74
|
+
<!-- (b) "Not reproducible in dev — [concrete reason]" if the failure -->
|
|
75
|
+
<!-- mode truly can't be exercised locally (race conditions, -->
|
|
76
|
+
<!-- event-driven paths requiring external signals, etc). -->
|
|
77
|
+
<!-- -->
|
|
78
|
+
<!-- If this release doesn't claim a bug fix (pure feature / refactor), -->
|
|
79
|
+
<!-- leave this section blank or delete it — it's only enforced when -->
|
|
80
|
+
<!-- "What Changed" describes a fix. -->
|
|
81
|
+
|
|
82
|
+
[Describe reproduction + verified fix, OR "Not reproducible in dev — [concrete reason]"]
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract the content of a markdown H2 section by title. Returns the section
|
|
87
|
+
* body (between `## Title` and the next `## ` or end of file), or null if the
|
|
88
|
+
* section isn't present.
|
|
89
|
+
*/
|
|
90
|
+
export function extractSection(content, title) {
|
|
91
|
+
const pattern = new RegExp(`## ${title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([\\s\\S]*?)(?:\\n## |$)`);
|
|
92
|
+
const m = content.match(pattern);
|
|
93
|
+
return m ? m[1] : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Does the guide claim to fix a bug? Scans "## What Changed" for fix-indicating
|
|
98
|
+
* keywords. Returns true when any pattern matches.
|
|
99
|
+
*/
|
|
100
|
+
export function claimsFix(content) {
|
|
101
|
+
const section = extractSection(content, 'What Changed');
|
|
102
|
+
if (!section) return false;
|
|
103
|
+
return FIX_PATTERNS.some(p => p.test(section));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Validate the Evidence section when a fix is claimed. Returns an array of
|
|
108
|
+
* issue strings (empty when valid).
|
|
109
|
+
*/
|
|
110
|
+
export function evidenceIssues(content) {
|
|
111
|
+
const issues = [];
|
|
112
|
+
const section = extractSection(content, 'Evidence');
|
|
113
|
+
if (section === null) {
|
|
114
|
+
issues.push(
|
|
115
|
+
'"What Changed" claims a bug fix, but the guide has no "## Evidence" section. ' +
|
|
116
|
+
'Add one with reproduction + observed before/after, or "Not reproducible in dev — [reason]". ' +
|
|
117
|
+
'Unit tests passing is not evidence.'
|
|
118
|
+
);
|
|
119
|
+
return issues;
|
|
120
|
+
}
|
|
121
|
+
// Strip HTML comments and trim
|
|
122
|
+
const stripped = section.replace(/<!--[\s\S]*?-->/g, '').trim();
|
|
123
|
+
if (!stripped) {
|
|
124
|
+
issues.push(
|
|
125
|
+
'"## Evidence" section is empty (only template comments). ' +
|
|
126
|
+
'Fill it in with reproduction + verified fix, or "Not reproducible in dev — [reason]".'
|
|
127
|
+
);
|
|
128
|
+
return issues;
|
|
129
|
+
}
|
|
130
|
+
// Reject obvious placeholder text
|
|
131
|
+
if (
|
|
132
|
+
/\[Describe reproduction/i.test(stripped) ||
|
|
133
|
+
/\[evidence goes here\]/i.test(stripped) ||
|
|
134
|
+
/TODO\b/.test(stripped) ||
|
|
135
|
+
/FIXME\b/.test(stripped)
|
|
136
|
+
) {
|
|
137
|
+
issues.push(
|
|
138
|
+
'"## Evidence" section still contains placeholder text. ' +
|
|
139
|
+
'Replace it with a real reproduction + verification, or explicit "Not reproducible in dev — [reason]".'
|
|
140
|
+
);
|
|
141
|
+
return issues;
|
|
142
|
+
}
|
|
143
|
+
// Require at least 80 chars of real content — any less and it can't describe
|
|
144
|
+
// a reproduction meaningfully.
|
|
145
|
+
if (stripped.length < 80) {
|
|
146
|
+
issues.push(
|
|
147
|
+
`"## Evidence" section is too short (${stripped.length} chars). ` +
|
|
148
|
+
'Include concrete reproduction + observed before/after, or "Not reproducible in dev" with a concrete reason.'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return issues;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Full structural validation of a guide's content. Returns issue strings.
|
|
156
|
+
*/
|
|
157
|
+
export function validateGuideContent(content) {
|
|
158
|
+
const issues = [];
|
|
159
|
+
|
|
160
|
+
for (const section of REQUIRED_SECTIONS) {
|
|
161
|
+
if (!content.includes(section)) {
|
|
162
|
+
issues.push(`missing "${section}" section`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (content.length < MIN_LENGTH) {
|
|
167
|
+
issues.push(`guide is too short (${content.length} chars, minimum ${MIN_LENGTH}) — probably incomplete`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Template placeholders that were never filled in
|
|
171
|
+
if (content.includes('<!-- Describe what changed')) {
|
|
172
|
+
issues.push('"What Changed" section still contains template placeholder — fill it in');
|
|
173
|
+
}
|
|
174
|
+
if (content.includes('[Feature name]') || content.includes('[Brief, friendly description')) {
|
|
175
|
+
issues.push('"What to Tell Your User" section still contains template placeholder — fill it in');
|
|
176
|
+
}
|
|
177
|
+
if (content.includes('[Capability]') && content.includes('[Endpoint, command')) {
|
|
178
|
+
issues.push('"Summary of New Capabilities" section still contains template placeholder — fill it in');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// "What to Tell Your User" technical-leakage checks
|
|
182
|
+
const userSection = extractSection(content, 'What to Tell Your User');
|
|
183
|
+
if (userSection) {
|
|
184
|
+
const camelCaseConfigKey = /\b[a-z]+[A-Z][a-zA-Z]+\s*(?::|=)/.test(userSection);
|
|
185
|
+
if (camelCaseConfigKey) {
|
|
186
|
+
issues.push(
|
|
187
|
+
'"What to Tell Your User" contains a camelCase config key reference (e.g. "silentReject: false"). ' +
|
|
188
|
+
'Users should never be told to edit config directly. ' +
|
|
189
|
+
'Rephrase conversationally: "I can turn that on for you" not "set silentReject: false".'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (/`[^`]+`/.test(userSection)) {
|
|
193
|
+
issues.push(
|
|
194
|
+
'"What to Tell Your User" contains inline code (`...`). ' +
|
|
195
|
+
'Remove code formatting — user-facing language should be plain and conversational.'
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (/```/.test(userSection)) {
|
|
199
|
+
issues.push(
|
|
200
|
+
'"What to Tell Your User" contains a fenced code block. ' +
|
|
201
|
+
'This section is for user-facing narrative — move technical examples to "What Changed".'
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Evidence bar — when the guide claims a fix, require an Evidence section.
|
|
207
|
+
if (claimsFix(content)) {
|
|
208
|
+
issues.push(...evidenceIssues(content));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return issues;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Parse the declared bump type from a guide's <!-- bump: TYPE --> comment.
|
|
216
|
+
* Returns 'patch' | 'minor' | 'major' | null.
|
|
217
|
+
*/
|
|
218
|
+
export function parseBumpType(content) {
|
|
219
|
+
const match = /<!--\s*bump:\s*(patch|minor|major)\s*-->/.exec(content);
|
|
220
|
+
return match ? match[1] : null;
|
|
221
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-04-
|
|
5
|
-
"instarVersion": "0.28.
|
|
4
|
+
"generatedAt": "2026-04-15T08:32:09.822Z",
|
|
5
|
+
"instarVersion": "0.28.41",
|
|
6
6
|
"entryCount": 186,
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
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": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:post-action-reflection": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "evolution",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:external-communication-guard": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "safety",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:scope-coherence-collector": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "coherence",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:free-text-guard": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "safety",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:claim-intercept": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "coherence",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept-response": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:auto-approve-permissions": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "safety",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"job:health-check": {
|
|
@@ -1408,7 +1408,7 @@
|
|
|
1408
1408
|
"type": "subsystem",
|
|
1409
1409
|
"domain": "sessions",
|
|
1410
1410
|
"sourcePath": "src/core/SessionManager.ts",
|
|
1411
|
-
"contentHash": "
|
|
1411
|
+
"contentHash": "541adcef56fc133c4b56034eeacf17d27ebc4512dd89b09eced58e427b10df39",
|
|
1412
1412
|
"since": "2025-01-01"
|
|
1413
1413
|
},
|
|
1414
1414
|
"subsystem:auto-updater": {
|
|
@@ -1432,7 +1432,7 @@
|
|
|
1432
1432
|
"type": "subsystem",
|
|
1433
1433
|
"domain": "updates",
|
|
1434
1434
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1435
|
-
"contentHash": "
|
|
1435
|
+
"contentHash": "ce2a71744732f5e6d5db9534ee06b33bac00a1f898d9dd8292d44705a353deaf",
|
|
1436
1436
|
"since": "2025-01-01"
|
|
1437
1437
|
},
|
|
1438
1438
|
"subsystem:scheduler": {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.36
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Compaction-resume now injects a re-orient prompt, not the user's last message.**
|
|
8
|
+
|
|
9
|
+
When a session compacts mid-conversation (via `/compact` or Claude Code's
|
|
10
|
+
auto-compact), its working context is gone. The prior behavior — re-sending the
|
|
11
|
+
user's last message verbatim — assumed the agent still had thread context, which
|
|
12
|
+
by definition it doesn't after compaction. That produced responses that read as
|
|
13
|
+
a second-pass attempt at the same message rather than a real continuation.
|
|
14
|
+
|
|
15
|
+
The compaction-resume helper (`recoverCompactedSession` in `server.ts`) now
|
|
16
|
+
injects a single prompt:
|
|
17
|
+
|
|
18
|
+
please read the previous messages in this topic and continue
|
|
19
|
+
|
|
20
|
+
This lets the agent fetch recent topic/channel history through its own tooling
|
|
21
|
+
and resume coherently. Applies to all three independent triggers:
|
|
22
|
+
|
|
23
|
+
1. `PreCompact` hook event
|
|
24
|
+
2. `SessionWatchdog` `compaction-idle` poll
|
|
25
|
+
3. `POST /internal/compaction-resume` from `compaction-recovery.sh`
|
|
26
|
+
|
|
27
|
+
The Slack path previously still detoured through `TriageOrchestrator`, whose
|
|
28
|
+
`reinject_message` heuristic ignores the passed-in `pendingMessage` and always
|
|
29
|
+
re-sends the last user message from history — defeating the override. Slack now
|
|
30
|
+
bypasses triage and injects directly, matching the Telegram path.
|
|
31
|
+
|
|
32
|
+
## What to Tell Your User
|
|
33
|
+
|
|
34
|
+
- **Smoother continuity after compaction**: "When my session gets auto-summarized mid-conversation, I'll now pick up by re-reading the recent thread and continuing — instead of replaying your message like nothing happened."
|
|
35
|
+
|
|
36
|
+
## Summary of New Capabilities
|
|
37
|
+
|
|
38
|
+
| Capability | How to Use |
|
|
39
|
+
|-----------|-----------|
|
|
40
|
+
| Compaction-resume re-orient prompt | Automatic — fires on all three compaction-recovery triggers |
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.37
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**New: `CompactionSentinel` — a dedicated monitor that owns the full post-compaction recovery lifecycle.**
|
|
8
|
+
|
|
9
|
+
Prior to this release, three independent trigger paths (PreCompact hook event, `SessionWatchdog` `compaction-idle` poll, and the `POST /internal/compaction-resume` endpoint called by `compaction-recovery.sh`) all called the `recoverCompactedSession` helper fire-and-forget. If the injection didn't wake the session up — which happens when the Claude process is hung, the injection is silently dropped, or the session is genuinely dead — nobody noticed. The 15-minute idle-prompt zombie-killer in `SessionManager` then raced the recovery window and killed the session, wiping the user's conversation.
|
|
10
|
+
|
|
11
|
+
The `CompactionSentinel` adds:
|
|
12
|
+
|
|
13
|
+
- **Dedupe** — multiple triggers for the same session inside a 1-minute window collapse into a single recovery lifecycle, preventing three overlapping inject attempts from racing each other.
|
|
14
|
+
- **Verification** — after each injection, the sentinel samples the most recently-modified Claude Code JSONL file for this project; growth in size or mtime within a 25s window is taken as proof that Claude actually processed the prompt and emitted output.
|
|
15
|
+
- **Retry** — if verification fails within the window, the sentinel re-injects, up to `maxInjectAttempts` (default 3). It only declares failure after verified exhaustion.
|
|
16
|
+
- **Zombie-kill veto** — `SessionManager` now exposes `setActiveRecoveryChecker`, and the idle-prompt cleanup skips sessions that are in active recovery. When it skips, it also resets the idle clock so we don't re-race the cleanup on the next poll either.
|
|
17
|
+
- **Observability** — every step logs with the `[Sentinel]` prefix. Lifecycle events (`compaction:detected`, `inject-attempted`, `recovered`, `failed`) are emitted with full state payloads so downstream monitors can subscribe.
|
|
18
|
+
|
|
19
|
+
The three trigger paths in `server.ts` now report into the sentinel instead of calling `recoverCompactedSession` directly. The helper is still used — the sentinel takes it as an injected dependency, so topic/channel routing stays in `server.ts` and this class stays focused on the recovery state machine.
|
|
20
|
+
|
|
21
|
+
Wiring detail: `CompactionSentinel` is instantiated next to `SessionWatchdog` in `server.ts` startup. `sessionManager.setActiveRecoveryChecker(session => sentinel.isRecoveryActive(session.tmuxSession))` is registered during the same block. Default config (1m dedupe, 25s verify window, 3 attempts, 10m recovery guard) is production-tuned; override via the config object if needed.
|
|
22
|
+
|
|
23
|
+
## What to Tell Your User
|
|
24
|
+
|
|
25
|
+
- **More reliable recovery after my session gets auto-summarized**: "When my conversation hits the context limit and gets compressed, I now verify that I actually woke up and responded — not just that a prompt was sent. If the first attempt didn't land, I retry before giving up. And the idle-session cleanup knows to leave me alone while a recovery is in progress, so conversations no longer get wiped out mid-recovery."
|
|
26
|
+
|
|
27
|
+
## Summary of New Capabilities
|
|
28
|
+
|
|
29
|
+
| Capability | How to Use |
|
|
30
|
+
|-----------|-----------|
|
|
31
|
+
| Compaction recovery verification | automatic (no config change) |
|
|
32
|
+
| Compaction recovery retry | automatic, up to 3 attempts per session |
|
|
33
|
+
| Zombie-kill veto during recovery | automatic via setActiveRecoveryChecker on SessionManager |
|
|
34
|
+
| Lifecycle events for observers | CompactionSentinel emits compaction:detected / inject-attempted / recovered / failed |
|
|
35
|
+
|
|
36
|
+
## Evidence
|
|
37
|
+
|
|
38
|
+
**Reproduction of the prior failure mode is documented in echo's server.log:**
|
|
39
|
+
|
|
40
|
+
At 2026-04-15T03:21:05Z and 03:26:35Z (pre-0.28.37, old behavior on session "echo-watchdog-issues"):
|
|
41
|
+
|
|
42
|
+
[Watchdog] "echo-watchdog-issues": compaction-idle detected — session compacted and at prompt
|
|
43
|
+
|
|
44
|
+
Followed by silence. No recovery attempt logged. No visibility into what happened next. The old code called recoverCompactedSession() and forgot. On 2026-04-15T01:33Z a different session went through the same sequence, showed a single [CompactionResume] direct re-inject OK line, and then was killed 25 minutes later as a zombie. That is the load-bearing failure this release addresses.
|
|
45
|
+
|
|
46
|
+
**Observed behavior after 0.28.37 was installed and the server restarted at 2026-04-15T03:27:34Z:**
|
|
47
|
+
|
|
48
|
+
Same watchdog trigger, same session, new behavior (within 5s of server startup):
|
|
49
|
+
|
|
50
|
+
[Watchdog] "echo-watchdog-issues": compaction-idle detected — session compacted and at prompt
|
|
51
|
+
[Sentinel] detected compaction on "echo-watchdog-issues" via watchdog-poll; baseline jsonl: none size=n/a
|
|
52
|
+
[Sentinel] inject-attempted on "echo-watchdog-issues" (attempt 1/3, accepted=false)
|
|
53
|
+
[Sentinel] failed "echo-watchdog-issues": recoverFn declined (no pending work or session gone)
|
|
54
|
+
|
|
55
|
+
The sentinel correctly determined there was no pending user message to recover and finalized as "failed" with a clear reason — an outcome that was silently invisible before.
|
|
56
|
+
|
|
57
|
+
**Direct endpoint path verified:**
|
|
58
|
+
|
|
59
|
+
A POST to /internal/compaction-resume with {"sessionName":"synthetic-test-session-xyz"} produced:
|
|
60
|
+
|
|
61
|
+
[Sentinel] detected compaction on "synthetic-test-session-xyz" via recovery-hook; baseline jsonl: none size=n/a
|
|
62
|
+
[Sentinel] inject-attempted on "synthetic-test-session-xyz" (attempt 1/3, accepted=false)
|
|
63
|
+
[Sentinel] failed "synthetic-test-session-xyz": recoverFn declined (no pending work or session gone)
|
|
64
|
+
|
|
65
|
+
**Dedupe verified in production:** three identical POSTs to the endpoint within 3 seconds produced exactly one lifecycle trace, not three. The old behavior would have attempted three injections racing each other.
|
|
66
|
+
|
|
67
|
+
**Unit coverage (14 new tests in tests/unit/CompactionSentinel.test.ts, 100% pass):** detection + recoverFn dispatch; dedupe within window; parallel session independence; verification success via JSONL size growth → recovered emitted with jsonlDelta; retry on no-growth up to maxInjectAttempts; success on later attempt stops the retry; max-attempts exhausted → failed emitted with reason; recoverFn returning false → fast failed without retry; recoverFn throwing → treated as non-acceptance; isRecoveryActive predicate correct across all lifecycle states; JSONL edge cases (no file, mtime-only change); clear/stop cleanup.
|
|
68
|
+
|
|
69
|
+
**What has NOT been verified in production:** a real-compaction positive-path trace (session genuinely compacts, has an unanswered user message, sentinel injects, Claude responds, JSONL grows, recovered emits). That requires either spawning a fresh Claude session and driving /compact through it, or waiting for a natural compaction on a session that happens to have an unanswered message — neither was deterministically triggerable during this autonomous run without disrupting the active user session. The verification logic itself is exercised by the 14 unit tests, and the production traces above confirm the sentinel is wired into all three trigger paths correctly. If a natural positive-path trace shows up during the remaining autonomous-mode time, the next release will reference it here.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.38
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Fix: CompactionSentinel targets the exact session's JSONL file by Claude Code session UUID, preventing false positives from sibling sessions.**
|
|
8
|
+
|
|
9
|
+
In 0.28.37 the verification step picked the most-recently-modified jsonl file in the project's `.claude/projects/<hash>/` directory. For projects with multiple concurrent sessions (the common case — a user topic session alongside a job session), a sibling session emitting output during the 25s verify window would look identical to "our session recovered." The sentinel would mistakenly emit `compaction:recovered` and close out, while the actually-stuck session was still asleep.
|
|
10
|
+
|
|
11
|
+
This release threads `getClaudeSessionId(sessionName)` through `CompactionSentinelDeps`. When the Claude Code session UUID is known (populated on the session's first hook event via `Session.claudeSessionId`), the sentinel reads exactly `<uuid>.jsonl` and nothing else. When the UUID isn't known yet — typically for very new sessions or sessions that haven't fired a hook — the sentinel falls back to the previous most-recently-modified behavior.
|
|
12
|
+
|
|
13
|
+
Wiring: `server.ts` passes a resolver that looks up `sessionManager.listRunningSessions()` and returns `session.claudeSessionId` for matching tmux sessions.
|
|
14
|
+
|
|
15
|
+
## What to Tell Your User
|
|
16
|
+
|
|
17
|
+
- **Recovery verification is now session-accurate**: "When I'm monitoring whether a compaction recovery actually worked, I check the right file — not just the most-recently-touched one. That matters on machines where several sessions are active at once."
|
|
18
|
+
|
|
19
|
+
## Summary of New Capabilities
|
|
20
|
+
|
|
21
|
+
| Capability | How to Use |
|
|
22
|
+
|-----------|-----------|
|
|
23
|
+
| Session-targeted jsonl verification | automatic when session has claudeSessionId; falls back to previous behavior otherwise |
|
|
24
|
+
|
|
25
|
+
## Evidence
|
|
26
|
+
|
|
27
|
+
**Reproduction of the failure mode via unit test (tests/unit/CompactionSentinel.test.ts):**
|
|
28
|
+
|
|
29
|
+
The new test "uses claudeSessionId to target the exact jsonl when available" sets up two sibling jsonl files in the temp jsonl root (`abc-123.jsonl` for session s1 and `sibling-session.jsonl` for another session). It then:
|
|
30
|
+
|
|
31
|
+
1. Reports compaction on s1 — sentinel records baseline from `abc-123.jsonl`.
|
|
32
|
+
2. Grows `sibling-session.jsonl` to 5000 bytes (simulating the sibling session emitting output).
|
|
33
|
+
3. Advances past the verify window.
|
|
34
|
+
4. Asserts `compaction:recovered` is NOT emitted (correctly — the sibling grew, not s1's file).
|
|
35
|
+
5. Grows `abc-123.jsonl` to 400 bytes.
|
|
36
|
+
6. Advances past another verify window.
|
|
37
|
+
7. Asserts `compaction:recovered` IS emitted.
|
|
38
|
+
|
|
39
|
+
Before the fix, step 4 would have failed (sentinel emits recovered on sibling growth). After the fix, it passes. All 15 CompactionSentinel tests pass (14 from 0.28.37 + this new isolation test).
|
|
40
|
+
|
|
41
|
+
**Not reproduced in production** — the fix is preventive. Catching it required reading the code after 0.28.37 was already live, and I chose to ship the fix immediately via unit-test-verified behavior rather than wait for a natural multi-session scenario to expose it. Natural in-production evidence would require two sessions within the same project compacting simultaneously with overlapping recovery windows, which is non-deterministic.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.39
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Fix: CompactionSentinel allows re-recovery after a previous recovery finalizes, even within the dedupe window.**
|
|
8
|
+
|
|
9
|
+
The `recentReports` map, used to dedupe overlapping trigger events from the PreCompact hook, watchdog poll, and recovery-hook endpoint, was only cleared by `stop()` and `clear()`. When a recovery finalized (either `recovered` or `failed`) and the session compacted again within the 60-second dedupe window, the second compaction was silently suppressed — same session, fresh compaction event, no recovery attempt.
|
|
10
|
+
|
|
11
|
+
Fix: on finalize, schedule the `recentReports[sessionName]` entry to be deleted alongside the `active[sessionName]` entry (after the keep-window). A second compaction on the same session is now always eligible for a fresh recovery once the first one has finalized and its keep-window elapsed.
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
- **Back-to-back session recoveries work now**: "If my session goes through compaction twice in quick succession, I recover from the second one the same way I recovered from the first — no silent skipping."
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| Re-recovery after finalize | automatic — no config change |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
**Reproduction of the failure mode via unit test (tests/unit/CompactionSentinel.test.ts "allows re-recovery after a previous recovery finalizes"):**
|
|
26
|
+
|
|
27
|
+
The test sets up a recovery that succeeds, waits out the 5-second keep-window, then triggers a second compaction on the same session. It asserts that exactly 2 `compaction:detected` events fire (one per compaction). Before the fix: only 1 event fired — the second report was silently suppressed by the `recentReports` timestamp from the first. After the fix: both fire. 16/16 CompactionSentinel tests pass.
|
|
28
|
+
|
|
29
|
+
**Not reproduced in production** — this is a code-audit finding. Triggering it naturally requires two compactions on the same session within a 65-second window (60s dedupe + up to 5s keep-window for a recovered state), which doesn't happen on the current traffic here. The fix is verified against the state machine via the new test; production evidence would require forcing that timing, which is not deterministically reproducible.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.40
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Injected recovery prompt now instructs the agent to re-orient on topic history first, inform the user that compaction occurred, and then continue.**
|
|
8
|
+
|
|
9
|
+
The prompt the CompactionSentinel injects after detection was previously:
|
|
10
|
+
|
|
11
|
+
please read the previous messages in this topic and continue
|
|
12
|
+
|
|
13
|
+
That got the agent reading history but left two gaps: (a) the user never learned that a compaction happened — the agent would appear to mysteriously shift tone/awareness with no explanation, and (b) the single-sentence instruction didn't make the re-orient-first ordering explicit, so the agent sometimes replied from a blank slate before reading history.
|
|
14
|
+
|
|
15
|
+
New prompt:
|
|
16
|
+
|
|
17
|
+
Your session just went through context compaction. Read the recent messages in this topic to re-orient, briefly let the user know compaction occurred, then continue where you left off.
|
|
18
|
+
|
|
19
|
+
Three explicit steps in order: orient (read history), acknowledge (tell the user), continue (pick up the thread). The user-facing acknowledgment is explicit — if an invisible context wipe happens, the user should know.
|
|
20
|
+
|
|
21
|
+
Applies to all three recovery trigger paths (PreCompact hook event, watchdog compaction-idle poll, recovery-hook endpoint) via the shared `COMPACTION_RESUME_PROMPT` constant in `server.ts`.
|
|
22
|
+
|
|
23
|
+
## What to Tell Your User
|
|
24
|
+
|
|
25
|
+
- **You'll know when my context gets compressed now**: "If my conversation hits the context limit and gets auto-summarized mid-thread, I'll briefly tell you that happened when I pick back up — instead of just silently continuing like nothing changed."
|
|
26
|
+
|
|
27
|
+
## Summary of New Capabilities
|
|
28
|
+
|
|
29
|
+
| Capability | How to Use |
|
|
30
|
+
|-----------|-----------|
|
|
31
|
+
| User notification on compaction recovery | automatic — the agent announces when compaction occurred on its next response |
|
|
32
|
+
| Explicit re-orient-first ordering | automatic — the injected prompt tells the agent to read history before replying |
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.41
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Two fixes for volatile state that didn't survive restarts in the Threadline REST adapter and relay offline queue:
|
|
8
|
+
|
|
9
|
+
1. **REST thread history now persists to disk.** `ThreadlineRESTServer` previously held thread history in memory only, so restarts lost all conversation history even when the client-side MessageStore had messages on disk. The server now hydrates from `~/.threadline/thread-history.json` on startup and persists debounced (1s) on incoming messages and thread deletions. Writes are atomic (temp file + rename) and size-bounded by the existing `maxMessageHistoryPerThread` cap. New config: `historyPath` (default `~/.threadline/thread-history.json`) and `persistHistory` (default `true`, set `false` for tests/ephemeral servers).
|
|
10
|
+
|
|
11
|
+
2. **Offline queue default TTL extended from 1h to 24h.** `InMemoryOfflineQueue`'s 1-hour default was shorter than typical offline/restart windows for agents, so messages to offline recipients expired before reconnection. Configurable via `OfflineQueueConfig.defaultTtlMs` if you need different behavior.
|
|
12
|
+
|
|
13
|
+
No API breakage. Existing servers/queues using defaults just get more durable behavior.
|
|
14
|
+
|
|
15
|
+
## What to Tell Your User
|
|
16
|
+
|
|
17
|
+
- **Conversation history survives restarts**: "Your thread history will stick around now even if I get restarted — no more losing context from earlier in our conversation."
|
|
18
|
+
- **Messages wait longer for offline agents**: "If you message another agent who's offline, the message will wait up to a day for them to come back online instead of expiring after an hour."
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
| Capability | How to Use |
|
|
23
|
+
|-----------|-----------|
|
|
24
|
+
| Persistent REST thread history | Automatic (opt-out via `persistHistory: false`) |
|
|
25
|
+
| 24h offline message retention | Automatic (override via `defaultTtlMs`) |
|
|
26
|
+
|
|
27
|
+
## Evidence
|
|
28
|
+
|
|
29
|
+
Reproduction before fix:
|
|
30
|
+
1. Start `npx @anthropic-ai/threadline serve --port 18800`
|
|
31
|
+
2. Receive messages on a thread → `GET /threads/{id}` returns them
|
|
32
|
+
3. Restart the server
|
|
33
|
+
4. `GET /threads/{id}` returns 404 — history lost
|
|
34
|
+
|
|
35
|
+
After fix:
|
|
36
|
+
1. Same steps 1–2
|
|
37
|
+
2. Within 1s, `~/.threadline/thread-history.json` contains the thread
|
|
38
|
+
3. Restart the server
|
|
39
|
+
4. `GET /threads/{id}` returns the same messages — hydrated from disk
|
|
40
|
+
|
|
41
|
+
Unit tests (40/40 passing in `OfflineQueue.test.ts` and `RESTServerE2E.test.ts`) cover the default config values and existing REST flows; persistence is best-effort (wrapped in try/catch) so disk failures don't crash the server.
|
package/upgrades/NEXT.md
CHANGED
|
@@ -33,3 +33,21 @@
|
|
|
33
33
|
| Capability | How to Use |
|
|
34
34
|
|-----------|-----------|
|
|
35
35
|
| [Capability] | [Endpoint, command, or "automatic"] |
|
|
36
|
+
|
|
37
|
+
## Evidence
|
|
38
|
+
|
|
39
|
+
<!-- REQUIRED if this release claims to fix a bug. -->
|
|
40
|
+
<!-- Unit tests passing is NOT evidence. Provide ONE of: -->
|
|
41
|
+
<!-- (a) Reproduction steps + observed before/after on a live system. -->
|
|
42
|
+
<!-- Include log excerpts, observed command output, or behavior -->
|
|
43
|
+
<!-- description. Make it specific enough that a future reader can -->
|
|
44
|
+
<!-- re-run it and see the same thing. -->
|
|
45
|
+
<!-- (b) "Not reproducible in dev — [concrete reason]" if the failure -->
|
|
46
|
+
<!-- mode truly can't be exercised locally (race conditions, -->
|
|
47
|
+
<!-- event-driven paths requiring external signals, etc). -->
|
|
48
|
+
<!-- -->
|
|
49
|
+
<!-- If this release doesn't claim a bug fix (pure feature / refactor), -->
|
|
50
|
+
<!-- leave this section blank or delete it — it's only enforced when -->
|
|
51
|
+
<!-- "What Changed" describes a fix. -->
|
|
52
|
+
|
|
53
|
+
[Describe reproduction + verified fix, OR "Not reproducible in dev — [concrete reason]"]
|