sneakoscope 3.1.14 → 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.
- package/README.md +26 -1
- 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/command-registry.js +6 -13
- package/dist/commands/doctor.js +29 -2
- package/dist/commands/proof.js +8 -0
- package/dist/core/build/build-once-runner.js +62 -0
- package/dist/core/codex/codex-config-readability.js +52 -38
- package/dist/core/commands/check-command.js +130 -0
- package/dist/core/commands/daemon-command.js +14 -0
- package/dist/core/commands/mad-sks-command.js +18 -1
- package/dist/core/commands/release-command.js +52 -0
- package/dist/core/commands/task-command.js +15 -0
- package/dist/core/commands/triwiki-command.js +38 -0
- package/dist/core/daemon/sksd-client.js +9 -0
- package/dist/core/daemon/sksd.js +55 -0
- package/dist/core/doctor/doctor-dirty-planner.js +30 -0
- package/dist/core/doctor/doctor-transaction.js +13 -0
- package/dist/core/feature-fixtures.js +1 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +7 -1
- package/dist/core/probes/probe-memoization.js +42 -0
- package/dist/core/release/extreme-parallel-scheduler.js +33 -0
- package/dist/core/release/gate-pack-manifest.js +118 -0
- package/dist/core/release/gate-pack-runner.js +113 -0
- package/dist/core/release/release-gate-cache-v2.js +73 -16
- package/dist/core/release/release-gate-dag.js +81 -2
- package/dist/core/release/resource-class-budget.js +22 -0
- package/dist/core/release/sla-scheduler.js +22 -0
- package/dist/core/routes.js +5 -0
- package/dist/core/triwiki/triwiki-affected-graph.js +56 -0
- package/dist/core/triwiki/triwiki-cache-key.js +221 -0
- package/dist/core/triwiki/triwiki-gate-impact-map.js +79 -0
- package/dist/core/triwiki/triwiki-module-card.js +37 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +132 -0
- package/dist/core/triwiki/triwiki-proof-card.js +42 -0
- package/dist/core/triwiki/triwiki-sla-certificate.js +30 -0
- package/dist/core/version.js +1 -1
- package/dist/scripts/fixtures/fake-codex-config-loader.js +12 -1
- package/dist/scripts/release-4000-required-gates.js +36 -0
- package/dist/scripts/release-gate-dag-runner.js +18 -0
- package/dist/scripts/release-speed-summary.js +9 -0
- package/package.json +43 -7
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { flag } from '../../cli/args.js';
|
|
2
|
+
import { printJson } from '../../cli/output.js';
|
|
3
|
+
import { projectRoot } from '../fsx.js';
|
|
4
|
+
import { computeTriWikiAffectedGraph } from '../triwiki/triwiki-affected-graph.js';
|
|
5
|
+
import { buildTriWikiGateImpactMap } from '../triwiki/triwiki-gate-impact-map.js';
|
|
6
|
+
import { DEFAULT_TRIWIKI_MODULE_CARDS } from '../triwiki/triwiki-module-card.js';
|
|
7
|
+
import { summarizeTriWikiProofBank } from '../triwiki/triwiki-proof-bank.js';
|
|
8
|
+
export async function triwikiCommand(args = []) {
|
|
9
|
+
const root = await projectRoot();
|
|
10
|
+
const sub = args[0] && !args[0].startsWith('-') ? args[0] : 'index';
|
|
11
|
+
const json = flag(args, '--json');
|
|
12
|
+
let result;
|
|
13
|
+
if (sub === 'index') {
|
|
14
|
+
result = {
|
|
15
|
+
schema: 'sks.triwiki-index.v1',
|
|
16
|
+
ok: true,
|
|
17
|
+
modules: DEFAULT_TRIWIKI_MODULE_CARDS,
|
|
18
|
+
impact_map: buildTriWikiGateImpactMap(root),
|
|
19
|
+
proof_bank: summarizeTriWikiProofBank(root)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
else if (sub === 'affected') {
|
|
23
|
+
result = computeTriWikiAffectedGraph({ root, tier: 'affected' });
|
|
24
|
+
}
|
|
25
|
+
else if (sub === 'proof-bank') {
|
|
26
|
+
result = summarizeTriWikiProofBank(root);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.error('Usage: sks triwiki index|affected|proof-bank [--json]');
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (json)
|
|
34
|
+
return printJson(result);
|
|
35
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=triwiki-command.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { sksdStatus, sksdStop, sksdWarm } from './sksd.js';
|
|
2
|
+
export function runSksdClient(root, action) {
|
|
3
|
+
if (action === 'warm')
|
|
4
|
+
return sksdWarm(root);
|
|
5
|
+
if (action === 'stop')
|
|
6
|
+
return sksdStop(root);
|
|
7
|
+
return sksdStatus(root);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=sksd-client.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const SKSD_STATE_SCHEMA = 'sks.sksd-state.v1';
|
|
4
|
+
export function sksdStatus(root) {
|
|
5
|
+
const state = readState(root);
|
|
6
|
+
return state || emptyState();
|
|
7
|
+
}
|
|
8
|
+
export function sksdWarm(root) {
|
|
9
|
+
const state = {
|
|
10
|
+
schema: SKSD_STATE_SCHEMA,
|
|
11
|
+
status: 'warm',
|
|
12
|
+
pid: process.pid,
|
|
13
|
+
warmed_at: new Date().toISOString(),
|
|
14
|
+
proof_bank_ready: true,
|
|
15
|
+
build_proof_ready: fs.existsSync(path.join(root, 'dist', '.sks-build-proof.json'))
|
|
16
|
+
};
|
|
17
|
+
writeState(root, state);
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
export function sksdStop(root) {
|
|
21
|
+
const state = { ...emptyState(), status: 'stopped' };
|
|
22
|
+
writeState(root, state);
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
25
|
+
function readState(root) {
|
|
26
|
+
const file = statePath(root);
|
|
27
|
+
try {
|
|
28
|
+
if (!fs.existsSync(file))
|
|
29
|
+
return null;
|
|
30
|
+
const json = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
31
|
+
return json.schema === SKSD_STATE_SCHEMA ? json : null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function writeState(root, state) {
|
|
38
|
+
const file = statePath(root);
|
|
39
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
40
|
+
fs.writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`);
|
|
41
|
+
}
|
|
42
|
+
function statePath(root) {
|
|
43
|
+
return path.join(root, '.sneakoscope', 'cache', 'sksd-state.json');
|
|
44
|
+
}
|
|
45
|
+
function emptyState() {
|
|
46
|
+
return {
|
|
47
|
+
schema: SKSD_STATE_SCHEMA,
|
|
48
|
+
status: 'stopped',
|
|
49
|
+
pid: null,
|
|
50
|
+
warmed_at: null,
|
|
51
|
+
proof_bank_ready: false,
|
|
52
|
+
build_proof_ready: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=sksd.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const DOCTOR_DIRTY_PLAN_SCHEMA = 'sks.doctor-dirty-plan.v1';
|
|
4
|
+
export function planDoctorDirtyRepair(root, phaseIds) {
|
|
5
|
+
const phases = phaseIds.map((id) => {
|
|
6
|
+
const marker = markerPath(root, id);
|
|
7
|
+
if (!fs.existsSync(marker))
|
|
8
|
+
return { id, status: 'dirty', reason: 'no_clean_marker' };
|
|
9
|
+
return { id, status: 'clean', reason: 'clean_marker_present' };
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
schema: DOCTOR_DIRTY_PLAN_SCHEMA,
|
|
13
|
+
root,
|
|
14
|
+
phases,
|
|
15
|
+
dirty_count: phases.filter((phase) => phase.status === 'dirty').length,
|
|
16
|
+
clean_count: phases.filter((phase) => phase.status === 'clean').length
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function markDoctorPhaseClean(root, id) {
|
|
20
|
+
const file = markerPath(root, id);
|
|
21
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
22
|
+
fs.writeFileSync(file, `${new Date().toISOString()}\n`);
|
|
23
|
+
}
|
|
24
|
+
export function isDoctorPhaseClean(plan, id) {
|
|
25
|
+
return plan?.phases.find((phase) => phase.id === id)?.status === 'clean';
|
|
26
|
+
}
|
|
27
|
+
function markerPath(root, id) {
|
|
28
|
+
return path.join(root, '.sneakoscope', 'cache', 'doctor-dirty', `${id.replace(/[^a-zA-Z0-9._-]+/g, '_')}.clean`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=doctor-dirty-planner.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { isDoctorPhaseClean, markDoctorPhaseClean } from './doctor-dirty-planner.js';
|
|
3
4
|
export async function runDoctorFixTransaction(input) {
|
|
4
5
|
const startedAt = nowIso();
|
|
5
6
|
const phases = [];
|
|
@@ -18,6 +19,16 @@ export async function runDoctorFixTransaction(input) {
|
|
|
18
19
|
artifact_path: null,
|
|
19
20
|
started_at: phaseStarted
|
|
20
21
|
};
|
|
22
|
+
if (isDoctorPhaseClean(input.dirtyPlan, definition.id)) {
|
|
23
|
+
phases.push({
|
|
24
|
+
...phase,
|
|
25
|
+
ok: true,
|
|
26
|
+
warnings: ['dirty_plan_skipped_clean_phase'],
|
|
27
|
+
completed_at: nowIso(),
|
|
28
|
+
duration_ms: Math.max(0, Date.now() - startedMs)
|
|
29
|
+
});
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
21
32
|
try {
|
|
22
33
|
const result = await definition.run();
|
|
23
34
|
phase = normalizePhase(definition, result, phase, startedMs);
|
|
@@ -48,6 +59,8 @@ export async function runDoctorFixTransaction(input) {
|
|
|
48
59
|
}
|
|
49
60
|
phase.completed_at = phase.completed_at || nowIso();
|
|
50
61
|
phase.duration_ms = phase.duration_ms ?? Math.max(0, Date.now() - startedMs);
|
|
62
|
+
if (phase.ok)
|
|
63
|
+
markDoctorPhaseClean(input.root, definition.id);
|
|
51
64
|
phases.push(phase);
|
|
52
65
|
}
|
|
53
66
|
const writeInput = {
|
|
@@ -21,6 +21,7 @@ const FIXTURES = Object.freeze({
|
|
|
21
21
|
'cli-hooks': fixture('mock', 'sks hooks trust-report --json', [], 'pass'),
|
|
22
22
|
'cli-features': fixture('execute', 'sks features check --json', [], 'pass'),
|
|
23
23
|
'cli-commands': fixture('execute', 'sks commands --json', [], 'pass'),
|
|
24
|
+
'cli-check': fixture('execute', 'sks check --tier confidence --sla 5m --plan --json', [], 'pass'),
|
|
24
25
|
'cli-run': fixture('execute_and_validate_artifacts', 'sks run "fixture" --mock --json', ['run-classification.json', 'completion-proof.json', 'evidence-index.json', 'route-completion-contract.json', 'trust-report.json'], 'pass'),
|
|
25
26
|
'cli-status': fixture('execute', 'sks status --json', [], 'pass'),
|
|
26
27
|
'cli-usage': fixture('execute', 'sks usage overview', [], 'pass'),
|
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 = '
|
|
8
|
+
export const PACKAGE_VERSION = '4.0.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() {
|
package/dist/core/init.js
CHANGED
|
@@ -806,7 +806,13 @@ export async function initProject(root, opts = {}) {
|
|
|
806
806
|
// Context7 credentials may live directly in this table as args/env/headers/url
|
|
807
807
|
// depending on the user's MCP client setup. Seed the default only when absent;
|
|
808
808
|
// never replace an existing Context7 block during setup/update.
|
|
809
|
-
|
|
809
|
+
// Seed the REMOTE (streamable HTTP `url`) transport, not local stdio: Codex
|
|
810
|
+
// merges the global ~/.codex/config.toml and the project config per-key, so a
|
|
811
|
+
// local-stdio `command` here merging with a remote `url` in the global config
|
|
812
|
+
// yields a stdio server that also carries a `url` — which Codex 0.140 rejects
|
|
813
|
+
// with `url is not supported for stdio`. Remote is also the transport the doctor
|
|
814
|
+
// migrates everyone to (local stdio can block interactive Codex launch).
|
|
815
|
+
{ table: 'mcp_servers.context7', text: context7ConfigToml('remote').trim(), preserveExisting: true },
|
|
810
816
|
{ table: 'agents.native_agent', text: agentConfigBlock('native_agent', 'Read-only SKS analysis agent.', './agents/native-agent-intake.toml', ['Analysis', 'Mapper']) },
|
|
811
817
|
{ table: 'agents.team_consensus', text: agentConfigBlock('team_consensus', 'SKS planning/debate agent.', './agents/team-consensus.toml', ['Consensus', 'Atlas']) },
|
|
812
818
|
{ table: 'agents.implementation_worker', text: agentConfigBlock('implementation_worker', 'SKS bounded implementation worker.', './agents/implementation-worker.toml', ['Builder', 'Mason']) },
|
|
@@ -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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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[
|
|
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
|
}
|