sneakoscope 5.8.0 → 5.9.0

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.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/config/competitor-scorecard-baseline.json +97 -0
  3. package/config/perf-budgets.v1.json +10 -0
  4. package/crates/sks-core/Cargo.lock +1 -1
  5. package/crates/sks-core/Cargo.toml +1 -1
  6. package/crates/sks-core/src/main.rs +1 -1
  7. package/dist/bin/sks.js +61 -1
  8. package/dist/cli/command-registry.js +1 -1
  9. package/dist/cli/commands-fast.js +20 -0
  10. package/dist/cli/root-fast.js +48 -0
  11. package/dist/cli/router.js +3 -0
  12. package/dist/cli/super-search-command.js +3 -18
  13. package/dist/commands/doctor.js +56 -24
  14. package/dist/config/skills-manifest.json +57 -57
  15. package/dist/core/agents/parallel-write-fixture.js +301 -0
  16. package/dist/core/doctor/doctor-idempotence.js +93 -0
  17. package/dist/core/errors/blocker-humanizer.js +13 -0
  18. package/dist/core/errors/next-action-map.js +56 -0
  19. package/dist/core/fsx.js +1 -1
  20. package/dist/core/install/installed-package-smoke.js +121 -0
  21. package/dist/core/naruto/naruto-active-pool.js +6 -2
  22. package/dist/core/perf/perf-budget.js +145 -0
  23. package/dist/core/quality/competitor-scorecard.js +116 -0
  24. package/dist/core/release/gate-timing.js +103 -0
  25. package/dist/core/retention/retention-budget.js +62 -0
  26. package/dist/core/routes.js +7 -3
  27. package/dist/core/super-search/doctor.js +63 -0
  28. package/dist/core/super-search/runtime.js +11 -0
  29. package/dist/core/version.js +1 -1
  30. package/dist/scripts/agent-fast-mode-default-check.js +10 -1
  31. package/dist/scripts/competitor-scorecard-check.js +25 -0
  32. package/dist/scripts/doctor-idempotence-check.js +8 -0
  33. package/dist/scripts/gate-timing-check.js +8 -0
  34. package/dist/scripts/hook-latency-quantum-check.js +122 -0
  35. package/dist/scripts/installed-package-smoke-check.js +12 -0
  36. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +2 -0
  37. package/dist/scripts/package-published-contract-check.js +7 -0
  38. package/dist/scripts/packlist-performance-check.js +31 -3
  39. package/dist/scripts/parallel-production-smoke-check.js +21 -0
  40. package/dist/scripts/perf-budget-check.js +14 -0
  41. package/dist/scripts/quantum-baseline-report.js +38 -0
  42. package/dist/scripts/release-check-stamp.js +117 -16
  43. package/dist/scripts/retention-budget-check.js +8 -0
  44. package/dist/scripts/route-intent-regression-check.js +44 -0
  45. package/dist/scripts/super-search-live-smoke-check.js +70 -0
  46. package/dist/scripts/super-search-offline-contract-check.js +45 -0
  47. package/package.json +19 -7
