sneakoscope 3.1.13 → 4.0.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 (53) hide show
  1. package/README.md +28 -3
  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/command-registry.js +6 -13
  7. package/dist/commands/doctor.js +29 -2
  8. package/dist/commands/proof.js +8 -0
  9. package/dist/core/agents/agent-cleanup-executor.js +75 -1
  10. package/dist/core/agents/agent-command-surface.js +29 -4
  11. package/dist/core/agents/agent-orchestrator.js +25 -6
  12. package/dist/core/agents/native-cli-session-swarm.js +1 -1
  13. package/dist/core/build/build-once-runner.js +62 -0
  14. package/dist/core/codex/codex-config-readability.js +52 -38
  15. package/dist/core/commands/check-command.js +130 -0
  16. package/dist/core/commands/daemon-command.js +14 -0
  17. package/dist/core/commands/local-model-command.js +1 -1
  18. package/dist/core/commands/mad-sks-command.js +18 -1
  19. package/dist/core/commands/naruto-command.js +14 -5
  20. package/dist/core/commands/release-command.js +52 -0
  21. package/dist/core/commands/task-command.js +15 -0
  22. package/dist/core/commands/triwiki-command.js +38 -0
  23. package/dist/core/daemon/sksd-client.js +9 -0
  24. package/dist/core/daemon/sksd.js +55 -0
  25. package/dist/core/doctor/doctor-codex-startup-repair.js +3 -2
  26. package/dist/core/doctor/doctor-dirty-planner.js +30 -0
  27. package/dist/core/doctor/doctor-transaction.js +13 -0
  28. package/dist/core/feature-fixtures.js +1 -0
  29. package/dist/core/fsx.js +1 -1
  30. package/dist/core/init.js +7 -1
  31. package/dist/core/probes/probe-memoization.js +42 -0
  32. package/dist/core/release/extreme-parallel-scheduler.js +33 -0
  33. package/dist/core/release/gate-pack-manifest.js +118 -0
  34. package/dist/core/release/gate-pack-runner.js +113 -0
  35. package/dist/core/release/release-gate-cache-v2.js +73 -16
  36. package/dist/core/release/release-gate-dag.js +81 -2
  37. package/dist/core/release/resource-class-budget.js +22 -0
  38. package/dist/core/release/sla-scheduler.js +22 -0
  39. package/dist/core/routes.js +5 -0
  40. package/dist/core/triwiki/triwiki-affected-graph.js +56 -0
  41. package/dist/core/triwiki/triwiki-cache-key.js +221 -0
  42. package/dist/core/triwiki/triwiki-gate-impact-map.js +79 -0
  43. package/dist/core/triwiki/triwiki-module-card.js +37 -0
  44. package/dist/core/triwiki/triwiki-proof-bank.js +132 -0
  45. package/dist/core/triwiki/triwiki-proof-card.js +42 -0
  46. package/dist/core/triwiki/triwiki-sla-certificate.js +30 -0
  47. package/dist/core/version.js +1 -1
  48. package/dist/core/zellij/zellij-worker-pane-manager.js +3 -2
  49. package/dist/scripts/fixtures/fake-codex-config-loader.js +12 -1
  50. package/dist/scripts/release-4000-required-gates.js +36 -0
  51. package/dist/scripts/release-gate-dag-runner.js +18 -0
  52. package/dist/scripts/release-speed-summary.js +9 -0
  53. package/package.json +43 -7
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { hashJson } from '../triwiki/triwiki-cache-key.js';
4
+ export const PROBE_MEMOIZATION_SCHEMA = 'sks.probe-memoization.v1';
5
+ export function memoizeProbe(input) {
6
+ const key = hashJson({
7
+ probe_id: input.probeId,
8
+ version: input.version,
9
+ env: (input.envAllowlist || []).sort().map((name) => ({ name, present: process.env[name] !== undefined }))
10
+ });
11
+ const file = probeCachePath(input.root, key);
12
+ const existing = readProbeRecord(file);
13
+ if (existing && new Date(existing.expires_at).getTime() > Date.now())
14
+ return { value: existing.value, reused: true, key };
15
+ const value = input.run();
16
+ const record = {
17
+ schema: PROBE_MEMOIZATION_SCHEMA,
18
+ key,
19
+ probe_id: input.probeId,
20
+ created_at: new Date().toISOString(),
21
+ expires_at: new Date(Date.now() + input.ttlMs).toISOString(),
22
+ value
23
+ };
24
+ fs.mkdirSync(path.dirname(file), { recursive: true });
25
+ fs.writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`);
26
+ return { value, reused: false, key };
27
+ }
28
+ function readProbeRecord(file) {
29
+ try {
30
+ if (!fs.existsSync(file))
31
+ return null;
32
+ const json = JSON.parse(fs.readFileSync(file, 'utf8'));
33
+ return json.schema === PROBE_MEMOIZATION_SCHEMA ? json : null;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function probeCachePath(root, key) {
40
+ return path.join(root, '.sneakoscope', 'cache', 'probes', `${key}.json`);
41
+ }
42
+ //# sourceMappingURL=probe-memoization.js.map
@@ -0,0 +1,33 @@
1
+ import { buildGatePackManifest } from './gate-pack-manifest.js';
2
+ import { computeResourceClassBudget } from './resource-class-budget.js';
3
+ export const EXTREME_PARALLEL_SCHEDULER_SCHEMA = 'sks.extreme-parallel-scheduler.v1';
4
+ export function planExtremeParallelSchedule(root, graph, budget = computeResourceClassBudget()) {
5
+ const manifest = buildGatePackManifest(root);
6
+ const selectedIds = new Set(graph?.gate_packs && graph.gate_packs.length ? graph.gate_packs : manifest.packs.map((pack) => pack.id));
7
+ const packs = manifest.packs.filter((pack) => selectedIds.has(pack.id)).sort((a, b) => b.estimated_ms - a.estimated_ms);
8
+ const laneCount = Math.max(1, Math.min(packs.length || 1, Math.max(4, budget.cpu_light)));
9
+ const lanes = Array.from({ length: laneCount }, () => ({ packs: [], estimated_ms: 0 }));
10
+ for (const pack of packs) {
11
+ const target = lanes.reduce((best, lane) => lane.estimated_ms < best.estimated_ms ? lane : best, lanes[0]);
12
+ target.packs.push(pack.id);
13
+ target.estimated_ms += pack.estimated_ms;
14
+ }
15
+ const batches = lanes
16
+ .filter((lane) => lane.packs.length > 0)
17
+ .map((lane, index) => ({ batch: index + 1, packs: lane.packs, estimated_ms: lane.estimated_ms }));
18
+ const sequential = packs.reduce((sum, pack) => sum + pack.estimated_ms, 0);
19
+ const critical = batches.reduce((max, batch) => Math.max(max, batch.estimated_ms), 0);
20
+ const ratio = sequential <= 0 ? 1 : critical / sequential;
21
+ const blockers = ratio <= 0.3 || packs.length <= 1 ? [] : ['critical_path_reduction_below_70_percent'];
22
+ return {
23
+ schema: EXTREME_PARALLEL_SCHEDULER_SCHEMA,
24
+ ok: blockers.length === 0,
25
+ batches,
26
+ sequential_ms: sequential,
27
+ critical_path_ms: critical,
28
+ reduction_ratio: Number(ratio.toFixed(4)),
29
+ budget,
30
+ blockers
31
+ };
32
+ }
33
+ //# sourceMappingURL=extreme-parallel-scheduler.js.map
@@ -0,0 +1,118 @@
1
+ import { buildTriWikiGateImpactMap } from '../triwiki/triwiki-gate-impact-map.js';
2
+ export const GATE_PACK_MANIFEST_SCHEMA = 'sks.gate-pack-manifest.v1';
3
+ export const REQUIRED_GATE_PACK_IDS = [
4
+ 'release-parity',
5
+ 'codex-0140',
6
+ 'doctor-production',
7
+ 'startup-mcp',
8
+ 'native-capability',
9
+ 'secret',
10
+ 'core-skill',
11
+ 'skill-dedupe',
12
+ 'zellij',
13
+ 'loop-mesh',
14
+ 'qa-research-image',
15
+ 'triwiki'
16
+ ];
17
+ export function buildGatePackManifest(root, gates) {
18
+ const impactMap = buildTriWikiGateImpactMap(root);
19
+ const byPack = new Map();
20
+ for (const pack of REQUIRED_GATE_PACK_IDS)
21
+ byPack.set(pack, []);
22
+ for (const impact of impactMap.impacts) {
23
+ const ids = byPack.get(impact.gate_pack) || [];
24
+ ids.push(impact.gate_id);
25
+ byPack.set(impact.gate_pack, ids);
26
+ }
27
+ for (const gate of gates || []) {
28
+ const pack = packForGateId(gate.id);
29
+ const ids = byPack.get(pack) || [];
30
+ if (!ids.includes(gate.id))
31
+ ids.push(gate.id);
32
+ byPack.set(pack, ids);
33
+ }
34
+ return {
35
+ schema: GATE_PACK_MANIFEST_SCHEMA,
36
+ root,
37
+ packs: REQUIRED_GATE_PACK_IDS.map((id) => ({
38
+ id,
39
+ description: descriptionForPack(id),
40
+ max_parallel: maxParallelForPack(id),
41
+ estimated_ms: estimatedMsForPack(id),
42
+ resource_classes: resourceClassesForPack(id),
43
+ gate_ids: [...new Set(byPack.get(id) || [])].sort()
44
+ }))
45
+ };
46
+ }
47
+ export function packForGateId(id) {
48
+ if (id.startsWith('triwiki:'))
49
+ return 'triwiki';
50
+ if (id.startsWith('codex:') || id.includes('0140'))
51
+ return 'codex-0140';
52
+ if (id.startsWith('doctor:'))
53
+ return 'doctor-production';
54
+ if (id.startsWith('sksd:') || id.startsWith('probes:') || id.includes('mcp'))
55
+ return 'startup-mcp';
56
+ if (id.includes('native') || id.startsWith('agent:'))
57
+ return 'native-capability';
58
+ if (id.startsWith('secret:') || id.includes('secret'))
59
+ return 'secret';
60
+ if (id.startsWith('core-skill:'))
61
+ return 'core-skill';
62
+ if (id.includes('skill-dedupe') || id.startsWith('skill:'))
63
+ return 'skill-dedupe';
64
+ if (id.includes('zellij') || id.startsWith('legacy:') || id.startsWith('orphan:'))
65
+ return 'zellij';
66
+ if (id.startsWith('loop:'))
67
+ return 'loop-mesh';
68
+ if (id.startsWith('qa-') || id.startsWith('research:') || id.startsWith('image:'))
69
+ return 'qa-research-image';
70
+ return 'release-parity';
71
+ }
72
+ function descriptionForPack(id) {
73
+ const descriptions = {
74
+ 'release-parity': 'Version, DAG, parity, and package release gates.',
75
+ 'codex-0140': 'Codex 0.140 capability and integration checks.',
76
+ 'doctor-production': 'Doctor repair and production safety checks.',
77
+ 'startup-mcp': 'Startup, MCP, daemon, and probe readiness checks.',
78
+ 'native-capability': 'Native agent and desktop capability checks.',
79
+ secret: 'Secret-preservation and redaction checks.',
80
+ 'core-skill': 'Immutable core skill checks.',
81
+ 'skill-dedupe': 'Skill duplication and inventory checks.',
82
+ zellij: 'Zellij and removed legacy runtime checks.',
83
+ 'loop-mesh': 'Loop mesh runtime checks.',
84
+ 'qa-research-image': 'QA, research, and image route checks.',
85
+ triwiki: 'TriWiki proof bank and affected graph checks.'
86
+ };
87
+ return descriptions[id] || id;
88
+ }
89
+ function maxParallelForPack(id) {
90
+ if (id === 'secret' || id === 'zellij')
91
+ return 1;
92
+ if (id === 'qa-research-image' || id === 'codex-0140')
93
+ return 2;
94
+ return 4;
95
+ }
96
+ function estimatedMsForPack(id) {
97
+ if (id === 'release-parity')
98
+ return 25_000;
99
+ if (id === 'triwiki')
100
+ return 12_000;
101
+ if (id === 'doctor-production')
102
+ return 18_000;
103
+ if (id === 'qa-research-image')
104
+ return 30_000;
105
+ return 15_000;
106
+ }
107
+ function resourceClassesForPack(id) {
108
+ if (id === 'secret')
109
+ return ['fs-read'];
110
+ if (id === 'zellij')
111
+ return ['zellij-real'];
112
+ if (id === 'qa-research-image')
113
+ return ['cpu-heavy', 'io-heavy'];
114
+ if (id === 'codex-0140')
115
+ return ['remote-model-real'];
116
+ return ['cpu-light', 'fs-read'];
117
+ }
118
+ //# sourceMappingURL=gate-pack-manifest.js.map
@@ -0,0 +1,113 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { computeTriWikiCacheKey } from '../triwiki/triwiki-cache-key.js';
5
+ import { createTriWikiProofCard } from '../triwiki/triwiki-proof-card.js';
6
+ import { readReusableTriWikiProofCard, writeTriWikiProofCard } from '../triwiki/triwiki-proof-bank.js';
7
+ import { buildGatePackManifest } from './gate-pack-manifest.js';
8
+ import { loadPackageScripts, loadReleaseGateManifest } from '../triwiki/triwiki-gate-impact-map.js';
9
+ export const GATE_PACK_RUNNER_SCHEMA = 'sks.gate-pack-runner.v1';
10
+ export function runGatePack(input) {
11
+ const manifest = buildGatePackManifest(input.root);
12
+ const pack = manifest.packs.find((candidate) => candidate.id === input.packId);
13
+ if (!pack) {
14
+ return { schema: GATE_PACK_RUNNER_SCHEMA, ok: false, root: input.root, pack_id: input.packId, mode: input.execute ? 'execute' : 'plan', reused: 0, executed: 0, failed: 0, proof_paths: [], blockers: ['pack_missing'] };
15
+ }
16
+ const gates = loadReleaseGateManifest(input.root).gates.filter((gate) => pack.gate_ids.includes(gate.id));
17
+ const scripts = loadPackageScripts(input.root);
18
+ const blockers = [];
19
+ const proofPaths = [];
20
+ let reused = 0;
21
+ let executed = 0;
22
+ let failed = 0;
23
+ for (const gate of gates) {
24
+ if (!input.execute)
25
+ continue;
26
+ const cacheKey = computeTriWikiCacheKey({
27
+ root: input.root,
28
+ id: gate.id,
29
+ inputs: gate.cache.inputs,
30
+ implementationFiles: [`src/scripts/${scriptFileForCommand(gate.command) || ''}`].filter(Boolean),
31
+ envAllowlist: ['CI', 'SKS_FAST_MODE', 'SKS_RELEASE_PRESET'],
32
+ fixtureVersion: 'sks-4.0.0'
33
+ });
34
+ const hit = readReusableTriWikiProofCard({ root: input.root, subjectId: gate.id, cacheKey: cacheKey.key });
35
+ if (hit.hit) {
36
+ reused += 1;
37
+ if (hit.path)
38
+ proofPaths.push(hit.path);
39
+ continue;
40
+ }
41
+ const scriptName = scriptNameForCommand(gate.command);
42
+ if (!scriptName || !scripts[scriptName]) {
43
+ failed += 1;
44
+ blockers.push(`script_missing:${gate.id}`);
45
+ continue;
46
+ }
47
+ const started = Date.now();
48
+ const run = spawnSync('npm', ['run', scriptName, '--silent'], {
49
+ cwd: input.root,
50
+ encoding: 'utf8',
51
+ maxBuffer: 1024 * 1024 * 10,
52
+ env: { ...process.env, ...(input.env || {}), CI: process.env.CI || 'true' }
53
+ });
54
+ executed += 1;
55
+ const passed = run.status === 0;
56
+ if (!passed) {
57
+ failed += 1;
58
+ blockers.push(`gate_failed:${gate.id}`);
59
+ }
60
+ const card = createTriWikiProofCard({
61
+ subject_type: 'gate',
62
+ subject_id: gate.id,
63
+ cache_key: cacheKey.key,
64
+ input_hash: cacheKey.input_hash,
65
+ implementation_hash: cacheKey.implementation_hash,
66
+ tool_version: cacheKey.tool_version,
67
+ fixture_version: cacheKey.fixture_version,
68
+ result: passed ? 'passed' : 'failed',
69
+ reusable: passed,
70
+ duration_ms: Math.max(0, Date.now() - started),
71
+ evidence: {
72
+ status: run.status,
73
+ stdout_tail: tail(String(run.stdout || '')),
74
+ stderr_tail: tail(String(run.stderr || ''))
75
+ },
76
+ invalidation_reasons: passed ? [] : ['gate_failed']
77
+ });
78
+ proofPaths.push(writeTriWikiProofCard(input.root, card));
79
+ }
80
+ const report = {
81
+ schema: GATE_PACK_RUNNER_SCHEMA,
82
+ ok: blockers.length === 0,
83
+ root: input.root,
84
+ pack_id: input.packId,
85
+ mode: input.execute ? 'execute' : 'plan',
86
+ reused,
87
+ executed,
88
+ failed,
89
+ proof_paths: proofPaths,
90
+ blockers
91
+ };
92
+ writeGatePackReport(input.root, report);
93
+ return report;
94
+ }
95
+ function writeGatePackReport(root, report) {
96
+ const file = path.join(root, '.sneakoscope', 'reports', 'gate-pack-runner.json');
97
+ fs.mkdirSync(path.dirname(file), { recursive: true });
98
+ fs.writeFileSync(file, `${JSON.stringify(report, null, 2)}\n`);
99
+ }
100
+ function scriptNameForCommand(command) {
101
+ const match = command.match(/^npm run ([^ ]+)/);
102
+ return match?.[1] || null;
103
+ }
104
+ function scriptFileForCommand(command) {
105
+ const script = scriptNameForCommand(command);
106
+ if (!script)
107
+ return null;
108
+ return `${script.replace(/[:]/g, '-')}${script.includes('blackbox') ? '' : '-check'}.ts`;
109
+ }
110
+ function tail(value, limit = 2000) {
111
+ return value.length > limit ? value.slice(-limit) : value;
112
+ }
113
+ //# sourceMappingURL=gate-pack-runner.js.map
@@ -2,10 +2,16 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import crypto from 'node:crypto';
4
4
  import { normalizeReleaseCacheInputForBehavior } from './release-cache-key.js';
5
+ import { computeTriWikiCacheKey } from '../triwiki/triwiki-cache-key.js';
6
+ import { createTriWikiProofCard } from '../triwiki/triwiki-proof-card.js';
7
+ import { readReusableTriWikiProofCard, writeTriWikiProofCard } from '../triwiki/triwiki-proof-bank.js';
5
8
  export const RELEASE_GATE_CACHE_V2_SCHEMA = 'sks.release-gate-cache.v2';
6
9
  export function releaseGateCacheFile(root) {
7
10
  return path.join(root, '.sneakoscope', 'reports', 'release-gates', 'cache-v2.json');
8
11
  }
12
+ export function releaseGateProofBankFile(root) {
13
+ return path.join(root, '.sneakoscope', 'proof-bank', 'gates', 'cache-v2.json');
14
+ }
9
15
  // Files whose only release-to-release difference is the version literal.
10
16
  // Hashing them version-neutrally keeps a pure `sks versioning bump` from
11
17
  // invalidating every behavior gate: bumping the version rewrites
@@ -117,11 +123,23 @@ export function readReleaseGateCacheHit(root, gate) {
117
123
  return Boolean(readReleaseGateCacheRecord(root, gate));
118
124
  }
119
125
  export function readReleaseGateCacheRecord(root, gate) {
120
- try {
121
- const parsed = JSON.parse(fs.readFileSync(releaseGateCacheFile(root), 'utf8'));
122
- const record = parsed.schema === RELEASE_GATE_CACHE_V2_SCHEMA ? parsed.records?.[releaseGateCacheKey(root, gate)] : null;
123
- if (record?.ok !== true)
124
- return null;
126
+ const key = releaseGateCacheKey(root, gate);
127
+ const proof = readReusableTriWikiProofCard({ root, subjectId: gate.id, cacheKey: key });
128
+ if (proof.hit && proof.card) {
129
+ return {
130
+ ok: true,
131
+ gate_id: gate.id,
132
+ command: gate.command,
133
+ resource: gate.resource,
134
+ preset: gate.preset,
135
+ duration_ms: Math.max(0, Math.floor(Number(proof.card.duration_ms) || 0)),
136
+ recorded_at: proof.card.created_at
137
+ };
138
+ }
139
+ for (const file of [releaseGateProofBankFile(root), releaseGateCacheFile(root)]) {
140
+ const record = readCacheRecord(file, key);
141
+ if (!record || record.ok !== true)
142
+ continue;
125
143
  return {
126
144
  ok: true,
127
145
  gate_id: String(record.gate_id || gate.id),
@@ -132,12 +150,59 @@ export function readReleaseGateCacheRecord(root, gate) {
132
150
  recorded_at: String(record.recorded_at || '')
133
151
  };
134
152
  }
153
+ return null;
154
+ }
155
+ export function writeReleaseGateCacheHit(root, gate, durationMs = 0) {
156
+ const key = releaseGateCacheKey(root, gate);
157
+ const record = {
158
+ ok: true,
159
+ gate_id: gate.id,
160
+ command: gate.command,
161
+ resource: gate.resource,
162
+ preset: gate.preset,
163
+ duration_ms: Math.max(0, Math.floor(Number(durationMs) || 0)),
164
+ recorded_at: new Date().toISOString()
165
+ };
166
+ writeCacheRecord(releaseGateCacheFile(root), key, record);
167
+ writeCacheRecord(releaseGateProofBankFile(root), key, record);
168
+ const triKey = computeTriWikiCacheKey({
169
+ root,
170
+ id: gate.id,
171
+ inputs: gate.cache.inputs,
172
+ implementationFiles: ['release-gates.v2.json', `src/scripts/${gate.id.replace(/[:]/g, '-')}-check.ts`],
173
+ envAllowlist: ['CI', 'SKS_FAST_MODE', 'SKS_RELEASE_PRESET'],
174
+ fixtureVersion: 'sks-4.0.0',
175
+ salt: key
176
+ });
177
+ writeTriWikiProofCard(root, createTriWikiProofCard({
178
+ subject_type: 'gate',
179
+ subject_id: gate.id,
180
+ cache_key: key,
181
+ input_hash: triKey.input_hash,
182
+ implementation_hash: triKey.implementation_hash,
183
+ tool_version: triKey.tool_version,
184
+ fixture_version: triKey.fixture_version,
185
+ result: 'passed',
186
+ reusable: true,
187
+ duration_ms: Math.max(0, Math.floor(Number(durationMs) || 0)),
188
+ evidence: {
189
+ command: gate.command,
190
+ cache_key_schema: 'release-cache-v2-compatible',
191
+ triwiki_key: triKey.key
192
+ },
193
+ invalidation_reasons: []
194
+ }));
195
+ }
196
+ function readCacheRecord(file, key) {
197
+ try {
198
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
199
+ return parsed.schema === RELEASE_GATE_CACHE_V2_SCHEMA ? parsed.records?.[key] || null : null;
200
+ }
135
201
  catch {
136
202
  return null;
137
203
  }
138
204
  }
139
- export function writeReleaseGateCacheHit(root, gate, durationMs = 0) {
140
- const file = releaseGateCacheFile(root);
205
+ function writeCacheRecord(file, key, record) {
141
206
  let parsed = { schema: RELEASE_GATE_CACHE_V2_SCHEMA, records: {} };
142
207
  try {
143
208
  parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -145,15 +210,7 @@ export function writeReleaseGateCacheHit(root, gate, durationMs = 0) {
145
210
  catch { }
146
211
  parsed.schema = RELEASE_GATE_CACHE_V2_SCHEMA;
147
212
  parsed.records ||= {};
148
- parsed.records[releaseGateCacheKey(root, gate)] = {
149
- ok: true,
150
- gate_id: gate.id,
151
- command: gate.command,
152
- resource: gate.resource,
153
- preset: gate.preset,
154
- duration_ms: Math.max(0, Math.floor(Number(durationMs) || 0)),
155
- recorded_at: new Date().toISOString()
156
- };
213
+ parsed.records[key] = record;
157
214
  fs.mkdirSync(path.dirname(file), { recursive: true });
158
215
  fs.writeFileSync(file, `${JSON.stringify(parsed, null, 2)}\n`);
159
216
  }
@@ -4,7 +4,7 @@ import { spawn } from 'node:child_process';
4
4
  import { createReleaseGateHermeticEnv } from './release-gate-hermetic-env.js';
5
5
  import { appendReleaseGateJsonl, writeReleaseGateJson } from './release-gate-report.js';
6
6
  import { findReadyReleaseGateNodes, findReleaseGatesBlockedByFailedDeps, pickReadyLaunchableReleaseGates } from './release-gate-scheduler.js';
7
- import { readReleaseGateCacheRecord, writeReleaseGateCacheHit } from './release-gate-cache-v2.js';
7
+ import { readReleaseGateCacheRecord, releaseGateProofBankFile, writeReleaseGateCacheHit } from './release-gate-cache-v2.js';
8
8
  import { RELEASE_GATE_NODE_SCHEMA, validateReleaseGateManifest } from './release-gate-node.js';
9
9
  import { countReleaseGateResources, defaultReleaseGateBudget, summarizeReleaseGateBudget } from './release-gate-resource-governor.js';
10
10
  import { selectAffectedReleaseGates } from './release-gate-affected-selector.js';
@@ -38,8 +38,11 @@ export async function runReleaseGateDag(input) {
38
38
  const reportDir = path.join(root, '.sneakoscope', 'reports', 'release-gates', runId);
39
39
  fs.mkdirSync(reportDir, { recursive: true });
40
40
  const timeline = path.join(reportDir, 'timeline.jsonl');
41
+ const affectedGraphFile = path.join(reportDir, 'affected-gate-graph.json');
42
+ const completionCertificateFile = path.join(reportDir, 'completion-certificate.json');
41
43
  appendReleaseGateJsonl(timeline, { event: 'retention', phase: 'before_run', ...retentionBefore, at: new Date().toISOString() });
42
44
  const started = Date.now();
45
+ const slaMs = Math.max(1, Math.floor(Number(input.slaMs || 300000)));
43
46
  const pending = new Map(selected.map((gate) => [gate.id, gate]));
44
47
  const running = new Map();
45
48
  const completed = new Map();
@@ -54,6 +57,23 @@ export async function runReleaseGateDag(input) {
54
57
  const writeSummarySnapshot = (finished = false) => {
55
58
  const wallMs = Date.now() - started;
56
59
  const failures = [...failed.values()].map((row) => ({ id: row.id, exit_code: row.exit_code, stderr_tail: row.stderr_tail, timed_out: row.timed_out, signal: row.signal }));
60
+ const affectedGraph = buildAffectedGraph({
61
+ selection: affected.selection,
62
+ selected,
63
+ cachedGates,
64
+ executedGates,
65
+ proofBankFile: releaseGateProofBankFile(root)
66
+ });
67
+ const completionCertificate = buildCompletionCertificate({
68
+ ok: failures.length === 0,
69
+ preset,
70
+ slaMs,
71
+ wallMs,
72
+ criticalPathMs: estimateCriticalPath(selected, completed),
73
+ affectedGraph,
74
+ affectedGraphFile,
75
+ skippedByAffected: affected.selection.mode === 'affected' ? affected.selection.skipped_gate_ids : []
76
+ });
57
77
  const snapshot = {
58
78
  schema: 'sks.release-gate-dag-run.v1',
59
79
  ok: failures.length === 0,
@@ -83,7 +103,9 @@ export async function runReleaseGateDag(input) {
83
103
  budget_snapshot: budget,
84
104
  budget_summary: summarizeReleaseGateBudget(budget),
85
105
  report_dir: reportDir,
86
- failures
106
+ failures,
107
+ affected_graph: affectedGraph,
108
+ completion_certificate: completionCertificate
87
109
  };
88
110
  if (!finished) {
89
111
  snapshot.in_progress = true;
@@ -91,6 +113,12 @@ export async function runReleaseGateDag(input) {
91
113
  snapshot.running = running.size;
92
114
  }
93
115
  writeReleaseGateJson(path.join(reportDir, 'summary.json'), snapshot);
116
+ writeReleaseGateJson(affectedGraphFile, affectedGraph);
117
+ writeReleaseGateJson(completionCertificateFile, completionCertificate);
118
+ if (finished) {
119
+ writeReleaseGateJson(path.join(root, '.sneakoscope', 'reports', 'affected-gate-graph.json'), affectedGraph);
120
+ writeReleaseGateJson(path.join(root, '.sneakoscope', 'reports', 'completion-certificate.json'), completionCertificate);
121
+ }
94
122
  return snapshot;
95
123
  };
96
124
  if (input.explain) {
@@ -179,6 +207,57 @@ export function selectReleaseGatePreset(manifest, preset) {
179
207
  const effectivePreset = preset === 'affected' || preset === 'fast' ? 'release' : preset;
180
208
  return manifest.gates.filter((gate) => gate.preset.includes(effectivePreset));
181
209
  }
210
+ function buildAffectedGraph(input) {
211
+ return {
212
+ schema: 'sks.affected-gate-graph.v1',
213
+ changed_files: input.selection.changed_files,
214
+ affected_modules: inferAffectedModules(input.selection.changed_files),
215
+ affected_gates: input.selected.map((gate) => gate.id),
216
+ reused_proofs: [...input.cachedGates],
217
+ invalidated_proofs: [...input.executedGates],
218
+ skipped_gate_ids: input.selection.skipped_gate_ids,
219
+ proof_bank_file: input.proofBankFile
220
+ };
221
+ }
222
+ function buildCompletionCertificate(input) {
223
+ const affectedScope = input.preset === 'affected' || input.preset === 'fast';
224
+ return {
225
+ schema: 'sks.five-minute-completion-certificate.v1',
226
+ ok: input.ok,
227
+ tier: input.preset === 'affected' ? 'confidence' : input.preset,
228
+ confidence: affectedScope ? 'release-equivalent-for-affected-scope' : 'full-release-proof',
229
+ sla_ms: input.slaMs,
230
+ sla_met: input.wallMs <= input.slaMs,
231
+ changed_files: input.affectedGraph.changed_files,
232
+ affected_gates: input.affectedGraph.affected_gates.length,
233
+ reused_proofs: input.affectedGraph.reused_proofs.length,
234
+ newly_executed_gates: input.affectedGraph.invalidated_proofs.length,
235
+ skipped_as_valid_cache: input.affectedGraph.reused_proofs.length,
236
+ skipped_as_unaffected: input.skippedByAffected.length,
237
+ critical_path_ms: input.criticalPathMs,
238
+ wall_ms: input.wallMs,
239
+ full_release_proof: affectedScope ? 'background_or_release_before_publish_required' : 'current_run',
240
+ proof_bank_file: input.affectedGraph.proof_bank_file,
241
+ affected_graph_file: input.affectedGraphFile
242
+ };
243
+ }
244
+ function inferAffectedModules(files) {
245
+ const modules = new Set();
246
+ for (const file of files) {
247
+ const normalized = file.replace(/\\/g, '/');
248
+ const parts = normalized.split('/').filter(Boolean);
249
+ if (!parts.length)
250
+ continue;
251
+ const top = parts[0];
252
+ if (parts[0] === 'src' && parts.length >= 3)
253
+ modules.add(parts.slice(0, 3).join('/'));
254
+ else if (parts[0] === 'test' && parts.length >= 2)
255
+ modules.add(parts.slice(0, 2).join('/'));
256
+ else
257
+ modules.add(top);
258
+ }
259
+ return [...modules].sort();
260
+ }
182
261
  function runGate(root, runId, reportRoot, gate) {
183
262
  const started = Date.now();
184
263
  const hermetic = createReleaseGateHermeticEnv({ root, runId, gate, reportRoot });
@@ -0,0 +1,22 @@
1
+ import os from 'node:os';
2
+ export const RESOURCE_CLASS_BUDGET_SCHEMA = 'sks.resource-class-budget.v1';
3
+ export function computeResourceClassBudget(env = process.env) {
4
+ const cpus = Math.max(2, os.cpus().length || 2);
5
+ return {
6
+ schema: RESOURCE_CLASS_BUDGET_SCHEMA,
7
+ cpu_light: readEnvInt(env, 'SKS_RESOURCE_CPU_LIGHT', Math.max(2, cpus - 1)),
8
+ cpu_heavy: readEnvInt(env, 'SKS_RESOURCE_CPU_HEAVY', Math.max(1, Math.floor(cpus / 2))),
9
+ io_light: readEnvInt(env, 'SKS_RESOURCE_IO_LIGHT', 8),
10
+ io_heavy: readEnvInt(env, 'SKS_RESOURCE_IO_HEAVY', 2),
11
+ fs_read: readEnvInt(env, 'SKS_RESOURCE_FS_READ', 8),
12
+ network: readEnvInt(env, 'SKS_RESOURCE_NETWORK', 2),
13
+ remote_model_real: readEnvInt(env, 'SKS_RESOURCE_REMOTE_MODEL_REAL', 1),
14
+ zellij_real: readEnvInt(env, 'SKS_RESOURCE_ZELLIJ_REAL', 1),
15
+ secret_sensitive: readEnvInt(env, 'SKS_RESOURCE_SECRET', 1)
16
+ };
17
+ }
18
+ function readEnvInt(env, key, fallback) {
19
+ const value = Number(env[key]);
20
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
21
+ }
22
+ //# sourceMappingURL=resource-class-budget.js.map
@@ -0,0 +1,22 @@
1
+ import { computeTriWikiAffectedGraph } from '../triwiki/triwiki-affected-graph.js';
2
+ import { buildTriWikiSlaCertificate } from '../triwiki/triwiki-sla-certificate.js';
3
+ import { planExtremeParallelSchedule } from './extreme-parallel-scheduler.js';
4
+ export const SLA_SCHEDULER_SCHEMA = 'sks.sla-scheduler.v1';
5
+ export function planFiveMinuteSla(root, graph = computeTriWikiAffectedGraph({ root, tier: 'affected' }), slaMs = 300_000) {
6
+ const schedule = planExtremeParallelSchedule(root, graph);
7
+ const certificate = buildTriWikiSlaCertificate({
8
+ graph,
9
+ slaMs,
10
+ estimatedCriticalPathMs: schedule.critical_path_ms,
11
+ estimatedSequentialMs: schedule.sequential_ms,
12
+ blockers: [...schedule.blockers]
13
+ });
14
+ return {
15
+ schema: SLA_SCHEDULER_SCHEMA,
16
+ ok: certificate.ok,
17
+ graph,
18
+ certificate,
19
+ highest_confidence_subset: certificate.ok ? graph.gates : graph.gates.slice(0, Math.max(1, Math.floor(graph.gates.length / 2)))
20
+ };
21
+ }
22
+ //# sourceMappingURL=sla-scheduler.js.map
@@ -623,6 +623,11 @@ export const COMMAND_CATALOG = [
623
623
  { name: 'update-check', usage: 'sks update-check [--json]', description: 'Check npm for the latest Sneakoscope Codex version.' },
624
624
  { name: 'wizard', usage: 'sks wizard', description: 'Open an interactive setup UI for install scope, setup, doctor, and verification.' },
625
625
  { name: 'commands', usage: 'sks commands [--json]', description: 'List every user-facing command with a short description.' },
626
+ { name: 'check', usage: 'sks check --tier instant|affected|confidence|release|real-check [--sla 5m] [--changed-since auto] [--json]', description: 'Run build-once proof-bank checks: affected/confidence use incremental build and cached proof reuse; release keeps full clean proof for publish readiness.' },
627
+ { name: 'task', usage: 'sks task run [--sla 5m] [--json]', description: 'Run the normal affected-scope, release-equivalent task verification path.' },
628
+ { name: 'release', usage: 'sks release affected|full|background [--json]', description: 'Run affected release proof, full release proof, or background release proof explicitly.' },
629
+ { name: 'triwiki', usage: 'sks triwiki index|affected|proof-bank [--json]', description: 'Inspect TriWiki module cards, gate impact maps, affected graphs, and proof bank status.' },
630
+ { name: 'daemon', usage: 'sks daemon status|warm|stop [--json]', description: 'Inspect or warm the local SKS daemon cache state for build/proof reuse.' },
626
631
  { name: 'run', usage: 'sks run "task" [--visual|--research|--db] [--json]', description: 'Classify a plain-language task, materialize a mission, and route it through the SKS trust kernel.' },
627
632
  { name: 'status', usage: 'sks status [--json]', description: 'Show the active mission, route, phase, proof, trust, native agent, image voxel, DB safety, and next action.' },
628
633
  { name: 'usage', usage: `sks usage [${USAGE_TOPICS}]`, description: 'Print copy-ready workflows for common tasks.' },