sneakoscope 5.3.0 → 5.5.1

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 (54) hide show
  1. package/README.md +4 -0
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/cli/install-helpers.js +17 -0
  7. package/dist/config/skills-manifest.json +58 -58
  8. package/dist/core/agents/agent-orchestrator.js +14 -2
  9. package/dist/core/agents/agent-proof-evidence.js +16 -0
  10. package/dist/core/bench.js +24 -2
  11. package/dist/core/codex-app/sks-menubar.js +83 -10
  12. package/dist/core/commands/agent-command.js +35 -1
  13. package/dist/core/commands/basic-cli.js +59 -0
  14. package/dist/core/commands/command-utils.js +14 -4
  15. package/dist/core/commands/goal-command.js +3 -0
  16. package/dist/core/commands/image-ux-review-command.js +4 -1
  17. package/dist/core/commands/mad-sks-command.js +10 -1
  18. package/dist/core/commands/naruto-command.js +12 -7
  19. package/dist/core/commands/ppt-command.js +40 -1
  20. package/dist/core/commands/qa-loop-command.js +4 -1
  21. package/dist/core/commands/release-command.js +3 -3
  22. package/dist/core/commands/research-command.js +4 -0
  23. package/dist/core/commands/route-success-helpers.js +2 -0
  24. package/dist/core/commands/run-command.js +22 -5
  25. package/dist/core/commands/seo-command.js +31 -4
  26. package/dist/core/db-safety.js +8 -1
  27. package/dist/core/feature-fixture-executor.js +262 -0
  28. package/dist/core/feature-fixtures.js +95 -52
  29. package/dist/core/fsx.js +1 -1
  30. package/dist/core/loops/loop-decomposer.js +4 -3
  31. package/dist/core/loops/loop-planner.js +7 -3
  32. package/dist/core/loops/loop-worker-runtime.js +6 -0
  33. package/dist/core/pipeline-internals/runtime-gates.js +39 -3
  34. package/dist/core/proof/selftest-proof-fixtures.js +8 -0
  35. package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +18 -1
  36. package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +28 -12
  37. package/dist/core/questions.js +12 -10
  38. package/dist/core/release/release-gate-node.js +2 -0
  39. package/dist/core/routes.js +11 -0
  40. package/dist/core/stop-gate/stop-gate-check.js +4 -0
  41. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  42. package/dist/core/trust-kernel/trust-report.js +10 -1
  43. package/dist/core/update/update-migration-state.js +15 -1
  44. package/dist/core/update-check.js +44 -2
  45. package/dist/core/version.js +1 -1
  46. package/dist/core/work-order-ledger.js +60 -0
  47. package/dist/scripts/gate-policy-audit-check.js +2 -2
  48. package/dist/scripts/packlist-performance-check.js +7 -1
  49. package/dist/scripts/release-dag-full-coverage-check.js +1 -1
  50. package/dist/scripts/release-gate-existence-audit.js +1 -1
  51. package/dist/scripts/release-gate-planner.js +1 -1
  52. package/dist/scripts/release-metadata-1-19-check.js +1 -1
  53. package/package.json +5 -1
  54. package/schemas/release/release-gate-node.schema.json +1 -0
@@ -155,6 +155,7 @@ export const ROUTES = [
155
155
  context7Policy: 'if_external_docs',
156
156
  reasoningPolicy: 'medium',
157
157
  stopGate: 'none',
158
+ coverageExemptReason: 'read-only answer route; never writes or modifies code/files',
158
159
  cliEntrypoint: 'implicit question route',
159
160
  examples: ['이 파이프라인이 왜 이렇게 동작해?', 'What does this hook do?']
160
161
  },
@@ -169,6 +170,7 @@ export const ROUTES = [
169
170
  context7Policy: 'optional',
170
171
  reasoningPolicy: 'medium',
171
172
  stopGate: 'honest_mode',
173
+ coverageExemptReason: 'setup/help/status routing only; delegates any real work to the target route it dispatches to',
172
174
  cliEntrypoint: 'sks commands',
173
175
  examples: ['$SKS show me available workflows']
174
176
  },
