instar 0.28.74 → 0.28.75
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/BranchManager.d.ts.map +1 -1
- package/dist/core/BranchManager.js +2 -0
- package/dist/core/BranchManager.js.map +1 -1
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +2 -0
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/HandoffManager.d.ts.map +1 -1
- package/dist/core/HandoffManager.js +2 -0
- package/dist/core/HandoffManager.js.map +1 -1
- package/dist/core/SourceTreeGuard.d.ts +69 -0
- package/dist/core/SourceTreeGuard.d.ts.map +1 -0
- package/dist/core/SourceTreeGuard.js +378 -0
- package/dist/core/SourceTreeGuard.js.map +1 -0
- package/dist/core/StateManager.d.ts.map +1 -1
- package/dist/core/StateManager.js +46 -9
- package/dist/core/StateManager.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/0.28.75.md +29 -0
- package/upgrades/side-effects/0.28.75.md +53 -0
- package/upgrades/side-effects/source-tree-guard.md +340 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourceTreeGuard — refuses destructive operations against the instar source tree.
|
|
3
|
+
*
|
|
4
|
+
* Background: on 2026-04-22 an e2e test fixture ran `git add -A && git commit`
|
|
5
|
+
* against the real instar source checkout (1,893 files wiped, force-push
|
|
6
|
+
* recovery). Root cause: destructive components trusted an incoming
|
|
7
|
+
* `projectDir` with zero verification it was the intended target.
|
|
8
|
+
*
|
|
9
|
+
* This module is the tactical guardrail described in
|
|
10
|
+
* `docs/specs/DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC.md`. It is deliberately a
|
|
11
|
+
* brittle blocker — the `docs/signal-vs-authority.md` "safety guards on
|
|
12
|
+
* irreversible actions" carve-out applies (false-pass cost is catastrophic,
|
|
13
|
+
* false-block cost is trivial).
|
|
14
|
+
*
|
|
15
|
+
* Detection is OR of three layers:
|
|
16
|
+
* (a) marker file `.instar-source-tree` at the resolved root.
|
|
17
|
+
* (b) canonical `origin` remote URL in the resolved common git dir's config.
|
|
18
|
+
* (c) source identity signature: package.json name === "instar" AND at
|
|
19
|
+
* least TWO of a set of instar-specific files exist.
|
|
20
|
+
*
|
|
21
|
+
* Fail-closed is TWO-TIER:
|
|
22
|
+
* - Detector-level errors (cannot canonicalize, cannot ascend to any
|
|
23
|
+
* existing ancestor, EACCES on all candidate ancestors) return TRUE.
|
|
24
|
+
* - Layer-level errors (one layer cannot evaluate) return FALSE for that
|
|
25
|
+
* sub-check; the OR across the other two layers decides.
|
|
26
|
+
*
|
|
27
|
+
* See the spec for the full rationale.
|
|
28
|
+
*/
|
|
29
|
+
import fs from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
// ── Canonical remote URLs ────────────────────────────────────────────
|
|
32
|
+
//
|
|
33
|
+
// Exact-match list (post-normalization). Forks running on their own package
|
|
34
|
+
// name and without the marker file will legitimately NOT be caught by layer
|
|
35
|
+
// (b) — that is intentional. Layer (c) still catches unrenamed forks, and
|
|
36
|
+
// the marker catches any fork that deliberately opts in.
|
|
37
|
+
//
|
|
38
|
+
// If instar ever moves org/repo, add an entry here — never substitute.
|
|
39
|
+
// Old URLs stay to catch legacy checkouts.
|
|
40
|
+
export const CANONICAL_INSTAR_REMOTES = Object.freeze([
|
|
41
|
+
'git@github.com:dawn/instar.git',
|
|
42
|
+
'https://github.com/dawn/instar.git',
|
|
43
|
+
'ssh://git@github.com/dawn/instar.git',
|
|
44
|
+
]);
|
|
45
|
+
// ── Source-identity signature files ──────────────────────────────────
|
|
46
|
+
//
|
|
47
|
+
// At least TWO of these (in addition to package.json name === "instar")
|
|
48
|
+
// must be present for layer (c) to match.
|
|
49
|
+
const SIGNATURE_FILES = Object.freeze([
|
|
50
|
+
'src/core/GitSync.ts',
|
|
51
|
+
'src/core/BranchManager.ts',
|
|
52
|
+
'src/core/HandoffManager.ts',
|
|
53
|
+
'tsconfig.json',
|
|
54
|
+
'skills/spec-converge/SKILL.md',
|
|
55
|
+
]);
|
|
56
|
+
const MARKER_FILENAME = '.instar-source-tree';
|
|
57
|
+
const MAX_WALK_LEVELS = 40;
|
|
58
|
+
// ── Error shape ──────────────────────────────────────────────────────
|
|
59
|
+
export class SourceTreeGuardError extends Error {
|
|
60
|
+
code = 'INSTAR_SOURCE_TREE_GUARD';
|
|
61
|
+
operation;
|
|
62
|
+
dir;
|
|
63
|
+
resolvedRoot;
|
|
64
|
+
constructor(dir, resolvedRoot, operation) {
|
|
65
|
+
super(`Refusing to run ${operation} against the instar source tree ` +
|
|
66
|
+
`(requested dir: ${dir}, resolved git root: ${resolvedRoot}). ` +
|
|
67
|
+
`This is a safety guard against the 2026-04-22 class of incident. ` +
|
|
68
|
+
`See docs/specs/DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC.md for the documented ` +
|
|
69
|
+
`escape hatch if this block is a genuine false-positive.`);
|
|
70
|
+
this.name = 'SourceTreeGuardError';
|
|
71
|
+
this.operation = operation;
|
|
72
|
+
this.dir = dir;
|
|
73
|
+
this.resolvedRoot = resolvedRoot;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ── Path canonicalization ────────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* Canonicalize `dir`. If it doesn't exist, walk upward via `path.dirname`
|
|
79
|
+
* until an existing ancestor is found, then canonicalize THAT.
|
|
80
|
+
*
|
|
81
|
+
* Returns null if no existing ancestor can be found or canonicalized
|
|
82
|
+
* (detector-level fail-closed — caller returns true from isInstarSourceTree).
|
|
83
|
+
*
|
|
84
|
+
* ENOENT and ENOTDIR during the walk are normal (keep ascending). EACCES
|
|
85
|
+
* is also tolerated on stat — we keep ascending. Only if we hit the
|
|
86
|
+
* filesystem root without finding any accessible existing ancestor do we
|
|
87
|
+
* return null.
|
|
88
|
+
*/
|
|
89
|
+
function canonicalizeNearestExistingAncestor(dir) {
|
|
90
|
+
// Absolute path so dirname walk terminates at filesystem root.
|
|
91
|
+
let candidate;
|
|
92
|
+
try {
|
|
93
|
+
candidate = path.resolve(dir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
for (let i = 0; i < MAX_WALK_LEVELS; i++) {
|
|
99
|
+
try {
|
|
100
|
+
const st = fs.statSync(candidate);
|
|
101
|
+
if (st) {
|
|
102
|
+
// Exists — now canonicalize with realpath (resolves symlinks).
|
|
103
|
+
try {
|
|
104
|
+
return fs.realpathSync(candidate);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// realpath failed on an existing path — detector-level fail-closed.
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const code = err?.code;
|
|
114
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
115
|
+
// Normal walk condition — keep ascending.
|
|
116
|
+
}
|
|
117
|
+
else if (code === 'EACCES' || code === 'EPERM') {
|
|
118
|
+
// Cannot stat — keep ascending; we'll decide at the loop end.
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Unknown stat error — treat as unable to decide; keep ascending.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const parent = path.dirname(candidate);
|
|
125
|
+
if (parent === candidate) {
|
|
126
|
+
// Reached filesystem root without finding an existing ancestor.
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
candidate = parent;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function findGitRoot(startDir) {
|
|
134
|
+
let current = startDir;
|
|
135
|
+
for (let i = 0; i < MAX_WALK_LEVELS; i++) {
|
|
136
|
+
const dotGit = path.join(current, '.git');
|
|
137
|
+
let stat = null;
|
|
138
|
+
try {
|
|
139
|
+
stat = fs.lstatSync(dotGit);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// No .git here — keep ascending.
|
|
143
|
+
}
|
|
144
|
+
if (stat) {
|
|
145
|
+
return {
|
|
146
|
+
worktreeRoot: current,
|
|
147
|
+
dotGitPath: dotGit,
|
|
148
|
+
dotGitIsFile: stat.isFile(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const parent = path.dirname(current);
|
|
152
|
+
if (parent === current)
|
|
153
|
+
break;
|
|
154
|
+
current = parent;
|
|
155
|
+
}
|
|
156
|
+
return { worktreeRoot: startDir, dotGitPath: null, dotGitIsFile: false };
|
|
157
|
+
}
|
|
158
|
+
// ── Worktree common-git-dir resolution ───────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* Given a `.git` FILE (worktree pointer), resolve the common git dir.
|
|
161
|
+
*
|
|
162
|
+
* The standard layout is `<common-git-dir>/worktrees/<name>` (i.e. the
|
|
163
|
+
* `gitdir:` pointer targets a subdirectory of `worktrees/` inside the
|
|
164
|
+
* main repo's `.git`). In that case the common git dir is
|
|
165
|
+
* `dirname(dirname(gitdir))`.
|
|
166
|
+
*
|
|
167
|
+
* Any other layout (submodule worktree, custom core.worktreesDir, etc.)
|
|
168
|
+
* returns null — layer (b) fails closed for the sub-check, and layers
|
|
169
|
+
* (a)/(c) still evaluate at the worktree root.
|
|
170
|
+
*/
|
|
171
|
+
function resolveCommonGitDirFromWorktreeFile(dotGitFilePath) {
|
|
172
|
+
let contents;
|
|
173
|
+
try {
|
|
174
|
+
contents = fs.readFileSync(dotGitFilePath, 'utf8');
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const match = contents.match(/^\s*gitdir:\s*(.+?)\s*$/m);
|
|
180
|
+
if (!match)
|
|
181
|
+
return null;
|
|
182
|
+
let gitdirRaw = match[1].trim();
|
|
183
|
+
if (!gitdirRaw)
|
|
184
|
+
return null;
|
|
185
|
+
// Relative paths resolve against the directory containing the `.git` file
|
|
186
|
+
// (the worktree root), per git-worktree(1).
|
|
187
|
+
let gitdir;
|
|
188
|
+
if (path.isAbsolute(gitdirRaw)) {
|
|
189
|
+
gitdir = gitdirRaw;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
gitdir = path.resolve(path.dirname(dotGitFilePath), gitdirRaw);
|
|
193
|
+
}
|
|
194
|
+
// Verify it exists and canonicalize.
|
|
195
|
+
try {
|
|
196
|
+
gitdir = fs.realpathSync(gitdir);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
// Standard layout: <common>/worktrees/<name>
|
|
202
|
+
const parent = path.dirname(gitdir);
|
|
203
|
+
if (path.basename(parent) !== 'worktrees') {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return path.dirname(parent);
|
|
207
|
+
}
|
|
208
|
+
// ── Layer (a): marker file ───────────────────────────────────────────
|
|
209
|
+
function layerMarker(resolvedRoot) {
|
|
210
|
+
try {
|
|
211
|
+
return fs.existsSync(path.join(resolvedRoot, MARKER_FILENAME));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// ── Layer (b): canonical remote URL ──────────────────────────────────
|
|
218
|
+
/**
|
|
219
|
+
* Minimal canonicalization per spec:
|
|
220
|
+
* 1. Strip leading/trailing whitespace and newlines.
|
|
221
|
+
* 2. Strip a single trailing `/`.
|
|
222
|
+
* 3. Strip a single trailing `.git`.
|
|
223
|
+
*/
|
|
224
|
+
function normalizeRemoteUrl(raw) {
|
|
225
|
+
let s = raw.trim();
|
|
226
|
+
if (s.endsWith('/'))
|
|
227
|
+
s = s.slice(0, -1);
|
|
228
|
+
if (s.endsWith('.git'))
|
|
229
|
+
s = s.slice(0, -'.git'.length);
|
|
230
|
+
return s;
|
|
231
|
+
}
|
|
232
|
+
const NORMALIZED_CANONICAL_REMOTES = Object.freeze(CANONICAL_INSTAR_REMOTES.map(normalizeRemoteUrl));
|
|
233
|
+
function parseOriginUrlFromGitConfig(configText) {
|
|
234
|
+
// Find the [remote "origin"] section and its url = ... line.
|
|
235
|
+
// Section is terminated by the next [section] header or EOF.
|
|
236
|
+
const sectionRe = /\[\s*remote\s+"origin"\s*\]\s*([\s\S]*?)(?=\n\s*\[|\s*$)/i;
|
|
237
|
+
const match = configText.match(sectionRe);
|
|
238
|
+
if (!match)
|
|
239
|
+
return null;
|
|
240
|
+
const body = match[1];
|
|
241
|
+
const urlMatch = body.match(/^\s*url\s*=\s*(.+?)\s*$/m);
|
|
242
|
+
if (!urlMatch)
|
|
243
|
+
return null;
|
|
244
|
+
return urlMatch[1];
|
|
245
|
+
}
|
|
246
|
+
function layerRemoteUrl(worktreeRoot, dotGitPath, dotGitIsFile) {
|
|
247
|
+
if (!dotGitPath)
|
|
248
|
+
return false;
|
|
249
|
+
let configPath;
|
|
250
|
+
if (dotGitIsFile) {
|
|
251
|
+
const commonGitDir = resolveCommonGitDirFromWorktreeFile(dotGitPath);
|
|
252
|
+
if (!commonGitDir)
|
|
253
|
+
return false; // Layer-level inconclusive → FALSE.
|
|
254
|
+
configPath = path.join(commonGitDir, 'config');
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
configPath = path.join(dotGitPath, 'config');
|
|
258
|
+
}
|
|
259
|
+
let configText;
|
|
260
|
+
try {
|
|
261
|
+
configText = fs.readFileSync(configPath, 'utf8');
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const rawUrl = parseOriginUrlFromGitConfig(configText);
|
|
267
|
+
if (rawUrl == null)
|
|
268
|
+
return false;
|
|
269
|
+
const normalized = normalizeRemoteUrl(rawUrl);
|
|
270
|
+
return NORMALIZED_CANONICAL_REMOTES.includes(normalized);
|
|
271
|
+
}
|
|
272
|
+
// ── Layer (c): source identity signature ─────────────────────────────
|
|
273
|
+
function layerSignature(resolvedRoot) {
|
|
274
|
+
const pkgPath = path.join(resolvedRoot, 'package.json');
|
|
275
|
+
let pkgText;
|
|
276
|
+
try {
|
|
277
|
+
pkgText = fs.readFileSync(pkgPath, 'utf8');
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
let pkg;
|
|
283
|
+
try {
|
|
284
|
+
pkg = JSON.parse(pkgText);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
if (pkg.name !== 'instar')
|
|
290
|
+
return false;
|
|
291
|
+
let hits = 0;
|
|
292
|
+
for (const rel of SIGNATURE_FILES) {
|
|
293
|
+
try {
|
|
294
|
+
if (fs.existsSync(path.join(resolvedRoot, rel))) {
|
|
295
|
+
hits++;
|
|
296
|
+
if (hits >= 2)
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Ignore individual file errors; a single stat failure is not enough
|
|
302
|
+
// to fail the whole sub-check.
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return hits >= 2;
|
|
306
|
+
}
|
|
307
|
+
// ── Public API ───────────────────────────────────────────────────────
|
|
308
|
+
/**
|
|
309
|
+
* Returns true if `dir` — or any of its git-root ancestors — is the
|
|
310
|
+
* instar source tree.
|
|
311
|
+
*
|
|
312
|
+
* See module header and the spec for full semantics.
|
|
313
|
+
*/
|
|
314
|
+
export function isInstarSourceTree(dir) {
|
|
315
|
+
// Step 1: resolve nearest-existing-ancestor and canonicalize.
|
|
316
|
+
const canonicalStart = canonicalizeNearestExistingAncestor(dir);
|
|
317
|
+
if (canonicalStart === null) {
|
|
318
|
+
// Detector-level fail-closed.
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
// Step 2: walk to the git root (or keep canonicalStart if none found).
|
|
322
|
+
const { worktreeRoot, dotGitPath, dotGitIsFile } = findGitRoot(canonicalStart);
|
|
323
|
+
// Step 3: OR across the three layers.
|
|
324
|
+
if (layerMarker(worktreeRoot))
|
|
325
|
+
return true;
|
|
326
|
+
if (layerRemoteUrl(worktreeRoot, dotGitPath, dotGitIsFile))
|
|
327
|
+
return true;
|
|
328
|
+
if (layerSignature(worktreeRoot))
|
|
329
|
+
return true;
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Throws `SourceTreeGuardError` if `dir` is the instar source tree.
|
|
334
|
+
*
|
|
335
|
+
* Callers MUST pass the raw projectDir from the original caller — NOT a
|
|
336
|
+
* post-sanitization/post-normalization value. Sanitization would hide the
|
|
337
|
+
* class of bug this guard exists to catch.
|
|
338
|
+
*/
|
|
339
|
+
export function assertNotInstarSourceTree(dir, operation) {
|
|
340
|
+
if (isInstarSourceTree(dir)) {
|
|
341
|
+
// Compute resolved root for the error (best-effort — if resolution
|
|
342
|
+
// fails we report the raw input).
|
|
343
|
+
const resolved = canonicalizeNearestExistingAncestor(dir);
|
|
344
|
+
let resolvedRoot = dir;
|
|
345
|
+
if (resolved !== null) {
|
|
346
|
+
const { worktreeRoot } = findGitRoot(resolved);
|
|
347
|
+
resolvedRoot = worktreeRoot;
|
|
348
|
+
}
|
|
349
|
+
throw new SourceTreeGuardError(dir, resolvedRoot, operation);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Convenience alias per spec §"The guard primitive".
|
|
354
|
+
*/
|
|
355
|
+
export function checkSourceTree(dir) {
|
|
356
|
+
return isInstarSourceTree(dir);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Test helper: resolve the canonical start + git root without running the
|
|
360
|
+
* layer checks. Not part of the public API — exported only for test
|
|
361
|
+
* introspection.
|
|
362
|
+
*
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
export function _resolveRootForTesting(dir) {
|
|
366
|
+
const canonicalStart = canonicalizeNearestExistingAncestor(dir);
|
|
367
|
+
if (canonicalStart === null) {
|
|
368
|
+
return {
|
|
369
|
+
canonicalStart: null,
|
|
370
|
+
worktreeRoot: null,
|
|
371
|
+
dotGitPath: null,
|
|
372
|
+
dotGitIsFile: false,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const { worktreeRoot, dotGitPath, dotGitIsFile } = findGitRoot(canonicalStart);
|
|
376
|
+
return { canonicalStart, worktreeRoot, dotGitPath, dotGitIsFile };
|
|
377
|
+
}
|
|
378
|
+
//# sourceMappingURL=SourceTreeGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceTreeGuard.js","sourceRoot":"","sources":["../../src/core/SourceTreeGuard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateManager.d.ts","sourceRoot":"","sources":["../../src/core/StateManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"StateManager.d.ts","sourceRoot":"","sources":["../../src/core/StateManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwCnE,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAuB;gBAE7B,QAAQ,EAAE,MAAM;IAI5B;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,uDAAuD;IACvD,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,CAE7B;IAED,wEAAwE;IACxE,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAIpC,8CAA8C;IAC9C,OAAO,CAAC,UAAU;IAMlB,iFAAiF;IACjF,OAAO,CAAC,WAAW;IAQnB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAoB7C,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAOnC,YAAY,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,GAAG,OAAO,EAAE;IA6BhE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAezC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAoB1C,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IASnC,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAmBvC,WAAW,CAAC,OAAO,EAAE;QACnB,KAAK,CAAC,EAAE,IAAI,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,aAAa,EAAE;IA0CnB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAoB7B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IASnC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAa5B;;;OAGG;IACH,OAAO,CAAC,WAAW;CAapB"}
|
|
@@ -8,6 +8,38 @@
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { DegradationReporter } from '../monitoring/DegradationReporter.js';
|
|
11
|
+
/**
|
|
12
|
+
* Discriminate filesystem read errors into operator-actionable categories.
|
|
13
|
+
*
|
|
14
|
+
* Previously all read failures (EPERM, EACCES, ENOENT, JSON.parse errors)
|
|
15
|
+
* were labeled "Corrupted ...". On macOS this misled operators: launchd-spawned
|
|
16
|
+
* processes hitting ~/Documents without Full Disk Access produce EPERM, which
|
|
17
|
+
* is a permissions issue, not file corruption. Surfacing the distinction lets
|
|
18
|
+
* agents (and the feedback pipeline) route the report correctly instead of
|
|
19
|
+
* chasing nonexistent corruption.
|
|
20
|
+
*/
|
|
21
|
+
function describeReadError(err, filePath) {
|
|
22
|
+
const code = err?.code;
|
|
23
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
24
|
+
if (code === 'EPERM' || code === 'EACCES') {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'permission',
|
|
27
|
+
reason: `Permission denied reading ${filePath} (${code}). ` +
|
|
28
|
+
`On macOS, launchd-spawned processes need Full Disk Access to read under ~/Documents. ` +
|
|
29
|
+
`Underlying error: ${msg}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (err instanceof SyntaxError) {
|
|
33
|
+
return {
|
|
34
|
+
kind: 'parse',
|
|
35
|
+
reason: `Corrupted state file ${filePath} (JSON parse failed): ${msg}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
kind: 'io',
|
|
40
|
+
reason: `Failed to read ${filePath}${code ? ` (${code})` : ''}: ${msg}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
11
43
|
export class StateManager {
|
|
12
44
|
stateDir;
|
|
13
45
|
_readOnly = false;
|
|
@@ -60,12 +92,13 @@ export class StateManager {
|
|
|
60
92
|
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
61
93
|
}
|
|
62
94
|
catch (err) {
|
|
63
|
-
|
|
95
|
+
const { reason, kind } = describeReadError(err, filePath);
|
|
96
|
+
console.warn(`[StateManager] getSession ${kind}: ${filePath}`);
|
|
64
97
|
DegradationReporter.getInstance().report({
|
|
65
98
|
feature: 'StateManager.getSession',
|
|
66
99
|
primary: 'Load valid session state from JSON',
|
|
67
100
|
fallback: 'Return null — session unavailable',
|
|
68
|
-
reason
|
|
101
|
+
reason,
|
|
69
102
|
impact: 'Session data lost, may affect job scheduling',
|
|
70
103
|
});
|
|
71
104
|
return null;
|
|
@@ -88,12 +121,14 @@ export class StateManager {
|
|
|
88
121
|
sessions.push(JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8')));
|
|
89
122
|
}
|
|
90
123
|
catch (err) {
|
|
91
|
-
|
|
124
|
+
const filePath = path.join(dir, f);
|
|
125
|
+
const { reason, kind } = describeReadError(err, filePath);
|
|
126
|
+
console.warn(`[StateManager] listSessions ${kind}: ${f}`);
|
|
92
127
|
DegradationReporter.getInstance().report({
|
|
93
128
|
feature: 'StateManager.listSessions',
|
|
94
129
|
primary: 'List all sessions from state files',
|
|
95
|
-
fallback: 'Skip corrupted session file',
|
|
96
|
-
reason
|
|
130
|
+
fallback: kind === 'permission' ? 'Skip unreadable session file' : 'Skip corrupted session file',
|
|
131
|
+
reason,
|
|
97
132
|
impact: 'Some sessions invisible to scheduler',
|
|
98
133
|
});
|
|
99
134
|
}
|
|
@@ -127,12 +162,13 @@ export class StateManager {
|
|
|
127
162
|
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
128
163
|
}
|
|
129
164
|
catch (err) {
|
|
130
|
-
|
|
165
|
+
const { reason, kind } = describeReadError(err, filePath);
|
|
166
|
+
console.warn(`[StateManager] getJobState ${kind}: ${filePath}`);
|
|
131
167
|
DegradationReporter.getInstance().report({
|
|
132
168
|
feature: 'StateManager.getJobState',
|
|
133
169
|
primary: 'Load job state from JSON',
|
|
134
170
|
fallback: 'Return null — job state unavailable',
|
|
135
|
-
reason
|
|
171
|
+
reason,
|
|
136
172
|
impact: 'Job scheduling may use stale data',
|
|
137
173
|
});
|
|
138
174
|
return null;
|
|
@@ -208,12 +244,13 @@ export class StateManager {
|
|
|
208
244
|
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
209
245
|
}
|
|
210
246
|
catch (err) {
|
|
211
|
-
|
|
247
|
+
const { reason, kind } = describeReadError(err, filePath);
|
|
248
|
+
console.warn(`[StateManager] get ${kind}: ${filePath}`);
|
|
212
249
|
DegradationReporter.getInstance().report({
|
|
213
250
|
feature: 'StateManager.get',
|
|
214
251
|
primary: 'Load generic state file',
|
|
215
252
|
fallback: 'Return null — state unavailable',
|
|
216
|
-
reason
|
|
253
|
+
reason,
|
|
217
254
|
impact: 'Feature depending on this state may malfunction',
|
|
218
255
|
});
|
|
219
256
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateManager.js","sourceRoot":"","sources":["../../src/core/StateManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,MAAM,OAAO,YAAY;IACf,QAAQ,CAAS;IACjB,SAAS,GAAY,KAAK,CAAC;IAC3B,UAAU,GAAkB,IAAI,CAAC;IAEzC,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,uDAAuD;IACvD,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,wEAAwE;IACxE,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,QAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,8CAA8C;IACtC,UAAU,CAAC,SAAiB;QAClC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,oEAAoE,SAAS,EAAE,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IAED,iFAAiF;IACzE,WAAW,CAAC,GAAW,EAAE,QAAgB,KAAK;QACpD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,GAAG,yDAAyD,CAAC,CAAC;QACtG,CAAC;IACH,CAAC;IAED,mEAAmE;IAEnE,UAAU,CAAC,SAAiB;QAC1B,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;QACpF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"StateManager.js","sourceRoot":"","sources":["../../src/core/StateManager.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -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-25T09:16:20.363Z",
|
|
5
|
+
"instarVersion": "0.28.75",
|
|
6
6
|
"entryCount": 186,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.75
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
`StateManager` now discriminates permission errors (`EPERM`/`EACCES`) from JSON corruption when reading state files. Previously, any failure in `getSession`, `listSessions`, `getJobState`, or `get` was reported as "Corrupted ... file" — including `EPERM` from launchd-spawned processes lacking macOS Full Disk Access on `~/Documents`. A new internal helper `describeReadError(err, filePath)` classifies failures into `permission`, `parse`, or `io` and feeds the discriminated reason into the DegradationReporter and `console.warn` line.
|
|
8
|
+
|
|
9
|
+
The null-return contract is preserved exactly. `feature`, `primary`, `fallback`, and `impact` strings are unchanged. Only the `reason` content is improved. No API surface changes.
|
|
10
|
+
|
|
11
|
+
Closes feedback cluster `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi` (45 reports across `ai-guy` and `sagemind` agents) and prevents future reports from being mis-clustered as corruption.
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
- **Clearer state-file errors**: when I can't read a state file because of a permissions issue (common on macOS when I'm started by a system service that doesn't have Full Disk Access on your Documents folder), I'll now say so plainly instead of telling you the file is corrupted. That should make the fix obvious — granting Full Disk Access in System Settings — instead of sending you on a corruption hunt.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| Discriminated state-file read errors | automatic — appears in degradation reports and warnings |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
Field repro: cluster `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi` shows 45 reports of `EPERM: operation not permitted, open '<path>'` mislabeled as "Corrupted job state file". Reporters: `ai-guy` and `sagemind` running instar 0.28.73 against `.instar/state/jobs/*.json` and `.instar/state/agent-attention-topic.json` files in `~/Documents/Projects/*`.
|
|
26
|
+
|
|
27
|
+
Post-change verification: `tests/unit/StateManager.test.ts` 26/26 passing, including a new `discriminates permission errors from corruption (EPERM/EACCES)` test that chmod 0o000s a file and asserts the warning emitted is keyed `permission`, not `parse` or `Corrupted`.
|
|
28
|
+
|
|
29
|
+
Build: clean.
|