instar 1.3.641 → 1.3.643
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +160 -6
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/InFlightSyncOpMarker.d.ts +29 -0
- package/dist/core/InFlightSyncOpMarker.d.ts.map +1 -0
- package/dist/core/InFlightSyncOpMarker.js +149 -0
- package/dist/core/InFlightSyncOpMarker.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +16 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +91 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +146 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +498 -144
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SleepWakeDetector.d.ts +33 -0
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +53 -0
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +23 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +52 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +42 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +79 -4
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +8 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/monitoring/DegradedTmuxGuard.d.ts +161 -0
- package/dist/monitoring/DegradedTmuxGuard.d.ts.map +1 -0
- package/dist/monitoring/DegradedTmuxGuard.js +277 -0
- package/dist/monitoring/DegradedTmuxGuard.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +18 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +13 -5
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -2
- package/scripts/lint-no-direct-destructive.js +14 -0
- package/scripts/lint-sync-subprocess-chokepoint.js +208 -0
- package/scripts/sync-subprocess-chokepoint-baseline.json +148 -0
- package/src/data/builtin-manifest.json +65 -65
- package/upgrades/1.3.642.md +46 -0
- package/upgrades/1.3.643.md +66 -0
- package/upgrades/side-effects/false-excuse-deferral-stop-guard.md +75 -0
- package/upgrades/side-effects/tmux-event-loop-resilience.md +86 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "instar",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.643",
|
|
4
4
|
"description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:contract": "node scripts/run-contract-tests.js",
|
|
29
29
|
"test:contract:raw": "vitest run --config vitest.contract.config.ts",
|
|
30
30
|
"test:all": "vitest run && vitest run --config vitest.integration.config.ts && vitest run --config vitest.e2e.config.ts",
|
|
31
|
-
"lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-store-retention-declared.js && node scripts/lint-no-wholefile-sync-read.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/lint-scrape-fixture-realness.js && node scripts/check-codex-rule1-drift.js",
|
|
31
|
+
"lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-store-retention-declared.js && node scripts/lint-no-wholefile-sync-read.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-sync-subprocess-chokepoint.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/lint-scrape-fixture-realness.js && node scripts/check-codex-rule1-drift.js",
|
|
32
32
|
"lint:destructive": "node scripts/lint-no-direct-destructive.js",
|
|
33
33
|
"lint:dev-agent-dark-gate": "node scripts/lint-dev-agent-dark-gate.js",
|
|
34
34
|
"lint:guard-manifest": "node scripts/lint-guard-manifest.js",
|
|
@@ -61,6 +61,15 @@ const ALLOWLIST = new Set([
|
|
|
61
61
|
'src/core/SafeFsExecutor.ts',
|
|
62
62
|
'tests/unit/SafeGitExecutor.test.ts',
|
|
63
63
|
'tests/unit/SafeFsExecutor.test.ts',
|
|
64
|
+
// tmux-event-loop-resilience Increment 1 tests: per-test cleanup of
|
|
65
|
+
// mkdtempSync dirs under os.tmpdir() (the cross-process marker round-trip
|
|
66
|
+
// needs a real on-disk mirror dir; the e2e needs a throwaway stateDir). The
|
|
67
|
+
// targets are os.tmpdir paths, never the source tree. Routing through
|
|
68
|
+
// SafeFsExecutor.safeRmSync would write an audit entry per teardown call —
|
|
69
|
+
// worse noise than a scoped allowlist entry for a test's own tmpdir cleanup.
|
|
70
|
+
// (Spec: docs/specs/tmux-event-loop-resilience.md, Step 8.)
|
|
71
|
+
'tests/unit/InFlightSyncOpMarker.test.ts',
|
|
72
|
+
'tests/e2e/tmux-resilience-lifecycle.test.ts',
|
|
64
73
|
// The lint rule itself runs before SafeGitExecutor.ts is compiled, so it
|
|
65
74
|
// needs direct execSync as a bootstrap escape. The single git call here is
|
|
66
75
|
// a read-only `git diff --cached --name-only` for staged-file detection.
|
|
@@ -96,6 +105,11 @@ const ALLOWLIST = new Set([
|
|
|
96
105
|
// instar#1069) — read-only `git diff --cached --name-only` for --staged.
|
|
97
106
|
// Cannot depend on the TS funnel because TS is not compiled at pre-push.
|
|
98
107
|
'scripts/lint-no-mainthread-cartographer-walk.js',
|
|
108
|
+
// Same bootstrap-escape pattern (tmux sync-subprocess chokepoint lint,
|
|
109
|
+
// docs/specs/tmux-event-loop-resilience.md) — a read-only `git ls-files` /
|
|
110
|
+
// `git diff --cached --name-only` for staged-file detection. Cannot depend on
|
|
111
|
+
// the TS funnel because TS is not compiled when the lint runs in pre-push.
|
|
112
|
+
'scripts/lint-sync-subprocess-chokepoint.js',
|
|
99
113
|
// Postinstall bootstrap script — runs before TypeScript is compiled and
|
|
100
114
|
// before SafeFsExecutor is available. CommonJS, can't use ESM imports.
|
|
101
115
|
'scripts/fix-better-sqlite3.cjs',
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-sync-subprocess-chokepoint.js — tmux Event-Loop Resilience, Increment 1.
|
|
4
|
+
*
|
|
5
|
+
* A SYNCHRONOUS subprocess spawn (`spawnSync` / `execSync` / `execFileSync`) blocks
|
|
6
|
+
* the single-threaded Node event loop for the FULL duration of the child process. A
|
|
7
|
+
* slow/wedged tmux (or any slow sync spawn) on the runtime hot path is exactly the
|
|
8
|
+
* ~0-CPU I/O-wait block this increment exists to make survivable: the marker module
|
|
9
|
+
* (src/core/InFlightSyncOpMarker.ts) is the SOLE funnel through which a sync blocking
|
|
10
|
+
* op may run — `withSyncOp(() => execFileSync(...))` records depth so SleepWakeDetector
|
|
11
|
+
* + ServerSupervisor can tell the block apart from a real sleep instead of mis-reaping
|
|
12
|
+
* the session or restarting a live-but-blocked server.
|
|
13
|
+
*
|
|
14
|
+
* Rule: in src/core/, src/monitoring/, and src/server/, no RAW synchronous child-process
|
|
15
|
+
* spawn (`spawnSync` / `execSync` / `execFileSync`) outside the chokepoint module. A
|
|
16
|
+
* raw sync spawn must EITHER funnel through `withSyncOp` (so the (B) marker sees it) OR,
|
|
17
|
+
* if it is a genuinely pre-runtime / CLI-boot call that can never run on a cadence,
|
|
18
|
+
* carry an inline justification comment on the same line or the line directly above:
|
|
19
|
+
* // lint-allow-sync-spawn: <why this never blocks the runtime event loop>
|
|
20
|
+
*
|
|
21
|
+
* This lint bans the RAW spawn outside the funnel — NOT sync-ness itself: the marker
|
|
22
|
+
* module legitimately WRAPS sync calls, and the D1-excluded injection-sequence callsites
|
|
23
|
+
* (send-keys + /bin/sleep, where synchronous timing IS the correctness mechanism) stay
|
|
24
|
+
* sync but funnel through `withSyncOp`. So this lint is satisfied when a sync spawn is
|
|
25
|
+
* funneled, allow-commented, or grandfathered in the FROZEN baseline.
|
|
26
|
+
*
|
|
27
|
+
* COVERAGE HONESTY (No Silent Degradation): a static line-regex, NOT complete — it cannot
|
|
28
|
+
* prove a flagged line is actually WRAPPED by withSyncOp at runtime (that is the (B) unit
|
|
29
|
+
* tests' job). It is the cheap FORWARD ratchet that stops a NEW raw sync spawn from being
|
|
30
|
+
* introduced outside the funnel. The 40+ existing SessionManager callsites are
|
|
31
|
+
* grandfathered by the baseline so this increment's conversion can land incrementally.
|
|
32
|
+
*
|
|
33
|
+
* Ships baseline-grandfathered: a FROZEN baseline records today's literal hits; a NEW hit
|
|
34
|
+
* (not in the baseline, not funneled, not allow-commented) FAILS. The baseline may only
|
|
35
|
+
* shrink. Exit codes: 0 — clean; 1 — at least one new violation.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* node scripts/lint-sync-subprocess-chokepoint.js # full scan dirs
|
|
39
|
+
* node scripts/lint-sync-subprocess-chokepoint.js --staged # staged files
|
|
40
|
+
* node scripts/lint-sync-subprocess-chokepoint.js path/to/file.ts # explicit files
|
|
41
|
+
* node scripts/lint-sync-subprocess-chokepoint.js --write-baseline # (re)generate baseline
|
|
42
|
+
* node scripts/lint-sync-subprocess-chokepoint.js --baseline <p> --root <p> # test overrides
|
|
43
|
+
*/
|
|
44
|
+
import fs from 'node:fs';
|
|
45
|
+
import path from 'node:path';
|
|
46
|
+
import { execSync } from 'node:child_process';
|
|
47
|
+
import { fileURLToPath } from 'node:url';
|
|
48
|
+
|
|
49
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
50
|
+
const ROOT = path.resolve(path.dirname(__filename), '..');
|
|
51
|
+
|
|
52
|
+
function argVal(flag) {
|
|
53
|
+
const i = process.argv.indexOf(flag);
|
|
54
|
+
return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const BASELINE = path.resolve(
|
|
58
|
+
argVal('--baseline') || path.join(ROOT, 'scripts', 'sync-subprocess-chokepoint-baseline.json'),
|
|
59
|
+
);
|
|
60
|
+
// --root overrides the repo root the SCAN_DIRS are resolved against (tests / alternate checkouts).
|
|
61
|
+
const SCAN_BASE = path.resolve(argVal('--root') || ROOT);
|
|
62
|
+
const SCAN_DIRS = ['src/core', 'src/monitoring', 'src/server'];
|
|
63
|
+
const EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs', '.cjs']);
|
|
64
|
+
|
|
65
|
+
// The chokepoint module itself — the SOLE place a raw sync spawn may legitimately live
|
|
66
|
+
// (it WRAPS the call in withSyncOp; everything else must route through it).
|
|
67
|
+
const CHOKEPOINT = 'src/core/InFlightSyncOpMarker.ts';
|
|
68
|
+
|
|
69
|
+
// A BARE synchronous child-process spawn imported from node:child_process. The
|
|
70
|
+
// negative lookbehind `(?<![.\w])` rejects a member-access form (`SafeGitExecutor.execSync(`,
|
|
71
|
+
// `SafeFsExecutor.execFileSync(`) — those are the SAFE funnels, not a raw event-loop block —
|
|
72
|
+
// and a longer identifier (`myExecSync`). `\s*\(` so only an actual call matches, never an
|
|
73
|
+
// import name. The *Async variants (execFileAsync / execFile) never match (different verb).
|
|
74
|
+
const VIOLATION = /(?<![.\w])(spawnSync|execSync|execFileSync)\s*\(/;
|
|
75
|
+
const ALLOW = /lint-allow-sync-spawn:/;
|
|
76
|
+
// The FUNNEL: a sync spawn lexically wrapped by withSyncOp(...) on the same line is the
|
|
77
|
+
// REQUIRED pattern (the (B) marker sees it) — never a violation, regardless of baseline.
|
|
78
|
+
const FUNNELED = /\bwithSyncOp\s*\(/;
|
|
79
|
+
|
|
80
|
+
const inScanDir = (p) => SCAN_DIRS.some((d) => p === d || p.startsWith(d + '/'));
|
|
81
|
+
const normalize = (p) => p.split(path.sep).join('/');
|
|
82
|
+
|
|
83
|
+
function listFiles() {
|
|
84
|
+
if (process.argv.includes('--staged')) {
|
|
85
|
+
const out = execSync('git diff --cached --name-only', { cwd: ROOT, encoding: 'utf-8' });
|
|
86
|
+
return out
|
|
87
|
+
.split('\n')
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.map(normalize)
|
|
90
|
+
.filter((p) => inScanDir(p));
|
|
91
|
+
}
|
|
92
|
+
// Explicit file args are checked as-given (targeted use / self-tests).
|
|
93
|
+
const explicit = process.argv
|
|
94
|
+
.slice(2)
|
|
95
|
+
.filter((a) => !a.startsWith('--'))
|
|
96
|
+
// Drop the values consumed by --baseline / --root.
|
|
97
|
+
.filter((a) => a !== argVal('--baseline') && a !== argVal('--root'));
|
|
98
|
+
if (explicit.length) return explicit;
|
|
99
|
+
|
|
100
|
+
const files = [];
|
|
101
|
+
const walk = (dir) => {
|
|
102
|
+
let entries;
|
|
103
|
+
try {
|
|
104
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
105
|
+
} catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const e of entries) {
|
|
109
|
+
if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist') continue;
|
|
110
|
+
const full = path.join(dir, e.name);
|
|
111
|
+
if (e.isDirectory()) walk(full);
|
|
112
|
+
else if (EXTENSIONS.has(path.extname(e.name))) files.push(full);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
for (const d of SCAN_DIRS) walk(path.join(SCAN_BASE, d));
|
|
116
|
+
return files;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Collect every raw sync-spawn hit not allow-commented and not in the chokepoint module.
|
|
120
|
+
// Keyed on file + the trimmed call line + the occurrence index of that exact line text
|
|
121
|
+
// within the file (line-number-independent so the baseline survives edits above it).
|
|
122
|
+
function collectHits() {
|
|
123
|
+
const hits = [];
|
|
124
|
+
for (const fileArg of listFiles()) {
|
|
125
|
+
const abs = path.isAbsolute(fileArg) ? fileArg : path.join(ROOT, fileArg);
|
|
126
|
+
const rel = normalize(path.relative(ROOT, abs));
|
|
127
|
+
if (!EXTENSIONS.has(path.extname(rel))) continue;
|
|
128
|
+
if (rel === CHOKEPOINT) continue; // the funnel may wrap sync calls
|
|
129
|
+
let content;
|
|
130
|
+
try {
|
|
131
|
+
content = fs.readFileSync(abs, 'utf-8');
|
|
132
|
+
} catch {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const lines = content.split('\n');
|
|
136
|
+
const seenLineText = new Map(); // trimmed-line-text → occurrence count so far
|
|
137
|
+
for (let i = 0; i < lines.length; i++) {
|
|
138
|
+
const raw = lines[i];
|
|
139
|
+
const trimmed = raw.trimStart();
|
|
140
|
+
if (/^(\/\/|\*|\/\*)/.test(trimmed)) continue; // comment-only mention
|
|
141
|
+
if (!VIOLATION.test(raw)) continue;
|
|
142
|
+
// FUNNELED: a sync spawn wrapped by withSyncOp(...) on the same line is the required
|
|
143
|
+
// pattern (the marker sees it) — allowed unconditionally, never grandfathered/baselined.
|
|
144
|
+
if (FUNNELED.test(raw)) continue;
|
|
145
|
+
// Inline justification on this line or within the 6 lines directly above.
|
|
146
|
+
let allowed = false;
|
|
147
|
+
for (let j = i; j >= Math.max(0, i - 6); j--) {
|
|
148
|
+
if (ALLOW.test(lines[j])) {
|
|
149
|
+
allowed = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (allowed) continue;
|
|
154
|
+
const text = raw.trim();
|
|
155
|
+
const occ = seenLineText.get(text) ?? 0;
|
|
156
|
+
seenLineText.set(text, occ + 1);
|
|
157
|
+
hits.push({ file: rel, line: i + 1, text, key: `${rel}::${text}::${occ}` });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return hits;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const hits = collectHits();
|
|
164
|
+
|
|
165
|
+
// Generate-baseline mode: record current hits as grandfathered.
|
|
166
|
+
if (process.argv.includes('--write-baseline')) {
|
|
167
|
+
fs.writeFileSync(
|
|
168
|
+
BASELINE,
|
|
169
|
+
JSON.stringify(
|
|
170
|
+
{
|
|
171
|
+
note:
|
|
172
|
+
'FROZEN baseline of RAW synchronous subprocess spawns (spawnSync/execSync/execFileSync) outside the InFlightSyncOpMarker funnel, at tmux Event-Loop Resilience Increment 1. These are grandfathered while the (A)/(B) conversion lands incrementally. May only SHRINK — a NEW raw sync spawn outside the funnel fails the lint. Funnel a call through withSyncOp(), or add an inline "// lint-allow-sync-spawn: <reason>" for a genuine pre-runtime/CLI-boot call.',
|
|
173
|
+
frozenAt: '2026-06-22',
|
|
174
|
+
keys: hits.map((h) => h.key).sort(),
|
|
175
|
+
},
|
|
176
|
+
null,
|
|
177
|
+
2,
|
|
178
|
+
) + '\n',
|
|
179
|
+
);
|
|
180
|
+
console.log(`lint-sync-subprocess-chokepoint: wrote baseline with ${hits.length} grandfathered hit(s).`);
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let baseline = { keys: [] };
|
|
185
|
+
try {
|
|
186
|
+
baseline = JSON.parse(fs.readFileSync(BASELINE, 'utf8'));
|
|
187
|
+
} catch {
|
|
188
|
+
// No baseline yet → treat as empty (every current hit is a NEW violation).
|
|
189
|
+
}
|
|
190
|
+
const baselineSet = new Set(baseline.keys || []);
|
|
191
|
+
|
|
192
|
+
const newViolations = hits.filter((h) => !baselineSet.has(h.key));
|
|
193
|
+
if (newViolations.length) {
|
|
194
|
+
console.error('lint-sync-subprocess-chokepoint: FAIL — new RAW synchronous subprocess spawn(s) outside the InFlightSyncOpMarker funnel:');
|
|
195
|
+
for (const v of newViolations) {
|
|
196
|
+
console.error(
|
|
197
|
+
` • ${v.file}:${v.line} — raw sync spawn (${v.text}). A sync blocking op must funnel through ` +
|
|
198
|
+
`withSyncOp() so the in-flight marker sees it (or add "// lint-allow-sync-spawn: <reason>" for a genuine ` +
|
|
199
|
+
`pre-runtime/CLI-boot call). See docs/specs/tmux-event-loop-resilience-spec.md.`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
console.error(`\nlint-sync-subprocess-chokepoint: ${newViolations.length} new violation(s).`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
console.log(
|
|
206
|
+
`lint-sync-subprocess-chokepoint: clean — ${hits.length} raw sync spawn(s), all grandfathered (${baselineSet.size} baselined).`,
|
|
207
|
+
);
|
|
208
|
+
process.exit(0);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"note": "FROZEN baseline of RAW synchronous subprocess spawns (spawnSync/execSync/execFileSync) outside the InFlightSyncOpMarker funnel, at tmux Event-Loop Resilience Increment 1. These are grandfathered while the (A)/(B) conversion lands incrementally. May only SHRINK — a NEW raw sync spawn outside the funnel fails the lint. Funnel a call through withSyncOp(), or add an inline \"// lint-allow-sync-spawn: <reason>\" for a genuine pre-runtime/CLI-boot call.",
|
|
3
|
+
"frozenAt": "2026-06-22",
|
|
4
|
+
"keys": [
|
|
5
|
+
"src/core/AgentRegistry.ts::execSync(`lsof -iTCP:${port} -sTCP:LISTEN -P -n`, { stdio: 'ignore' });::0",
|
|
6
|
+
"src/core/AgentWorktreeDetector.ts::output = execFileSync('git', ['-C', opts.instarRepo, 'worktree', 'list', '--porcelain'], {::0",
|
|
7
|
+
"src/core/BitwardenProvider.ts::const created = execFileSync(bw, [::0",
|
|
8
|
+
"src/core/BitwardenProvider.ts::const existingJson = execFileSync(bw, [::0",
|
|
9
|
+
"src/core/BitwardenProvider.ts::const output = execFileSync(bw, ['status', '--raw'], {::0",
|
|
10
|
+
"src/core/BitwardenProvider.ts::const output = execFileSync(bw, [::0",
|
|
11
|
+
"src/core/BitwardenProvider.ts::const output = execFileSync(bw, [::1",
|
|
12
|
+
"src/core/BitwardenProvider.ts::const output = execFileSync(bw, [::2",
|
|
13
|
+
"src/core/BitwardenProvider.ts::const output = execFileSync(bw, [::3",
|
|
14
|
+
"src/core/BitwardenProvider.ts::const result = execFileSync('which', ['bw'], {::0",
|
|
15
|
+
"src/core/BitwardenProvider.ts::const session = execFileSync(bw, ['login', email, masterPassword, '--raw'], {::0",
|
|
16
|
+
"src/core/BitwardenProvider.ts::const session = execFileSync(bw, ['unlock', masterPassword, '--raw'], {::0",
|
|
17
|
+
"src/core/BitwardenProvider.ts::execFileSync(bw, ['list', 'folders', '--raw'], {::0",
|
|
18
|
+
"src/core/BitwardenProvider.ts::execFileSync(bw, [::0",
|
|
19
|
+
"src/core/BitwardenProvider.ts::execFileSync(bw, [::1",
|
|
20
|
+
"src/core/BitwardenProvider.ts::execFileSync(bw, [::2",
|
|
21
|
+
"src/core/CaffeinateManager.ts::const result = spawnSync('pgrep', ['-x', 'caffeinate'], {::0",
|
|
22
|
+
"src/core/Config.ts::const asdfResult = execFileSync(asdfBin, ['which', name], {::0",
|
|
23
|
+
"src/core/Config.ts::const npmPrefix = execFileSync('npm', ['config', 'get', 'prefix'], {::0",
|
|
24
|
+
"src/core/Config.ts::const result = execFileSync('which', ['gh'], { encoding: 'utf-8', stdio: 'pipe' }).trim();::0",
|
|
25
|
+
"src/core/Config.ts::const result = execFileSync('which', ['git'], { encoding: 'utf-8', stdio: 'pipe' }).trim();::0",
|
|
26
|
+
"src/core/Config.ts::const result = execFileSync('which', ['tmux'], { encoding: 'utf-8', stdio: 'pipe' }).trim();::0",
|
|
27
|
+
"src/core/Config.ts::const result = execFileSync('which', [name], {::0",
|
|
28
|
+
"src/core/FileClassifier.ts::execFileSync(bin, args, {::0",
|
|
29
|
+
"src/core/GitSync.ts::execFileSync('sh', ['-c', validationConfig.buildCommand], {::0",
|
|
30
|
+
"src/core/GitSync.ts::execFileSync('sh', ['-c', validationConfig.testCommand], {::0",
|
|
31
|
+
"src/core/GlobalInstallCleanup.ts::execFileSync('asdf', ['reshim', 'nodejs'], {::0",
|
|
32
|
+
"src/core/InstarWorktreeManager.ts::execFileSync('npm', ['run', 'prepare'], {::0",
|
|
33
|
+
"src/core/OAuthRefresher.ts::const raw = execFileSync(::0",
|
|
34
|
+
"src/core/OAuthRefresher.ts::execFileSync(::0",
|
|
35
|
+
"src/core/PostUpdateMigrator.ts::execFileSync('plutil', ['-lint', plistPath], { stdio: 'pipe' });::0",
|
|
36
|
+
"src/core/PostUpdateMigrator.ts::try { execFileSync('launchctl', ['bootout', `gui/${uid}`, plistPath], { stdio: 'ignore' }); } catch { /* not loaded */ }::0",
|
|
37
|
+
"src/core/PostUpdateMigrator.ts::try { execFileSync('launchctl', ['bootstrap', `gui/${uid}`, plistPath], { stdio: 'ignore' }); } catch { /* non-fatal */ }::0",
|
|
38
|
+
"src/core/Prerequisites.ts::const output = execFileSync(claudePath, ['--version'], {::0",
|
|
39
|
+
"src/core/Prerequisites.ts::const output = execFileSync(tmuxPath, ['-V'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();::0",
|
|
40
|
+
"src/core/Prerequisites.ts::execFileSync('/bin/bash', [::0",
|
|
41
|
+
"src/core/Prerequisites.ts::execFileSync('which', ['brew'], { encoding: 'utf-8', stdio: 'pipe' });::0",
|
|
42
|
+
"src/core/Prerequisites.ts::execFileSync(cmd, args, {::0",
|
|
43
|
+
"src/core/SafeGitExecutor.ts::_cachedConfigIdentity.email = execFileSync('git', ['config', '--global', 'user.email'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim() || undefined;::0",
|
|
44
|
+
"src/core/SafeGitExecutor.ts::_cachedConfigIdentity.name = execFileSync('git', ['config', '--global', 'user.name'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim() || undefined;::0",
|
|
45
|
+
"src/core/SafeGitExecutor.ts::static execSync(args: readonly string[], opts: SafeGitOptions): string {::0",
|
|
46
|
+
"src/core/SafeGitExecutor.ts::stdout = execFileSync('git', args as string[], {::0",
|
|
47
|
+
"src/core/SafeGitExecutor.ts::stdout = execFileSync('git', args as string[], {::1",
|
|
48
|
+
"src/core/SecretStore.ts::const result = execFileSync('secret-tool', [::0",
|
|
49
|
+
"src/core/SecretStore.ts::const result = execFileSync('security', [::0",
|
|
50
|
+
"src/core/SecretStore.ts::execFileSync('secret-tool', [::0",
|
|
51
|
+
"src/core/SecretStore.ts::execFileSync('security', [::0",
|
|
52
|
+
"src/core/SecretStore.ts::execFileSync('security', [::1",
|
|
53
|
+
"src/core/SessionManager.ts::const out = execFileSync(::0",
|
|
54
|
+
"src/core/SessionManager.ts::const out = execFileSync(::1",
|
|
55
|
+
"src/core/SessionManager.ts::const paneInfo = execFileSync(::0",
|
|
56
|
+
"src/core/SessionManager.ts::const panePid = execFileSync(::0",
|
|
57
|
+
"src/core/SessionManager.ts::const panePid = execFileSync(::1",
|
|
58
|
+
"src/core/SessionManager.ts::const panePid = execFileSync(::2",
|
|
59
|
+
"src/core/SessionManager.ts::const psOutput = execFileSync(::0",
|
|
60
|
+
"src/core/SessionManager.ts::const psOutput = execFileSync(::1",
|
|
61
|
+
"src/core/SessionManager.ts::execFileSync('/bin/sleep', [postPasteDelaySec], { timeout: 4000 });::0",
|
|
62
|
+
"src/core/SessionManager.ts::execFileSync(::0",
|
|
63
|
+
"src/core/SessionManager.ts::execFileSync(::1",
|
|
64
|
+
"src/core/SessionManager.ts::execFileSync(::2",
|
|
65
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['has-session', '-t', `=${name}`], {::0",
|
|
66
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['kill-session', '-t', `=${session.tmuxSession}`], {::0",
|
|
67
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['kill-session', '-t', `=${session.tmuxSession}`], {::1",
|
|
68
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['kill-session', '-t', tmuxSession], { encoding: 'utf-8' });::0",
|
|
69
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', `=${tmuxSession}:`, 'Down'], {::0",
|
|
70
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', `=${tmuxSession}:`, 'Enter'], {::0",
|
|
71
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, '-l', text], {::0",
|
|
72
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, '-l', text], {::1",
|
|
73
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, 'Enter'], {::0",
|
|
74
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, 'Enter'], {::1",
|
|
75
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, '\\x1b[200~'], {::0",
|
|
76
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, ['send-keys', '-t', exactTarget, '\\x1b[201~'], {::0",
|
|
77
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::0",
|
|
78
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::1",
|
|
79
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::2",
|
|
80
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::3",
|
|
81
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::4",
|
|
82
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, [::5",
|
|
83
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, tmuxArgs, { encoding: 'utf-8' });::0",
|
|
84
|
+
"src/core/SessionManager.ts::execFileSync(this.config.tmuxPath, tmuxArgs, { encoding: 'utf-8' });::1",
|
|
85
|
+
"src/core/SessionManager.ts::execFileSync(tmuxPath, ['send-keys', '-t', target, 'C-m'], { encoding: 'utf-8', timeout: 5000 });::0",
|
|
86
|
+
"src/core/SessionManager.ts::execFileSync(tmuxPath, ['send-keys', '-t', target, 'Enter'], { encoding: 'utf-8', timeout: 5000 });::0",
|
|
87
|
+
"src/core/SessionManager.ts::execFileSync(tmuxPath, ['send-keys', '-t', target, 'Enter'], { encoding: 'utf-8', timeout: 5000 });::1",
|
|
88
|
+
"src/core/SessionManager.ts::execFileSync(tmuxPath, ['send-keys', '-t', target, 'Enter'], { encoding: 'utf-8', timeout: 5000 });::2",
|
|
89
|
+
"src/core/SessionManager.ts::return execFileSync(::0",
|
|
90
|
+
"src/core/SessionManager.ts::try { execFileSync('/bin/sleep', ['0.15'], { timeout: 1000 }); } catch { /* @silent-fallback-ok — sleep is best-effort */ }::0",
|
|
91
|
+
"src/core/SessionManager.ts::try { execFileSync('/bin/sleep', ['0.3'], { timeout: 2000 }); } catch { /* ignore */ }::0",
|
|
92
|
+
"src/core/SessionManager.ts::try { execFileSync('/bin/sleep', ['0.3'], { timeout: 2000 }); } catch { /* ignore */ }::1",
|
|
93
|
+
"src/core/SessionManager.ts::try { execFileSync('/bin/sleep', ['0.3'], { timeout: 2000 }); } catch { /* ignore */ }::2",
|
|
94
|
+
"src/core/TopicResumeMap.ts::const hasSession = spawnSync(this.tmuxPath, ['has-session', '-t', `=${info.sessionName}`]);::0",
|
|
95
|
+
"src/core/UpdateChecker.ts::execFileSync('xattr', ['-rd', attr, shadowDir], { stdio: 'ignore' });::0",
|
|
96
|
+
"src/core/UpdateChecker.ts::execFileSync(process.execPath, ['-e', `require(\"fs\").readFileSync(${JSON.stringify(newCliPath)})`], {::0",
|
|
97
|
+
"src/core/WorktreeKeyVault.ts::const raw = execFileSync('secret-tool', ['lookup', 'service', KEYCHAIN_SERVICE, 'account', account], {::0",
|
|
98
|
+
"src/core/WorktreeKeyVault.ts::const raw = execFileSync('security', [::0",
|
|
99
|
+
"src/core/WorktreeKeyVault.ts::execFileSync('secret-tool', ['--version'], { stdio: 'pipe', timeout: 2000 });::0",
|
|
100
|
+
"src/core/WorktreeKeyVault.ts::execFileSync('secret-tool', ['store', '--label', `instar:${account}`,::0",
|
|
101
|
+
"src/core/WorktreeKeyVault.ts::execFileSync('security', ['delete-generic-password', '-s', KEYCHAIN_SERVICE, '-a', account], {::0",
|
|
102
|
+
"src/core/WorktreeKeyVault.ts::execFileSync('security', ['list-keychains', '-d', 'user'], { stdio: 'pipe', timeout: 2000 });::0",
|
|
103
|
+
"src/core/WorktreeKeyVault.ts::execFileSync('security', [::0",
|
|
104
|
+
"src/core/WorktreeManager.ts::execFileSync('cp', ['-R', '--reflink=auto', mainNodeModules, wtNodeModules], { timeout: 60_000 });::0",
|
|
105
|
+
"src/core/WorktreeManager.ts::execFileSync('cp', ['-R', mainNodeModules, wtNodeModules], { timeout: 600_000 });::0",
|
|
106
|
+
"src/core/WorktreeManager.ts::execFileSync('cp', ['-c', '-R', mainNodeModules, wtNodeModules], { timeout: 60_000 });::0",
|
|
107
|
+
"src/core/WorktreeManager.ts::return execFileSync('sysctl', ['-n', 'kern.boottime'], { encoding: 'utf-8', timeout: 2000 }).trim();::0",
|
|
108
|
+
"src/core/WorktreeManager.ts::return execFileSync('wmic', ['os', 'get', 'lastbootuptime'], { encoding: 'utf-8', timeout: 2000 }).trim();::0",
|
|
109
|
+
"src/core/WorktreeManager.ts::try { execFileSync('zstd', ['--version'], { stdio: 'pipe' }); return true; }::0",
|
|
110
|
+
"src/core/accountFollowMeCooperativeWipe.ts::execFileSync('security', ['delete-generic-password', '-s', service], {::0",
|
|
111
|
+
"src/core/accountFollowMeCooperativeWipe.ts::execFileSync(spec.cmd, spec.args, {::0",
|
|
112
|
+
"src/core/codexCapabilities.ts::const help = execFileSync(binaryPath, ['--help'], {::0",
|
|
113
|
+
"src/core/codexHookArm.ts::const tmux = (args: string[]) => execFileSync(deps.tmuxPath, args, { encoding: 'utf-8', timeout: 10_000 });::0",
|
|
114
|
+
"src/core/codexHookArm.ts::execFileSync('sleep', ['1']);::0",
|
|
115
|
+
"src/core/codexHookArm.ts::execFileSync('sleep', ['2']);::0",
|
|
116
|
+
"src/core/codexHookArm.ts::execFileSync('sleep', ['2']);::1",
|
|
117
|
+
"src/core/codexHookArm.ts::execFileSync('sleep', ['3']);::0",
|
|
118
|
+
"src/monitoring/CiFailurePoller.ts::((args) => execFileSync('gh', args, { encoding: 'utf-8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] }));::0",
|
|
119
|
+
"src/monitoring/CredentialProvider.ts::const result = execFileSync(::0",
|
|
120
|
+
"src/monitoring/CredentialProvider.ts::const result = execFileSync(::1",
|
|
121
|
+
"src/monitoring/CredentialProvider.ts::execFileSync('security', ['-i'], {::0",
|
|
122
|
+
"src/monitoring/CredentialProvider.ts::execFileSync(::0",
|
|
123
|
+
"src/monitoring/HealthChecker.ts::execFileSync(this.config.sessions.tmuxPath, ['-V'], {::0",
|
|
124
|
+
"src/monitoring/HealthChecker.ts::execFileSync(this.config.sessions.tmuxPath, ['list-sessions'], {::0",
|
|
125
|
+
"src/monitoring/MemoryPressureMonitor.ts::const output = spawnSync('vm_stat', [], { encoding: 'utf-8', timeout: 5000 }).stdout ?? '';::0",
|
|
126
|
+
"src/monitoring/OrphanProcessReaper.ts::return spawnSync('/bin/sh', ['-c', cmd], { encoding: 'utf-8', timeout }).stdout ?? '';::0",
|
|
127
|
+
"src/monitoring/ResumeQueue.ts::out = execFileSync('df', ['-P', stateDir], { timeout: 3000, encoding: 'utf-8' });::0",
|
|
128
|
+
"src/monitoring/SessionRecovery.ts::const output = execFileSync('lsof', ['-p', String(pid), '-Fn'], {::0",
|
|
129
|
+
"src/monitoring/TelemetryHeartbeat.ts::const output = execFileSync('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf-8' });::0",
|
|
130
|
+
"src/monitoring/WorktreeMonitor.ts::const result = spawnSync('/bin/sh', ['-c', `git ${args}`], {::0",
|
|
131
|
+
"src/monitoring/agentWorktreeGit.ts::out = execFileSync('lsof', ['-w', '-d', 'cwd', '-Fn'], { encoding: 'utf-8', timeout: 15_000, maxBuffer: 32 * 1024 * 1024 });::0",
|
|
132
|
+
"src/monitoring/mcpProcessReaperDeps.ts::return execFileSync(file, args, { encoding: 'utf8', timeout, stdio: ['ignore', 'pipe', 'ignore'] });::0",
|
|
133
|
+
"src/monitoring/probes/PlatformProbe.ts::execSync('sleep 0.2', { timeout: 1000 });::0",
|
|
134
|
+
"src/monitoring/probes/PlatformProbe.ts::execSync(::0",
|
|
135
|
+
"src/monitoring/probes/PlatformProbe.ts::execSync(`${tmuxPath} has-session -t ${testSessionName} 2>/dev/null`, { timeout: 1000 });::0",
|
|
136
|
+
"src/monitoring/probes/PlatformProbe.ts::execSync(`ls \"${testDir}\" > /dev/null 2>&1`, { timeout: 3000 });::0",
|
|
137
|
+
"src/monitoring/probes/PlatformProbe.ts::try { execSync(`${tmuxPath} kill-session -t ${testSessionName} 2>/dev/null`, { timeout: 3000 }); } catch { /* ok */ }::0",
|
|
138
|
+
"src/monitoring/probes/PlatformProbe.ts::try { execSync(`${tmuxPath} kill-session -t ${testSessionName} 2>/dev/null`, { timeout: 3000 }); } catch { /* ok */ }::1",
|
|
139
|
+
"src/monitoring/probes/SessionProbe.ts::execFileSync(deps.tmuxPath, ['-V'], { encoding: 'utf-8', timeout: 3000 });::0",
|
|
140
|
+
"src/monitoring/probes/SessionProbe.ts::execFileSync(deps.tmuxPath, ['has-session', '-t', session.tmuxSession], {::0",
|
|
141
|
+
"src/server/routes.ts::const ghRepo = execFileSync('gh', ['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner'], {::0",
|
|
142
|
+
"src/server/routes.ts::const out = execFileSync(::0",
|
|
143
|
+
"src/server/routes.ts::const output = execFileSync(tmuxPath, ['list-sessions', '-F', '#{session_name}'], {::0",
|
|
144
|
+
"src/server/routes.ts::const result = execFileSync('gh', [::0",
|
|
145
|
+
"src/server/routes.ts::execFileSync(tmuxPath, ['kill-session', '-t', `=${paneSession}`], { stdio: 'ignore' });::0",
|
|
146
|
+
"src/server/routes.ts::try { execFileSync(tmuxPath, ['clear-history', '-t', `=${paneSession}:`], { stdio: 'ignore' }); cleared = true; }::0"
|
|
147
|
+
]
|
|
148
|
+
}
|