@@ -213,6 +215,7 @@ export const ROUTES = [
213
215
  context7Policy: 'not_required',
214
216
  reasoningPolicy: 'low',
215
217
  stopGate: 'none',
218
+ coverageExemptReason: 'single boolean project-config toggle, not a code-changing work order',
216
219
  cliEntrypoint: 'sks fast-mode on|off|status|clear [--json]',
217
220
  examples: ['$Fast-On', '$Fast-Off', '$Fast-Mode status']
218
221
  },
@@ -229,6 +232,7 @@ export const ROUTES = [
229
232
  context7Policy: 'not_required',
230
233
  reasoningPolicy: 'low',
231
234
  stopGate: 'none',
235
+ coverageExemptReason: 'single boolean project-config toggle, not a code-changing work order',
232
236
  cliEntrypoint: 'sks with-local-llm on|off|status|set-model [--json]',
233
237
  examples: ['$with-local-llm-on', '$with-local-llm-off', 'sks with-local-llm status --json']
234
238
  },
@@ -249,6 +253,7 @@ export const ROUTES = [
249
253
  context7Policy: 'optional',
250
254
  reasoningPolicy: 'high',
251
255
  stopGate: 'naruto-gate.json',
256
+ coverage_required: true,
252
257
  cliEntrypoint: 'sks team "task" [executor:5 reviewer:6 user:1] | sks team log|tail|watch|lane|status|event|message|open-zellij|attach-zellij|cleanup-zellij',
253
258
  examples: ['$Team executor:5 agree on the best plan and implement it', '$From-Chat-IMG 채팅+첨부 이미지 작업 지시서']
254
259
  },
@@ -265,6 +270,7 @@ export const ROUTES = [
265
270
  context7Policy: 'optional',
266
271
  reasoningPolicy: 'high',
267
272
  stopGate: 'naruto-gate.json',
273
+ coverage_required: true,
268
274
  cliEntrypoint: 'sks naruto run "task" [--clones N] [--backend codex-sdk|fake|ollama] [--parallel-write] | sks naruto status',
269
275
  examples: ['$Naruto run sweep the codebase for TODO comments with 50 clones', '$Work', '$Swarm "fix all lint errors"']
270
276
  },
@@ -353,6 +359,7 @@ export const ROUTES = [
353
359
  context7Policy: 'if_external_docs',
354
360
  reasoningPolicy: 'medium',
355
361
  stopGate: 'loop-graph-proof.json',
362
+ coverage_required: true,
356
363
  cliEntrypoint: 'sks goal create|pause|resume|clear|status',
357
364
  examples: ['$Goal persist this migration workflow with native /goal continuation']
358
365
  },
@@ -367,6 +374,7 @@ export const ROUTES = [
367
374
  context7Policy: 'not_required',
368
375
  reasoningPolicy: 'low',
369
376
  stopGate: 'none',
377
+ coverageExemptReason: 'packages already-made changes into one commit; does not itself decide what work to do',
370
378
  cliEntrypoint: 'sks commit [--message "msg"] [--json]',
371
379
  examples: ['$Commit 이번 작업 커밋해줘']
372
380
  },
@@ -381,6 +389,7 @@ export const ROUTES = [
381
389
  context7Policy: 'not_required',
382
390
  reasoningPolicy: 'low',
383
391
  stopGate: 'none',
392
+ coverageExemptReason: 'packages already-made changes into one commit and pushes; does not itself decide what work to do',
384
393
  cliEntrypoint: 'sks commit-and-push [--message "msg"] [--json]',
385
394
  examples: ['$Commit-And-Push 커밋하고 바로 푸쉬해줘']
386
395
  },
@@ -514,6 +523,7 @@ export const ROUTES = [
514
523
  context7Policy: 'optional',
515
524
  reasoningPolicy: 'medium',
516
525
  stopGate: 'none',
526
+ coverageExemptReason: 'single fixed maintenance action (refresh/pack/validate/prune), not a free-form work order',
517
527
  cliEntrypoint: 'sks wiki refresh|pack|validate|prune',
518
528
  examples: ['$Wiki refresh', '$Wiki prune and validate']
519
529
  },
