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.
- package/README.md +4 -0
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/install-helpers.js +17 -0
- package/dist/config/skills-manifest.json +58 -58
- package/dist/core/agents/agent-orchestrator.js +14 -2
- package/dist/core/agents/agent-proof-evidence.js +16 -0
- package/dist/core/bench.js +24 -2
- package/dist/core/codex-app/sks-menubar.js +83 -10
- package/dist/core/commands/agent-command.js +35 -1
- package/dist/core/commands/basic-cli.js +59 -0
- package/dist/core/commands/command-utils.js +14 -4
- package/dist/core/commands/goal-command.js +3 -0
- package/dist/core/commands/image-ux-review-command.js +4 -1
- package/dist/core/commands/mad-sks-command.js +10 -1
- package/dist/core/commands/naruto-command.js +12 -7
- package/dist/core/commands/ppt-command.js +40 -1
- package/dist/core/commands/qa-loop-command.js +4 -1
- package/dist/core/commands/release-command.js +3 -3
- package/dist/core/commands/research-command.js +4 -0
- package/dist/core/commands/route-success-helpers.js +2 -0
- package/dist/core/commands/run-command.js +22 -5
- package/dist/core/commands/seo-command.js +31 -4
- package/dist/core/db-safety.js +8 -1
- package/dist/core/feature-fixture-executor.js +262 -0
- package/dist/core/feature-fixtures.js +95 -52
- package/dist/core/fsx.js +1 -1
- package/dist/core/loops/loop-decomposer.js +4 -3
- package/dist/core/loops/loop-planner.js +7 -3
- package/dist/core/loops/loop-worker-runtime.js +6 -0
- package/dist/core/pipeline-internals/runtime-gates.js +39 -3
- package/dist/core/proof/selftest-proof-fixtures.js +8 -0
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +18 -1
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +28 -12
- package/dist/core/questions.js +12 -10
- package/dist/core/release/release-gate-node.js +2 -0
- package/dist/core/routes.js +11 -0
- package/dist/core/stop-gate/stop-gate-check.js +4 -0
- package/dist/core/stop-gate/stop-gate-writer.js +4 -0
- package/dist/core/trust-kernel/trust-report.js +10 -1
- package/dist/core/update/update-migration-state.js +15 -1
- package/dist/core/update-check.js +44 -2
- package/dist/core/version.js +1 -1
- package/dist/core/work-order-ledger.js +60 -0
- package/dist/scripts/gate-policy-audit-check.js +2 -2
- package/dist/scripts/packlist-performance-check.js +7 -1
- package/dist/scripts/release-dag-full-coverage-check.js +1 -1
- package/dist/scripts/release-gate-existence-audit.js +1 -1
- package/dist/scripts/release-gate-planner.js +1 -1
- package/dist/scripts/release-metadata-1-19-check.js +1 -1
- package/package.json +5 -1
- package/schemas/release/release-gate-node.schema.json +1 -0
package/dist/core/routes.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
|
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) }));
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '5.
|
|
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 >
|
|
29
|
+
if (releaseGates.length > 220)
|
|
30
30
|
blockers.push(`release_preset_gate_budget_exceeded:${releaseGates.length}`);
|
|
31
|
-
if (gates.length >
|
|
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
|
-
|
|
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 <=
|
|
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 <=
|
|
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 <=
|
|
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 <=
|
|
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.
|
|
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"],
|