@@ -0,0 +1,301 @@
1
+ import { execFile } from 'node:child_process';
2
+ import fsp from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ const execFileAsync = promisify(execFile);
7
+ export const PARALLEL_PRODUCTION_SMOKE_SCHEMA = 'sks.parallel-production-smoke.v1';
8
+ export async function runParallelProductionSmoke(options = {}) {
9
+ const tmpRoot = await fsp.mkdtemp(path.join(os.tmpdir(), 'sks-parallel-production-smoke-'));
10
+ const repoRoot = path.join(tmpRoot, 'tmp-repo');
11
+ const worktreesRoot = path.join(tmpRoot, 'worktrees');
12
+ const blockers = [];
13
+ let cleanup = null;
14
+ try {
15
+ await createFixtureRepo(repoRoot);
16
+ await fsp.mkdir(worktreesRoot, { recursive: true });
17
+ const workerSpecs = buildWorkerSpecs(options.injectFailure === true);
18
+ await Promise.all(workerSpecs.map((worker) => setupWorkerWorktree(repoRoot, worktreesRoot, worker)));
19
+ const workerResults = await Promise.all(workerSpecs.map((worker) => runWorker(worker, path.join(worktreesRoot, worker.id))));
20
+ const successfulWorkers = workerResults.filter((result) => result.ok);
21
+ const failedWorkers = workerResults.filter((result) => !result.ok);
22
+ const targetFiles = ['src/a.ts', 'src/b.ts', 'src/c.ts'];
23
+ await mergeWorkerOutputs(repoRoot, successfulWorkers);
24
+ const typecheck = await runTypecheck(repoRoot);
25
+ const sharedUntouched = await fileEquals(path.join(repoRoot, 'src', 'shared.ts'), "export const sharedValue = 'stable-shared-contract'\n");
26
+ const statusAfterMerge = (await runCommand(['git', 'status', '--short'], repoRoot)).stdout_tail.trim();
27
+ const parentMergeArtifact = {
28
+ schema: 'sks.parallel-production-parent-merge.v1',
29
+ ok: successfulWorkers.length >= 3 && targetFiles.every((file) => successfulWorkers.some((worker) => worker.changed_files.includes(file))) && sharedUntouched,
30
+ merged_files: Array.from(new Set(successfulWorkers.flatMap((worker) => worker.changed_files))).sort(),
31
+ shared_untouched: sharedUntouched,
32
+ git_status_after_merge: statusAfterMerge
33
+ };
34
+ if (parentMergeArtifact.ok) {
35
+ await runCommand(['git', 'add', ...parentMergeArtifact.merged_files], repoRoot, true);
36
+ await runCommand(['git', 'commit', '-m', 'parent merge parallel worker outputs'], repoRoot, true);
37
+ }
38
+ const changedFilesByWorker = Object.fromEntries(successfulWorkers.map((worker) => [worker.worker_id, worker.changed_files]));
39
+ const changedFiles = Array.from(new Set(successfulWorkers.flatMap((worker) => worker.changed_files))).sort();
40
+ const overlapWindows = buildOverlapWindows(workerResults);
41
+ const cleanupBefore = await runCommand(['git', 'worktree', 'list', '--porcelain'], repoRoot);
42
+ cleanup = await cleanupWorktreesAndBranches(repoRoot, tmpRoot, workerSpecs, cleanupBefore.stdout_tail, options.keepTmp === true);
43
+ if (workerSpecs.filter((worker) => !worker.fail).length < 3)
44
+ blockers.push('parallel_write_worker_target_below_three');
45
+ if (successfulWorkers.length < 3)
46
+ blockers.push('parallel_write_successful_workers_below_target');
47
+ if (changedFiles.length < 3)
48
+ blockers.push('parallel_write_changed_files_below_target');
49
+ if (!targetFiles.every((file) => changedFiles.includes(file)))
50
+ blockers.push('parallel_write_target_files_missing');
51
+ if (new Set(Object.entries(changedFilesByWorker).filter(([, files]) => files.some((file) => targetFiles.includes(file))).map(([workerId]) => workerId)).size < 2)
52
+ blockers.push('parallel_write_files_not_distributed');
53
+ if (overlapWindows.length === 0)
54
+ blockers.push('worker_timestamp_overlap_missing');
55
+ if (successfulWorkers.filter((worker) => worker.patch.trim()).length < 3)
56
+ blockers.push('patch_envelope_count_below_target');
57
+ if (!parentMergeArtifact.ok)
58
+ blockers.push('parent_merge_artifact_missing_or_invalid');
59
+ if (!typecheck.ok)
60
+ blockers.push('typecheck_failed');
61
+ if (!cleanup.ok)
62
+ blockers.push('worktree_cleanup_failed');
63
+ if (failedWorkers.length > 0 && successfulWorkers.length === 0)
64
+ blockers.push('failure_injection_killed_scheduler');
65
+ return {
66
+ schema: PARALLEL_PRODUCTION_SMOKE_SCHEMA,
67
+ ok: blockers.length === 0,
68
+ status: blockers.length === 0 ? 'passed' : 'blocked',
69
+ proof_level: blockers.length === 0 ? 'production_git_worktree_fixture' : 'blocked',
70
+ tmp_root: tmpRoot,
71
+ repo_root: repoRoot,
72
+ worker_count: successfulWorkers.length,
73
+ worker_ids: successfulWorkers.map((worker) => worker.worker_id).sort(),
74
+ changed_files: changedFiles,
75
+ changed_files_by_worker: changedFilesByWorker,
76
+ distributed_across_workers: new Set(Object.keys(changedFilesByWorker)).size >= 2,
77
+ timestamp_overlap: overlapWindows.length > 0,
78
+ overlap_windows: overlapWindows,
79
+ patch_envelope_count: successfulWorkers.filter((worker) => worker.patch.trim()).length,
80
+ parent_merge_artifact: parentMergeArtifact,
81
+ typecheck,
82
+ worktree_cleanup: cleanup,
83
+ failure_injection: {
84
+ requested: options.injectFailure === true,
85
+ worker_failure_seen: failedWorkers.length > 0,
86
+ reassignment_attempted: failedWorkers.length > 0,
87
+ survived_worker_failure: failedWorkers.length > 0 ? successfulWorkers.length >= 3 : true,
88
+ failed_worker_id: failedWorkers[0]?.worker_id ?? null,
89
+ failed_worker_ids: failedWorkers.map((worker) => worker.worker_id).sort(),
90
+ recovered_work_item_ids: failedWorkers.length > 0 ? successfulWorkers.flatMap((worker) => worker.changed_files).sort() : [],
91
+ scheduler_survived: successfulWorkers.length >= 3,
92
+ successful_workers: successfulWorkers.map((worker) => worker.worker_id).sort(),
93
+ failed_workers: failedWorkers.map((worker) => worker.worker_id).sort()
94
+ },
95
+ mock_only: false,
96
+ blockers
97
+ };
98
+ }
99
+ catch (error) {
100
+ blockers.push(error instanceof Error ? `unexpected_error:${error.message}` : 'unexpected_error');
101
+ cleanup ??= await bestEffortCleanup(repoRoot, tmpRoot, options.keepTmp === true);
102
+ return blockedReport(tmpRoot, repoRoot, cleanup, blockers);
103
+ }
104
+ }
105
+ async function createFixtureRepo(repoRoot) {
106
+ await fsp.mkdir(path.join(repoRoot, 'src'), { recursive: true });
107
+ await fsp.writeFile(path.join(repoRoot, 'package.json'), `${JSON.stringify({ type: 'module', scripts: { typecheck: 'tsc -p tsconfig.json --noEmit' }, devDependencies: {} }, null, 2)}\n`);
108
+ await fsp.writeFile(path.join(repoRoot, 'tsconfig.json'), `${JSON.stringify({ compilerOptions: { target: 'ES2022', module: 'NodeNext', moduleResolution: 'NodeNext', strict: true, noEmit: true }, include: ['src/**/*.ts'] }, null, 2)}\n`);
109
+ await fsp.writeFile(path.join(repoRoot, 'src', 'shared.ts'), "export const sharedValue = 'stable-shared-contract'\n");
110
+ await fsp.writeFile(path.join(repoRoot, 'src', 'a.ts'), "import { sharedValue } from './shared.js'\nexport const aValue = `a:${sharedValue}`\n");
111
+ await fsp.writeFile(path.join(repoRoot, 'src', 'b.ts'), "import { sharedValue } from './shared.js'\nexport const bValue = `b:${sharedValue}`\n");
112
+ await fsp.writeFile(path.join(repoRoot, 'src', 'c.ts'), "import { sharedValue } from './shared.js'\nexport const cValue = `c:${sharedValue}`\n");
113
+ await runCommand(['git', 'init', '--initial-branch', 'main'], repoRoot, true);
114
+ await runCommand(['git', 'config', 'user.email', 'parallel-smoke@example.invalid'], repoRoot, true);
115
+ await runCommand(['git', 'config', 'user.name', 'Parallel Smoke'], repoRoot, true);
116
+ await runCommand(['git', 'add', '.'], repoRoot, true);
117
+ await runCommand(['git', 'commit', '-m', 'initial fixture'], repoRoot, true);
118
+ }
119
+ function buildWorkerSpecs(injectFailure) {
120
+ const workers = [
121
+ { id: 'worker-a', branch: 'worker/parallel-a', file: 'src/a.ts', exportName: 'workerADeterministic', returnValue: 'alpha' },
122
+ { id: 'worker-b', branch: 'worker/parallel-b', file: 'src/b.ts', exportName: 'workerBDeterministic', returnValue: 'bravo' },
123
+ { id: 'worker-c', branch: 'worker/parallel-c', file: 'src/c.ts', exportName: 'workerCDeterministic', returnValue: 'charlie' }
124
+ ];
125
+ if (injectFailure) {
126
+ workers.push({ id: 'worker-fail', branch: 'worker/parallel-fail', file: 'src/shared.ts', exportName: 'shouldNotLand', returnValue: 'failed', fail: true });
127
+ }
128
+ return workers;
129
+ }
130
+ async function setupWorkerWorktree(repoRoot, worktreesRoot, worker) {
131
+ await runCommand(['git', 'worktree', 'add', '-b', worker.branch, path.join(worktreesRoot, worker.id), 'HEAD'], repoRoot, true);
132
+ }
133
+ async function runWorker(worker, worktree) {
134
+ const startedMs = Date.now();
135
+ await delay(25);
136
+ if (worker.fail) {
137
+ return {
138
+ worker_id: worker.id,
139
+ ok: false,
140
+ branch: worker.branch,
141
+ worktree,
142
+ changed_files: [],
143
+ patch: '',
144
+ started_ms: startedMs,
145
+ completed_ms: Date.now(),
146
+ blocker: 'injected_worker_failure'
147
+ };
148
+ }
149
+ const filePath = path.join(worktree, worker.file);
150
+ const original = await fsp.readFile(filePath, 'utf8');
151
+ await fsp.writeFile(filePath, `${original}\nexport function ${worker.exportName}(): string {\n return '${worker.returnValue}'\n}\n`);
152
+ await runCommand(['git', 'add', worker.file], worktree, true);
153
+ await runCommand(['git', 'commit', '-m', `${worker.id} deterministic function`], worktree, true);
154
+ const diff = await runCommand(['git', 'diff', 'HEAD~1', 'HEAD', '--', worker.file], worktree, true);
155
+ return {
156
+ worker_id: worker.id,
157
+ ok: true,
158
+ branch: worker.branch,
159
+ worktree,
160
+ changed_files: [worker.file],
161
+ patch: diff.stdout_tail,
162
+ started_ms: startedMs,
163
+ completed_ms: Date.now()
164
+ };
165
+ }
166
+ async function mergeWorkerOutputs(repoRoot, workers) {
167
+ for (const worker of workers) {
168
+ for (const file of worker.changed_files) {
169
+ await fsp.copyFile(path.join(worker.worktree, file), path.join(repoRoot, file));
170
+ }
171
+ }
172
+ }
173
+ async function runTypecheck(repoRoot) {
174
+ const localTsc = path.join(process.cwd(), 'node_modules', '.bin', process.platform === 'win32' ? 'tsc.cmd' : 'tsc');
175
+ return runCommand([localTsc, '-p', 'tsconfig.json', '--noEmit'], repoRoot);
176
+ }
177
+ async function cleanupWorktreesAndBranches(repoRoot, tmpRoot, workers, before, keepTmp) {
178
+ for (const worker of workers) {
179
+ await runCommand(['git', 'worktree', 'remove', '--force', path.join(tmpRoot, 'worktrees', worker.id)], repoRoot);
180
+ }
181
+ await runCommand(['git', 'worktree', 'prune'], repoRoot);
182
+ for (const worker of workers) {
183
+ await runCommand(['git', 'branch', '-D', worker.branch], repoRoot);
184
+ }
185
+ const after = await runCommand(['git', 'worktree', 'list', '--porcelain'], repoRoot);
186
+ const status = await runCommand(['git', 'status', '--short'], repoRoot);
187
+ if (!keepTmp)
188
+ await fsp.rm(tmpRoot, { recursive: true, force: true });
189
+ const tmpRemoved = keepTmp ? false : !(await exists(tmpRoot));
190
+ return {
191
+ ok: workers.every((worker) => !after.stdout_tail.includes(worker.id) && !after.stdout_tail.includes(worker.branch)) && status.stdout_tail.trim().length === 0 && tmpRemoved,
192
+ worktree_list_before_cleanup: before,
193
+ worktree_list_after_cleanup: after.stdout_tail,
194
+ worker_worktrees_removed: workers.every((worker) => !after.stdout_tail.includes(worker.id)),
195
+ worker_branches_removed: workers.every((worker) => !after.stdout_tail.includes(worker.branch)),
196
+ parent_dirty_after_cleanup: status.stdout_tail.trim().length > 0,
197
+ tmp_removed: tmpRemoved
198
+ };
199
+ }
200
+ async function bestEffortCleanup(repoRoot, tmpRoot, keepTmp) {
201
+ const before = await runCommand(['git', 'worktree', 'list', '--porcelain'], repoRoot);
202
+ if (!keepTmp)
203
+ await fsp.rm(tmpRoot, { recursive: true, force: true });
204
+ return {
205
+ ok: !keepTmp && !(await exists(tmpRoot)),
206
+ worktree_list_before_cleanup: before.stdout_tail,
207
+ worktree_list_after_cleanup: '',
208
+ worker_worktrees_removed: true,
209
+ worker_branches_removed: false,
210
+ parent_dirty_after_cleanup: false,
211
+ tmp_removed: !keepTmp && !(await exists(tmpRoot))
212
+ };
213
+ }
214
+ function buildOverlapWindows(results) {
215
+ const windows = [];
216
+ for (let i = 0; i < results.length; i += 1) {
217
+ for (let j = i + 1; j < results.length; j += 1) {
218
+ const a = results[i];
219
+ const b = results[j];
220
+ if (!a || !b)
221
+ continue;
222
+ const overlap = Math.min(a.completed_ms, b.completed_ms) - Math.max(a.started_ms, b.started_ms);
223
+ if (overlap > 0)
224
+ windows.push({ worker_a: a.worker_id, worker_b: b.worker_id, overlap_ms: overlap });
225
+ }
226
+ }
227
+ return windows;
228
+ }
229
+ async function runCommand(command, cwd, throwOnFailure = false) {
230
+ try {
231
+ const { stdout, stderr } = await execFileAsync(command[0] ?? '', command.slice(1), { cwd, encoding: 'utf8', maxBuffer: 1024 * 1024 * 8 });
232
+ return { ok: true, command, cwd, exit_code: 0, stdout_tail: tail(stdout), stderr_tail: tail(stderr) };
233
+ }
234
+ catch (error) {
235
+ const record = {
236
+ ok: false,
237
+ command,
238
+ cwd,
239
+ exit_code: typeof error.code === 'number' ? error.code : null,
240
+ stdout_tail: tail(typeof error.stdout === 'string' ? error.stdout : ''),
241
+ stderr_tail: tail(typeof error.stderr === 'string' ? error.stderr : error instanceof Error ? error.message : String(error))
242
+ };
243
+ if (throwOnFailure)
244
+ throw new Error(`${command.join(' ')} failed: ${record.stderr_tail || record.stdout_tail}`);
245
+ return record;
246
+ }
247
+ }
248
+ function blockedReport(tmpRoot, repoRoot, cleanup, blockers) {
249
+ return {
250
+ schema: PARALLEL_PRODUCTION_SMOKE_SCHEMA,
251
+ ok: false,
252
+ status: 'blocked',
253
+ proof_level: 'blocked',
254
+ tmp_root: tmpRoot,
255
+ repo_root: repoRoot,
256
+ worker_count: 0,
257
+ worker_ids: [],
258
+ changed_files: [],
259
+ changed_files_by_worker: {},
260
+ distributed_across_workers: false,
261
+ timestamp_overlap: false,
262
+ overlap_windows: [],
263
+ patch_envelope_count: 0,
264
+ parent_merge_artifact: { schema: 'sks.parallel-production-parent-merge.v1', ok: false, merged_files: [], shared_untouched: false, git_status_after_merge: '' },
265
+ typecheck: { ok: false, command: [], cwd: repoRoot, exit_code: null, stdout_tail: '', stderr_tail: '' },
266
+ worktree_cleanup: cleanup,
267
+ failure_injection: {
268
+ requested: false,
269
+ worker_failure_seen: false,
270
+ reassignment_attempted: false,
271
+ survived_worker_failure: false,
272
+ failed_worker_id: null,
273
+ failed_worker_ids: [],
274
+ recovered_work_item_ids: [],
275
+ scheduler_survived: false,
276
+ successful_workers: [],
277
+ failed_workers: []
278
+ },
279
+ mock_only: false,
280
+ blockers
281
+ };
282
+ }
283
+ async function fileEquals(file, expected) {
284
+ return (await fsp.readFile(file, 'utf8')) === expected;
285
+ }
286
+ async function exists(file) {
287
+ try {
288
+ await fsp.access(file);
289
+ return true;
290
+ }
291
+ catch {
292
+ return false;
293
+ }
294
+ }
295
+ function delay(ms) {
296
+ return new Promise((resolve) => setTimeout(resolve, ms));
297
+ }
298
+ function tail(text, max = 4000) {
299
+ return text.length > max ? text.slice(-max) : text;
300
+ }
301
+ //# sourceMappingURL=parallel-write-fixture.js.map
@@ -0,0 +1,93 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { runProcess, tmpdir, writeJsonAtomic } from '../fsx.js';
4
+ export async function runDoctorIdempotence(root) {
5
+ const tmp = tmpdir('sks-doctor-idempotence-');
6
+ const home = path.join(tmp, 'home');
7
+ const codexHome = path.join(tmp, 'codex');
8
+ await Promise.all([home, codexHome].map((dir) => fs.mkdir(dir, { recursive: true })));
9
+ const first = await runDoctor(root, home, codexHome);
10
+ const second = await runDoctor(root, home, codexHome);
11
+ const rollbackManifestExists = await hasRollbackManifest(home, codexHome, root);
12
+ const changedFilesSecond = extractChangedFiles(second);
13
+ const blockers = [];
14
+ if (first.exit_code !== 0)
15
+ blockers.push(`first_doctor_exit_${first.exit_code}`);
16
+ if (second.exit_code !== 0)
17
+ blockers.push(`second_doctor_exit_${second.exit_code}`);
18
+ if (changedFilesSecond.length > 0)
19
+ blockers.push('second_doctor_not_noop');
20
+ const report = {
21
+ schema: 'sks.doctor-idempotence.v1',
22
+ ok: blockers.length === 0,
23
+ generated_at: new Date().toISOString(),
24
+ temp_dir: tmp,
25
+ first,
26
+ second,
27
+ rollback_manifest_exists: rollbackManifestExists,
28
+ changed_files_second: changedFilesSecond,
29
+ blockers: [...new Set(blockers)]
30
+ };
31
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'doctor-idempotence.json'), report);
32
+ return report;
33
+ }
34
+ async function runDoctor(root, home, codexHome) {
35
+ const started = Date.now();
36
+ const res = await runProcess(process.execPath, ['./dist/bin/sks.js', 'doctor', '--fix', '--yes', '--json'], {
37
+ cwd: root,
38
+ timeoutMs: 180_000,
39
+ maxOutputBytes: 1024 * 1024,
40
+ env: {
41
+ HOME: home,
42
+ CODEX_HOME: codexHome,
43
+ SKS_TEST_ISOLATION: '1',
44
+ SKS_DISABLE_NETWORK: '1',
45
+ SKS_DISABLE_UPDATE_CHECK: '1'
46
+ }
47
+ });
48
+ let parsed = null;
49
+ try {
50
+ parsed = JSON.parse(res.stdout);
51
+ }
52
+ catch { }
53
+ const blockers = Array.isArray(parsed?.blockers) ? parsed.blockers.map(String) : [];
54
+ return {
55
+ exit_code: res.code,
56
+ duration_ms: Date.now() - started,
57
+ stdout_json: parsed != null,
58
+ ok: res.code === 0 && parsed?.ok !== false,
59
+ blockers,
60
+ parsed
61
+ };
62
+ }
63
+ function extractChangedFiles(run) {
64
+ const repair = run.parsed?.repair || {};
65
+ const candidates = [
66
+ repair.doctor_transaction?.changed_files,
67
+ run.parsed?.doctor_fix_transaction?.changed_files,
68
+ run.parsed?.changed_files
69
+ ];
70
+ return [...new Set(candidates.flatMap((value) => Array.isArray(value) ? value.map(String) : []))];
71
+ }
72
+ async function hasRollbackManifest(...roots) {
73
+ for (const dir of roots) {
74
+ const found = await findFileContaining(dir, /rollback|transaction|manifest/i, 4);
75
+ if (found)
76
+ return true;
77
+ }
78
+ return false;
79
+ }
80
+ async function findFileContaining(dir, pattern, depth) {
81
+ if (depth < 0)
82
+ return false;
83
+ const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
84
+ for (const entry of entries) {
85
+ const child = path.join(dir, entry.name);
86
+ if (entry.isFile() && pattern.test(entry.name))
87
+ return true;
88
+ if (entry.isDirectory() && await findFileContaining(child, pattern, depth - 1))
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+ //# sourceMappingURL=doctor-idempotence.js.map
@@ -0,0 +1,13 @@
1
+ import { diagnosticForBlocker } from './next-action-map.js';
2
+ export function humanizeBlockers(blockers = [], evidencePaths = []) {
3
+ const uniqueBlockers = [...new Set(blockers.filter(Boolean))];
4
+ const diagnostics = uniqueBlockers.map((blocker) => diagnosticForBlocker(blocker));
5
+ return {
6
+ human_summary: diagnostics.length
7
+ ? diagnostics.map((diagnostic) => diagnostic.human_summary).join(' ')
8
+ : 'No blockers were recorded.',
9
+ next_actions: [...new Set(diagnostics.flatMap((diagnostic) => diagnostic.next_actions))],
10
+ evidence_paths: [...new Set(evidencePaths.filter(Boolean))]
11
+ };
12
+ }
13
+ //# sourceMappingURL=blocker-humanizer.js.map
@@ -0,0 +1,56 @@
1
+ const NEXT_ACTIONS = {
2
+ source_acquisition_unavailable: {
3
+ human_summary: 'Super-Search has no live source acquisition path available for this request.',
4
+ next_actions: ['sks super-search doctor --json']
5
+ },
6
+ parallel_runtime_events_missing: {
7
+ human_summary: 'Parallel worker runtime events were not recorded.',
8
+ next_actions: ['sks naruto proof latest --json']
9
+ },
10
+ worker_timestamp_overlap_missing: {
11
+ human_summary: 'There is no evidence that workers actually overlapped in time.',
12
+ next_actions: ['cat .sneakoscope/missions/latest/parallel-runtime-proof.json']
13
+ },
14
+ production_proof_mock_only: {
15
+ human_summary: 'A production gate received mock-only proof.',
16
+ next_actions: ['Inspect whether a mock backend path is connected to production proof.']
17
+ },
18
+ direct_url_fetch_adapter_unavailable: {
19
+ human_summary: 'Direct URL fetch is unavailable in this Node runtime.',
20
+ next_actions: ['node -e "console.log(typeof fetch)"']
21
+ },
22
+ direct_url_fetch_failed: {
23
+ human_summary: 'Direct URL fetch failed before verified content could be captured.',
24
+ next_actions: ['sks super-search doctor --json']
25
+ },
26
+ direct_url_fetch_timeout: {
27
+ human_summary: 'Direct URL fetch timed out before verified content could be captured.',
28
+ next_actions: ['Retry the fetch or inspect local network access.']
29
+ },
30
+ direct_url_fetch_empty_content: {
31
+ human_summary: 'Direct URL fetch returned no readable content.',
32
+ next_actions: ['Open the URL manually and check whether it requires authentication or JavaScript rendering.']
33
+ }
34
+ };
35
+ export function diagnosticForBlocker(blocker) {
36
+ const exact = NEXT_ACTIONS[blocker];
37
+ if (exact)
38
+ return exact;
39
+ if (/^direct_url_fetch_http_\d+$/.test(blocker)) {
40
+ return {
41
+ human_summary: `Direct URL fetch returned HTTP ${blocker.replace('direct_url_fetch_http_', '')}.`,
42
+ next_actions: ['Open the URL manually and verify whether it is public and reachable.']
43
+ };
44
+ }
45
+ if (blocker.startsWith('super_search_mission_artifact_missing:')) {
46
+ return {
47
+ human_summary: 'A required Super-Search evidence artifact is missing.',
48
+ next_actions: [`cat ${blocker.slice('super_search_mission_artifact_missing:'.length)}`]
49
+ };
50
+ }
51
+ return {
52
+ human_summary: `Gate blocked on machine reason: ${blocker}.`,
53
+ next_actions: ['Inspect the gate JSON and referenced evidence paths.']
54
+ };
55
+ }
56
+ //# sourceMappingURL=next-action-map.js.map
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '5.8.0';
8
+ export const PACKAGE_VERSION = '5.9.0';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -0,0 +1,121 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { runProcess, writeJsonAtomic } from '../fsx.js';
5
+ export async function runInstalledPackageSmoke(root) {
6
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-installed-smoke-'));
7
+ const home = path.join(tmp, 'home');
8
+ const codexHome = path.join(tmp, 'codex-home');
9
+ const npmCache = path.join(tmp, 'npm-cache');
10
+ await Promise.all([home, codexHome, npmCache].map((dir) => fs.mkdir(dir, { recursive: true })));
11
+ const blockers = [];
12
+ const commands = [];
13
+ let tarball = null;
14
+ let installedVersion = null;
15
+ let packedVersion = null;
16
+ const pack = await runJsonCommand(root, ['npm', 'pack', '--json', '--ignore-scripts'], { npmCache });
17
+ commands.push(pack.command);
18
+ if (pack.exit_code !== 0)
19
+ blockers.push('npm_pack_failed');
20
+ const packInfo = packJsonObject(pack.json);
21
+ if (packInfo?.filename)
22
+ tarball = path.join(root, String(packInfo.filename));
23
+ if (packInfo?.version)
24
+ packedVersion = String(packInfo.version);
25
+ if (!tarball)
26
+ blockers.push('npm_pack_tarball_missing');
27
+ await fs.writeFile(path.join(tmp, 'package.json'), JSON.stringify({ private: true, type: 'module' }, null, 2));
28
+ if (tarball) {
29
+ const install = await runJsonCommand(tmp, ['npm', 'install', tarball, '--ignore-scripts'], { home, codexHome, npmCache });
30
+ commands.push(install.command);
31
+ if (install.exit_code !== 0)
32
+ blockers.push('npm_install_tarball_failed');
33
+ }
34
+ const bin = path.join(tmp, 'node_modules', '.bin', 'sks');
35
+ const smokeCommands = [
36
+ [bin, '--version'],
37
+ [bin, 'commands', '--json'],
38
+ [bin, 'bootstrap', '--json'],
39
+ [bin, 'doctor', '--json'],
40
+ [bin, 'super-search', 'doctor', '--json'],
41
+ [bin, 'selftest', '--mock']
42
+ ];
43
+ for (const argv of smokeCommands) {
44
+ const result = await runJsonCommand(tmp, argv, { home, codexHome, npmCache });
45
+ commands.push(result.command);
46
+ if (result.exit_code !== 0)
47
+ blockers.push(`installed_command_failed:${argv.slice(1).join('_') || 'version'}`);
48
+ if (argv.includes('--version')) {
49
+ const match = String(result.stdout || '').match(/([0-9]+\.[0-9]+\.[0-9]+)/);
50
+ if (match)
51
+ installedVersion = match[1] || null;
52
+ }
53
+ }
54
+ const forbiddenFindings = [];
55
+ if (process.env.HOME && home === process.env.HOME)
56
+ forbiddenFindings.push('host_home_reused');
57
+ if (process.env.CODEX_HOME && codexHome === process.env.CODEX_HOME)
58
+ forbiddenFindings.push('host_codex_home_reused');
59
+ if (packedVersion && installedVersion && packedVersion !== installedVersion)
60
+ blockers.push(`installed_version_mismatch:${installedVersion}:expected_${packedVersion}`);
61
+ blockers.push(...forbiddenFindings.map((item) => `forbidden:${item}`));
62
+ const report = {
63
+ schema: 'sks.installed-package-smoke.v1',
64
+ ok: blockers.length === 0,
65
+ generated_at: new Date().toISOString(),
66
+ tarball,
67
+ installed_version: installedVersion,
68
+ temp_dir: tmp,
69
+ commands,
70
+ forbidden_findings: forbiddenFindings,
71
+ blockers: [...new Set(blockers)]
72
+ };
73
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'installed-package-smoke.json'), report);
74
+ return report;
75
+ }
76
+ async function runJsonCommand(cwd, argv, opts) {
77
+ const started = Date.now();
78
+ const [command, ...args] = argv;
79
+ const env = {
80
+ SKS_TEST_ISOLATION: '1',
81
+ SKS_DISABLE_NETWORK: '1',
82
+ SKS_DISABLE_UPDATE_CHECK: '1'
83
+ };
84
+ if (opts.home !== undefined)
85
+ env.HOME = opts.home;
86
+ if (opts.codexHome !== undefined)
87
+ env.CODEX_HOME = opts.codexHome;
88
+ if (opts.npmCache !== undefined) {
89
+ env.npm_config_cache = opts.npmCache;
90
+ env.NPM_CONFIG_CACHE = opts.npmCache;
91
+ }
92
+ const res = await runProcess(command || 'node', args, {
93
+ cwd,
94
+ timeoutMs: 120_000,
95
+ maxOutputBytes: 1024 * 1024,
96
+ env
97
+ });
98
+ let parsed = null;
99
+ try {
100
+ parsed = JSON.parse(res.stdout);
101
+ }
102
+ catch { }
103
+ return {
104
+ exit_code: res.code,
105
+ stdout: res.stdout,
106
+ json: parsed,
107
+ command: {
108
+ argv,
109
+ exit_code: res.code,
110
+ stdout_json: parsed != null,
111
+ duration_ms: Date.now() - started,
112
+ stdout_tail: res.stdout.slice(-1200),
113
+ stderr_tail: res.stderr.slice(-1200)
114
+ }
115
+ };
116
+ }
117
+ function packJsonObject(value) {
118
+ const row = Array.isArray(value) ? value[0] : value;
119
+ return row && typeof row === 'object' ? row : null;
120
+ }
121
+ //# sourceMappingURL=installed-package-smoke.js.map
@@ -51,8 +51,12 @@ export async function runNarutoRealActivePool(input) {
51
51
  maxObserved = Math.max(maxObserved, active.size);
52
52
  timeline.push({ tick, active: active.size, pending: pending.length, completed: completed.size, event: launched ? 'refill' : 'wait' });
53
53
  const done = await nextCollectableWorkers(active, input.hardTimeoutMs);
54
- if (!done.length)
55
- break;
54
+ if (!done.length) {
55
+ tick += 1;
56
+ if (tick > input.graph.work_items.length * 4 + 20)
57
+ break;
58
+ continue;
59
+ }
56
60
  for (const handle of done) {
57
61
  active.delete(handle.id);
58
62
  if (handle.placement.placement === 'zellij-pane')