@@ -528,6 +538,7 @@ export const ROUTES = [
528
538
  context7Policy: 'optional',
529
539
  reasoningPolicy: 'medium',
530
540
  stopGate: 'none',
541
+ coverageExemptReason: 'read-only help/discovery output; never writes or modifies code/files',
531
542
  cliEntrypoint: 'sks help',
532
543
  examples: ['$Help show available SKS commands']
533
544
  }
@@ -145,6 +145,10 @@ export async function checkStopGate(input) {
145
145
  missingFields.push('regression_test_added');
146
146
  if (bugfixMission && normalizedGate.evidence.regression_test_failed_before_fix !== true)
147
147
  missingFields.push('regression_test_failed_before_fix');
148
+ if (normalizedGate.evidence.required_coverage_passed === false)
149
+ missingFields.push('coverage_required_but_not_passed');
150
+ if ((normalizedGate.evidence.uncovered_required_count ?? 0) > 0)
151
+ missingFields.push(`work_order_uncovered_count:${normalizedGate.evidence.uncovered_required_count}`);
148
152
  if (normalizedGate.status === 'passed'
149
153
  && normalizedGate.passed === true
150
154
  && normalizedGate.blockers.length === 0
@@ -140,6 +140,10 @@ function evidenceMissingBlockers(evidence) {
140
140
  blockers.push('regression_test_not_added');
141
141
  if (evidence.regression_test_failed_before_fix === false)
142
142
  blockers.push('regression_test_failed_before_fix_missing');
143
+ if (evidence.required_coverage_passed === false)
144
+ blockers.push('coverage_required_but_not_passed');
145
+ if ((evidence.uncovered_required_count ?? 0) > 0)
146
+ blockers.push(`work_order_uncovered_count:${evidence.uncovered_required_count}`);
143
147
  return blockers;
144
148
  }
145
149
  //# sourceMappingURL=stop-gate-writer.js.map
@@ -54,7 +54,14 @@ export async function latestTrustReport(root, missionArg = 'latest') {
54
54
  return staleTrustReport(report, temporalIssues);
55
55
  }
56
56
  if (!proof) {
57
- return {
57
+ // Persist this computed report even though it's a blocked result: `trust report`
58
+ // is expected to leave a durable trust-report.json behind for anyone (a human or
59
+ // a later `sks trust report <mission>` call) to inspect, the same way the
60
+ // completion-proof-present path below does via writeTrustArtifactsForProof(). An
61
+ // in-memory-only "blocked" result that vanishes after this call returns is
62
+ // surprising and was the reason `sks trust report latest --json` could exit 0
63
+ // with a blocked status yet leave no artifact on disk.
64
+ const report = {
58
65
  schema: TRUST_REPORT_SCHEMA,
59
66
  ...trustKernelMetadata(),
60
67
  ok: false,
@@ -62,6 +69,8 @@ export async function latestTrustReport(root, missionArg = 'latest') {
62
69
  status: 'blocked',
63
70
  issues: ['completion_proof_missing']
64
71
  };
72
+ await writeJsonAtomic(trustReportPath(root, missionId), report).catch(() => undefined);
73
+ return report;
65
74
  }
66
75
  const rebuilt = await writeTrustArtifactsForProof(root, proof);
67
76
  if (!rebuilt) {
@@ -477,6 +477,13 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
477
477
  if (input.skipMigrationGate === true || commandSkipsMigrationGate(command)) {
478
478
  return { ...empty, ok: true, status: 'skipped', receipt: null, doctor: null, failed_stage_id: null, blockers: [], warnings: [`skip_migration_gate_command:${command}`] };
479
479
  }
480
+ // projectRoot() falls back to the raw cwd when no project marker is found.
481
+ // launchd-spawned callers (the SKS menu bar app) run with cwd=/ — there is
482
+ // no project to migrate there, and proceeding would mkdir /.sneakoscope and
483
+ // crash the whole command before it starts.
484
+ if (root === path.parse(root).root) {
485
+ return { ...empty, ok: true, status: 'skipped', receipt: null, doctor: null, failed_stage_id: null, blockers: [], warnings: ['no_project_workspace_at_filesystem_root'] };
486
+ }
480
487
  const [epoch, receipt] = await Promise.all([
481
488
  ensureInstallationEpoch('first-command-gate'),
482
489
  readProjectUpdateMigrationReceipt(root)
@@ -738,7 +745,14 @@ function delay(ms) {
738
745
  }
739
746
  async function withUpdateMigrationLock(root, base, fn, options = {}) {
740
747
  const lockPath = path.join(root, '.sneakoscope', 'update', 'migration.lock');
741
- await ensureDir(path.dirname(lockPath));
748
+ try {
749
+ await ensureDir(path.dirname(lockPath));
750
+ }
751
+ catch (err) {
752
+ // An unwritable root (read-only mount, cwd outside any workspace) must fail
753
+ // the gate with a reportable blocker, not crash the whole CLI dispatch.
754
+ return { ...base, ok: false, status: 'blocked', receipt: null, doctor: null, failed_stage_id: 'migration-lock', blockers: [`update_migration_lock_dir_unwritable:${err?.message || String(err)}`], warnings: [] };
755
+ }
742
756
  const recheck = options.recheck ?? null;
743
757
  const deadline = Date.now() + (options.maxWaitMs ?? MIGRATION_LOCK_WAIT_MS);
744
758
  let reapedStale = false;
@@ -1,5 +1,6 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
3
4
  import { PACKAGE_VERSION, packageRoot, readJson, runProcess, throttleLines, which } from './fsx.js';
4
5
  import { createRequestedScopeContract } from './safety/requested-scope-contract.js';
5
6
  import { guardedPackageInstall, guardContextForRoute } from './safety/mutation-guard.js';
@@ -351,7 +352,10 @@ export async function runSksUpdateNow(options = {}) {
351
352
  stage('project_receipt', migrationCurrent, migrationCurrent ? 'current' : 'failed', { root: projectReceiptRoot });
352
353
  if (migrationCurrent)
353
354
  sksMenuBar = await installUpdateSksMenuBar({ root: projectReceiptRoot, env, stage, quiet: machineOutput });
354
- await runUpdateGlobalSkillsReconcile(stage, { quiet: machineOutput });
355
+ await runUpdateGlobalSkillsReconcile(stage, {
356
+ quiet: machineOutput,
357
+ newPackageRoot: newBinary ? path.resolve(path.dirname(newBinary), '..', '..') : null
358
+ });
355
359
  }
356
360
  }
357
361
  const verification = await runFinalUpdateVerification({ installOk, newBinary, installVersion, env, projectReceiptRoot });
@@ -392,8 +396,46 @@ export async function runSksUpdateNow(options = {}) {
392
396
  });
393
397
  }
394
398
  async function runUpdateGlobalSkillsReconcile(stage, opts = {}) {
399
+ const targetDir = path.join(os.homedir(), '.agents', 'skills');
400
+ // reconcileSkills stamps ~/.agents/skills/.sks-generated.json with the
401
+ // PACKAGE_VERSION compiled into whichever module runs it. This function
402
+ // executes inside the OLD (driver) binary, so after a real version install
403
+ // an in-process reconcile would overwrite the manifest the new binary's
404
+ // migration doctor just wrote and make final self-verification report
405
+ // skills_manifest stale forever. Delegate to the freshly installed package.
406
+ if (opts.newPackageRoot) {
407
+ const moduleHref = pathToFileURL(path.join(opts.newPackageRoot, 'dist', 'core', 'init', 'skills.js')).href;
408
+ const script = [
409
+ `const m = await import(${JSON.stringify(moduleHref)});`,
410
+ `const r = await m.reconcileSkills({ targetDir: ${JSON.stringify(targetDir)}, scope: 'global', fix: true });`,
411
+ 'console.log(JSON.stringify(r));',
412
+ 'if (r && (r.ok === false || r.error)) process.exit(1);'
413
+ ].join('\n');
414
+ const work = runProcess(process.execPath, ['--input-type=module', '-e', script], {
415
+ timeoutMs: 120_000,
416
+ maxOutputBytes: 1024 * 1024
417
+ }).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
418
+ const run = opts.quiet ? await work : await withHeartbeat('skills reconcile', work, { warnAfterMs: 30_000 });
419
+ let parsed = null;
420
+ for (const line of String(run.stdout || '').trim().split('\n').reverse()) {
421
+ try {
422
+ parsed = JSON.parse(line);
423
+ break;
424
+ }
425
+ catch { /* not the JSON result line */ }
426
+ }
427
+ const ok = run.code === 0 && parsed?.ok !== false && !parsed?.error;
428
+ stage('global_skills_reconcile', ok, ok ? 'reconciled' : 'failed', {
429
+ via: 'new_package_binary',
430
+ installed: Array.isArray(parsed?.installed) ? parsed.installed.length : null,
431
+ updated: Array.isArray(parsed?.updated) ? parsed.updated.length : null,
432
+ removed: Array.isArray(parsed?.removed) ? parsed.removed.length : null,
433
+ error: ok ? null : parsed?.error || String(run.stderr || '').trim().slice(-400) || `exit_${run.code}`
434
+ });
435
+ return parsed || { schema: 'sks.skill-reconcile.v1', ok };
436
+ }
395
437
  const work = reconcileSkills({
396
- targetDir: path.join(os.homedir(), '.agents', 'skills'),
438
+ targetDir,
397
439
  scope: 'global',
398
440
  fix: true
399
441
  }).catch((err) => ({ schema: 'sks.skill-reconcile.v1', ok: false, error: err?.message || String(err) }));
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '5.3.0';
1
+ export const PACKAGE_VERSION = '5.5.1';
2
2
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { nowIso, readJson, writeJsonAtomic } from './fsx.js';
3
3
  import { ARTIFACT_FILES, validateWorkOrderLedger } from './artifact-schemas.js';
4
+ import { promptRequirementItems } from './questions.js';
4
5
  export function createWorkOrderLedger({ missionId = 'unassigned', route = 'team', requests = [], sourcesComplete = false } = {}) {
5
6
  const items = requests.map((request, index) => ({
6
7
  id: `WO-${String(index + 1).padStart(3, '0')}`,
@@ -38,6 +39,27 @@ export async function writeWorkOrderLedger(dir, ledger) {
38
39
  export async function readWorkOrderLedger(dir) {
39
40
  return readJson(path.join(dir, ARTIFACT_FILES.work_order_ledger), null);
40
41
  }
42
+ export function evaluateWorkOrderCoverage(ledger) {
43
+ if (!ledger)
44
+ return { ok: true, blockers: [], uncovered_count: 0 };
45
+ const blockers = [];
46
+ if (!ledger.source_inventory_complete)
47
+ blockers.push('work_order_inventory_truncated');
48
+ if (!ledger.all_customer_requests_mapped)
49
+ blockers.push('work_order_requests_not_mapped');
50
+ if (!ledger.all_work_items_verified)
51
+ blockers.push('work_order_items_not_verified');
52
+ let uncovered_count = 0;
53
+ for (const item of (ledger.items || [])) {
54
+ const isVerified = item.status === 'verified';
55
+ const isBlockedWithEvidence = item.status === 'blocked' && item.blocker?.blocked === true;
56
+ if (!isVerified && !isBlockedWithEvidence) {
57
+ uncovered_count += 1;
58
+ blockers.push(`work_order_uncovered:${item.id}:"${item.source?.verbatim?.slice(0, 40) || ''}"`);
59
+ }
60
+ }
61
+ return { ok: blockers.length === 0, blockers, uncovered_count };
62
+ }
41
63
  export function updateWorkOrderItem(ledger, id, patch = {}) {
42
64
  return {
43
65
  ...ledger,
@@ -51,4 +73,42 @@ export function updateWorkOrderItem(ledger, id, patch = {}) {
51
73
  })
52
74
  };
53
75
  }
76
+ /**
77
+ * Parses a free-form work-order prompt into WO-001..N ledger items (via
78
+ * promptRequirementItems) and persists the ledger at mission creation time,
79
+ * so every item is registered verbatim before any execution starts.
80
+ */
81
+ export async function createAndWriteWorkOrderLedgerForPrompt(dir, { missionId, route, prompt } = {}) {
82
+ const { items, truncated } = promptRequirementItems(String(prompt || ''));
83
+ const requests = items.map((item) => ({
84
+ type: 'chat_text',
85
+ verbatim: item.text,
86
+ location: `prompt:${item.id}`
87
+ }));
88
+ const ledger = createWorkOrderLedger({ missionId, route, requests, sourcesComplete: !truncated });
89
+ await writeWorkOrderLedger(dir, ledger);
90
+ return ledger;
91
+ }
92
+ /**
93
+ * Closes out every item in a mission's ledger to a terminal state once the
94
+ * route's own gate has decided ok/not-ok, so evaluateStop's coverage gate
95
+ * (which blocks stop while any item sits in 'pending') can never wait
96
+ * forever: an item is either verified (route succeeded) or honestly
97
+ * blocked (route failed, real blockers recorded), never left hanging.
98
+ * This is coarser than true per-item tracking, but it closes the loop.
99
+ */
100
+ export async function closeWorkOrderLedgerForRouteResult(dir, { ok, blockers = [] }) {
101
+ const ledger = await readWorkOrderLedger(dir);
102
+ if (!ledger || !Array.isArray(ledger.items) || ledger.items.length === 0)
103
+ return null;
104
+ let next = ledger;
105
+ for (const item of ledger.items) {
106
+ const patch = ok
107
+ ? { status: 'verified', implementation_tasks: item.implementation_tasks?.length ? item.implementation_tasks : ['route_completion'] }
108
+ : { status: 'blocked', blocker: { blocked: true, reason: blockers.join(', ') || 'route_completion_blocked', needed_to_unblock: 'resolve the route blockers and re-run' } };
109
+ next = updateWorkOrderItem(next, item.id, patch);
110
+ }
111
+ await writeWorkOrderLedger(dir, next);
112
+ return next;
113
+ }
54
114
  //# sourceMappingURL=work-order-ledger.js.map
@@ -26,9 +26,9 @@ const directCodexConfigWrites = scanDirectCodexConfigWrites();
26
26
  const commandGateContract = scanCommandGateContract();
27
27
  const directMkdtempCalls = scanDirectMkdtempCalls();
28
28
  const rustTempdirCalls = scanRustTempdirCalls();
29
- if (releaseGates.length > 200)
29
+ if (releaseGates.length > 220)
30
30
  blockers.push(`release_preset_gate_budget_exceeded:${releaseGates.length}`);
31
- if (gates.length > 200)
31
+ if (gates.length > 220)
32
32
  blockers.push(`release_manifest_gate_budget_exceeded:${gates.length}`);
33
33
  if (Object.keys(pkg.scripts || {}).length > 100)
34
34
  blockers.push(`package_script_budget_exceeded:${Object.keys(pkg.scripts || {}).length}`);
@@ -7,7 +7,13 @@ import { spawnSync } from 'node:child_process';
7
7
  import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
8
8
  const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 2100);
9
9
  const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 10 * 1024 * 1024);
10
- const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || 2300 * 1024);
10
+ // Raised from 2300 KiB after the 5.4.0 dollar-command hardening pass added
11
+ // src/core/feature-fixture-executor.ts and expanded several command modules
12
+ // (route-success-helpers.ts, seo-command.ts, ppt-command.ts, qa-loop-command.ts,
13
+ // research-command.ts, image-ux-review-command.ts, mad-sks-command.ts,
14
+ // sks-menubar.ts) with genuine new production logic, pushing the packed size
15
+ // to ~2306 KiB. Kept modest headroom rather than a large jump.
16
+ const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || 2340 * 1024);
11
17
  function runNpmPack() {
12
18
  const npmCli = process.env.npm_execpath; // set when invoked via `npm run`
13
19
  const npmCache = process.env.SKS_RELEASE_NPM_CACHE || path.join(os.tmpdir(), 'sneakoscope-npm-cache');
@@ -42,7 +42,7 @@ const schemaComplete = [...releaseManifest.gates, ...harnessManifest.gates].ever
42
42
  const report = {
43
43
  schema: 'sks.release-dag-full-coverage-check.v2',
44
44
  ok: schemaComplete
45
- && releasePreset.length <= 200
45
+ && releasePreset.length <= 220
46
46
  && missingRequiredReleasePreset.length === 0
47
47
  && missingRequiredHarnessPreset.length === 0
48
48
  && duplicateAcrossManifests.length === 0
@@ -42,7 +42,7 @@ const releaseCheckTarget = releaseCheck.includes('release:check:affected')
42
42
  ? String(scripts['release:check:affected'] || '')
43
43
  : releaseCheck;
44
44
  assertGate(releaseCheckTarget.includes('release-gate-dag-runner') && /--preset\s+(?:release|affected)/.test(releaseCheckTarget), 'release:check must use the v2 DAG release/affected preset', { release_check: scripts['release:check'], resolved_release_check: releaseCheckTarget });
45
- assertGate(releaseGates.length > 0 && releaseGates.length <= 200, 'release v2 manifest must include 1..200 release gates', { gate_count: releaseGates.length });
45
+ assertGate(releaseGates.length > 0 && releaseGates.length <= 220, 'release v2 manifest must include 1..220 release gates', { gate_count: releaseGates.length });
46
46
  const PACKAGE_SCRIPT_BUDGET = 150;
47
47
  assertGate(Object.keys(scripts).length <= PACKAGE_SCRIPT_BUDGET, 'package script budget exceeded', { script_count: Object.keys(scripts).length, limit: PACKAGE_SCRIPT_BUDGET });
48
48
  for (const id of requiredRelease)
@@ -27,7 +27,7 @@ const harnessNonZellij = harnessIds.filter((id) => !id.startsWith('zellij:'));
27
27
  const releasePresetLeaks = release.gates.filter((gate) => !Array.isArray(gate.preset) || !gate.preset.includes('release')).map((gate) => gate.id);
28
28
  const harnessPresetLeaks = harness.gates.filter((gate) => !Array.isArray(gate.preset) || !gate.preset.includes('harness') || gate.preset.includes('release')).map((gate) => gate.id);
29
29
  const npmRunGates = release.gates.concat(harness.gates).filter((gate) => /\bnpm\s+run\b/.test(String(gate.command))).map((gate) => gate.id);
30
- assertGate(release.gates.length <= 200, 'release preset gate budget exceeded', { release_gates: release.gates.length, limit: 200 });
30
+ assertGate(release.gates.length <= 220, 'release preset gate budget exceeded', { release_gates: release.gates.length, limit: 220 });
31
31
  assertGate(Object.keys(scripts).length <= PACKAGE_SCRIPT_BUDGET, 'package.json script budget exceeded', { scripts: Object.keys(scripts).length, limit: PACKAGE_SCRIPT_BUDGET });
32
32
  assertGate(duplicateAcrossManifests.length === 0, 'gate appears in both release and harness manifests', { duplicateAcrossManifests });
33
33
  assertGate(releaseZellij.length === 0, 'zellij gates must live in infra-harness-gates.json, not release-gates.v2.json', { releaseZellij });
@@ -370,7 +370,7 @@ assertGate(releaseCheckScript.startsWith('npm run release:check:parallel')
370
370
  || releaseCheckScript.includes('release:check:affected'), 'release:check must use release:check:parallel, release:check:affected, or the release gate DAG runner');
371
371
  assertGate(releaseManifest?.schema === 'sks.release-gates.v2', 'release gate manifest schema mismatch', { schema: releaseManifest?.schema || null });
372
372
  assertGate(harnessManifest?.schema === 'sks.infra-harness-gates.v1', 'infra harness manifest schema mismatch', { schema: harnessManifest?.schema || null });
373
- assertGate(releaseGates.length > 0 && releaseGates.length <= 200, 'release v2 manifest must include 1..200 release gates', { release_gates: releaseGates.length });
373
+ assertGate(releaseGates.length > 0 && releaseGates.length <= 220, 'release v2 manifest must include 1..220 release gates', { release_gates: releaseGates.length });
374
374
  assertGate(harnessGates.length > 0, 'infra harness manifest must include harness gates', { harness_gates: harnessGates.length });
375
375
  const PACKAGE_SCRIPT_BUDGET = 100;
376
376
  assertGate(Object.keys(pkg.scripts || {}).length <= PACKAGE_SCRIPT_BUDGET, 'package script budget exceeded', { script_count: Object.keys(pkg.scripts || {}).length, limit: PACKAGE_SCRIPT_BUDGET });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "5.3.0",
4
+ "version": "5.5.1",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -63,6 +63,8 @@
63
63
  "build": "npm run build:clean",
64
64
  "build:clean": "node -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('.sneakoscope/cache/tsbuildinfo',{recursive:true,force:true})\" && tsc -p tsconfig.json && node ./dist/scripts/ensure-bin-executable.js && node ./dist/scripts/build-dist.js",
65
65
  "build:incremental": "tsc -p tsconfig.json && node ./dist/scripts/ensure-bin-executable.js && node ./dist/scripts/build-dist.js",
66
+ "test": "node --test --test-concurrency=1 dist/core/__tests__/*.test.js dist/core/proof/__tests__/*.test.js dist/core/stop-gate/__tests__/*.test.js dist/core/commands/__tests__/*.test.js dist/core/dfix/__tests__/*.test.js dist/core/ppt/__tests__/*.test.js dist/core/mad-sks/__tests__/*.test.js dist/core/codex-app/__tests__/*.test.js dist/core/doctor/__tests__/*.test.js dist/cli/__tests__/*.test.js",
67
+ "selftest:real": "node ./dist/bin/sks.js selftest --real --json",
66
68
  "check": "npm run release:check:affected",
67
69
  "dev:sks": "npm run build:incremental --silent && node ./dist/bin/sks.js",
68
70
  "prepublishOnly": "npm run release:check:affected",
@@ -73,6 +75,8 @@
73
75
  "release:check": "npm run release:check:affected --silent",
74
76
  "release:metadata": "node ./dist/scripts/release-metadata-check.js",
75
77
  "release:gate-script-parity": "node ./dist/scripts/release-gate-script-parity-check.js",
78
+ "ppt:real-imagegen-wiring": "node ./dist/scripts/ppt-real-imagegen-wiring-check.js",
79
+ "ux-review:run-wires-imagegen": "node ./dist/scripts/ux-review-run-wires-imagegen-check.js",
76
80
  "release:check:parallel": "npm run build --silent && node ./dist/scripts/release-parallel-check.js && node ./dist/scripts/release-check-stamp.js write",
77
81
  "release:check:legacy": "npm run release:check:parallel --silent",
78
82
  "release:dag-full-coverage": "node ./dist/scripts/release-dag-full-coverage-check.js",
@@ -24,6 +24,7 @@
24
24
  "side_effect": { "enum": ["hermetic", "real-env"] },
25
25
  "timeout_ms": { "type": "integer", "minimum": 1 },
26
26
  "output_contract": { "const": "sks.gate-result.v1" },
27
+ "contract_note": { "type": "string", "minLength": 1 },
27
28
  "cache": {
28
29
  "type": "object",
29
30
  "required": ["enabled", "inputs"],