sneakoscope 0.9.17 → 0.9.18
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 +11 -6
- 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/package.json +8 -5
- package/src/core/commands/command-utils.mjs +1 -1
- package/src/core/commands/ppt-command.mjs +1 -1
- package/src/core/commands/scouts-command.mjs +74 -6
- package/src/core/feature-fixture-runner.mjs +42 -14
- package/src/core/feature-fixtures.mjs +64 -5
- package/src/core/feature-registry.mjs +7 -5
- package/src/core/fsx.mjs +1 -1
- package/src/core/pipeline/active-context.mjs +3 -0
- package/src/core/pipeline/pipeline-plan-writer.mjs +8 -0
- package/src/core/pipeline/plan-schema.mjs +4 -0
- package/src/core/pipeline/prompt-context.mjs +6 -0
- package/src/core/pipeline/route-prep.mjs +3 -0
- package/src/core/pipeline/scout-stage-policy.mjs +4 -0
- package/src/core/pipeline/stage-policy.mjs +4 -0
- package/src/core/pipeline/stop-gate.mjs +10 -0
- package/src/core/pipeline/validation.mjs +3 -0
- package/src/core/pipeline-runtime.mjs +1693 -0
- package/src/core/pipeline.mjs +34 -1693
- package/src/core/scouts/engines/codex-app-subagent-engine.mjs +9 -0
- package/src/core/scouts/engines/codex-exec-parallel-engine.mjs +52 -0
- package/src/core/scouts/engines/local-static-engine.mjs +10 -0
- package/src/core/scouts/engines/scout-engine-base.mjs +118 -0
- package/src/core/scouts/engines/scout-engine-detect.mjs +63 -0
- package/src/core/scouts/engines/scout-engine-policy.mjs +76 -0
- package/src/core/scouts/engines/sequential-fallback-engine.mjs +10 -0
- package/src/core/scouts/engines/tmux-lane-engine.mjs +9 -0
- package/src/core/scouts/scout-proof-evidence.mjs +10 -0
- package/src/core/scouts/scout-readonly-guard.mjs +80 -0
- package/src/core/scouts/scout-runner.mjs +197 -23
- package/src/core/scouts/scout-schema.mjs +4 -2
- package/src/core/version.mjs +1 -1
package/README.md
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Fast legacy-free proof-first Codex trust layer with image-based Voxel TriWiki.
|
|
4
4
|
|
|
5
|
-
Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable. `0.9.
|
|
5
|
+
Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable. `0.9.18` makes route execution hermetic and evidence-strict: Five-Scout intake can use real Codex/tmux engines when available, route E2E runs in temp project roots, feature fixtures no longer receive implicit static-pass fallback, and pipeline planning is exposed through split policy modules with a facade compatibility layer.
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
## 0.9.
|
|
9
|
+
## 0.9.18 Current Release
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
0.9.18 makes SKS route execution hermetic and evidence-strict. Five-Scout intake can run through real Codex/tmux engines when available, falls back honestly when not, and never claims speedup from mock/static evidence. E2E route tests run in isolated temp project roots. Feature fixtures no longer receive implicit static-pass fallback; every feature declares an explicit fixture. Pipeline architecture is split into stage policy, scout policy, prompt context, active context, and stop-gate modules.
|
|
12
12
|
|
|
13
13
|
Highlights:
|
|
14
14
|
|
|
15
15
|
- Serious route mock/fixture commands call `maybeFinalizeRoute`, so Team, QA-LOOP, Research, PPT, Image UX Review, Computer Use, DB, Wiki, and GX fixtures produce route-local `completion-proof.json` without a separate repair/finalize step.
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
16
|
+
- `sks scouts engines --json` reports Codex exec, tmux, Codex App subagent, local static, and sequential fallback availability with blockers.
|
|
17
|
+
- Scout read-only guards snapshot source state and allow only mission-local `scout-*` artifacts plus scout reports.
|
|
18
|
+
- E2E route tests run actual route commands inside isolated temp roots instead of sharing the source checkout `.sneakoscope`.
|
|
19
|
+
- Feature fixtures execute deterministic allowlisted commands in hermetic temp roots and validate artifacts generated by those commands, including mission-local proof, visual ledgers, DB reports, and GX/Wiki evidence.
|
|
20
|
+
- `npm run release:check` includes `pipeline-budget:check`, `scout-engines:check`, strict scout validation, and hermetic fixture execution.
|
|
19
21
|
- `sks rust status|smoke --json` reports optional Rust availability, detects stale native binaries, and proves JS fallback parity when native Rust is missing or version-mismatched.
|
|
20
22
|
- `npm run release:check` includes `route-modularity:check`, `command-budget:check`, and `feature-fixtures:strict`.
|
|
21
23
|
|
|
@@ -36,6 +38,9 @@ Learn more:
|
|
|
36
38
|
- Image Voxel TriWiki: [docs/image-voxel-ledger.md](docs/image-voxel-ledger.md)
|
|
37
39
|
- Route finalization: [docs/route-finalization.md](docs/route-finalization.md)
|
|
38
40
|
- Feature fixtures: [docs/feature-fixtures.md](docs/feature-fixtures.md)
|
|
41
|
+
- Scout engines: [docs/scout-engines.md](docs/scout-engines.md)
|
|
42
|
+
- Hermetic E2E: [docs/testing-hermetic-e2e.md](docs/testing-hermetic-e2e.md)
|
|
43
|
+
- Pipeline architecture: [docs/pipeline-architecture.md](docs/pipeline-architecture.md)
|
|
39
44
|
- Rust accelerator: [docs/rust-accelerator.md](docs/rust-accelerator.md)
|
|
40
45
|
- Codex App Hooks/PAT: [docs/hooks-pat.md](docs/hooks-pat.md)
|
|
41
46
|
- codex-lb: [docs/codex-lb.md](docs/codex-lb.md)
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 0.9.
|
|
7
|
+
Some("--version") => println!("sks-rs 0.9.18"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.18",
|
|
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",
|
|
@@ -43,24 +43,27 @@
|
|
|
43
43
|
"legacy-free:check": "node ./scripts/check-legacy-free.mjs",
|
|
44
44
|
"route-modularity:check": "node ./scripts/check-route-modularity.mjs",
|
|
45
45
|
"command-budget:check": "node ./scripts/check-command-module-budget.mjs",
|
|
46
|
+
"pipeline-budget:check": "node ./scripts/check-pipeline-budget.mjs",
|
|
46
47
|
"sizecheck": "node ./scripts/sizecheck.mjs",
|
|
47
48
|
"registry:check": "node ./scripts/release-registry-check.mjs",
|
|
48
49
|
"feature:check": "node ./bin/sks.mjs features check --json",
|
|
49
50
|
"all-features:selftest": "node ./bin/sks.mjs all-features selftest --mock --json",
|
|
50
51
|
"all-features:execute-fixtures": "node ./bin/sks.mjs all-features selftest --mock --execute-fixtures --strict-artifacts --json",
|
|
51
|
-
"feature-fixtures:strict": "node ./bin/sks.mjs all-features selftest --mock --execute-fixtures --strict-artifacts --json",
|
|
52
|
-
"
|
|
53
|
-
"scouts:
|
|
52
|
+
"feature-fixtures:strict": "node ./bin/sks.mjs all-features selftest --mock --execute-fixtures --strict-artifacts --hermetic --json",
|
|
53
|
+
"scout-engines:check": "node ./bin/sks.mjs scouts engines --json",
|
|
54
|
+
"scouts:selftest": "node ./bin/sks.mjs scouts run latest --engine local-static --mock --json",
|
|
55
|
+
"scouts:check": "node ./bin/sks.mjs scouts validate latest --strict --json",
|
|
54
56
|
"perf:cold-start": "node ./bin/sks.mjs perf cold-start --json",
|
|
55
57
|
"perf:gate": "node ./scripts/perf-gate.mjs",
|
|
56
58
|
"test": "node --test \"test/**/*.test.mjs\"",
|
|
57
59
|
"test:unit": "node --test \"test/unit/**/*.test.mjs\"",
|
|
58
60
|
"test:integration:mock": "node --test \"test/integration/**/*.test.mjs\"",
|
|
59
61
|
"test:e2e:mock": "node --test \"test/e2e/**/*.test.mjs\"",
|
|
62
|
+
"test:real-scouts": "node --test \"test/real/**/*.test.mjs\"",
|
|
60
63
|
"rust:check": "cargo check --manifest-path crates/sks-core/Cargo.toml",
|
|
61
64
|
"rust:smoke": "node ./scripts/rust-smoke.mjs",
|
|
62
65
|
"coverage": "node --experimental-test-coverage --test \"test/**/*.test.mjs\"",
|
|
63
|
-
"release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run legacy-free:check && npm run
|
|
66
|
+
"release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run legacy-free:check && npm run route-modularity:check && npm run command-budget:check && npm run pipeline-budget:check && npm run packcheck && npm run feature:check && npm run all-features:selftest && npm run scout-engines:check && npm run scouts:selftest && npm run scouts:check && npm run feature-fixtures:strict && npm run selftest && npm run test:unit && npm run test:integration:mock && npm run test:e2e:mock && npm run rust:check && npm run rust:smoke && npm run perf:gate && npm run sizecheck && npm run registry:check",
|
|
64
67
|
"publish:dry": "npm run release:check && npm --cache /tmp/sks-npm-cache publish --dry-run --registry https://registry.npmjs.org/ --access public",
|
|
65
68
|
"publish:npm": "npm --cache /tmp/sks-npm-cache publish --registry https://registry.npmjs.org/ --access public",
|
|
66
69
|
"prepublishOnly": "npm run release:check && node ./scripts/release-registry-check.mjs --require-unpublished"
|
|
@@ -30,7 +30,7 @@ export function positionalArgs(args = []) {
|
|
|
30
30
|
'--lines', '--intent', '--changed', '--route', '--skills', '--prompt-signature',
|
|
31
31
|
'--mission-id', '--source', '--image-id', '--bbox', '--label', '--evidence',
|
|
32
32
|
'--claim-id', '--type', '--before', '--after', '--anchors', '--verification',
|
|
33
|
-
'--status', '--scouts'
|
|
33
|
+
'--status', '--scouts', '--engine'
|
|
34
34
|
]);
|
|
35
35
|
for (let i = 0; i < args.length; i += 1) {
|
|
36
36
|
const arg = String(args[i]);
|
|
@@ -81,7 +81,7 @@ function mockPptFixtureGate(gate = {}) {
|
|
|
81
81
|
|
|
82
82
|
function fixtureAnswers() {
|
|
83
83
|
return {
|
|
84
|
-
PRESENTATION_AUDIENCE_PROFILE: 'Release reviewer validating SKS 0.9.
|
|
84
|
+
PRESENTATION_AUDIENCE_PROFILE: 'Release reviewer validating SKS 0.9.18 route evidence.',
|
|
85
85
|
PRESENTATION_STP_STRATEGY: 'Segment: developer tooling. Target: Codex trust-layer maintainers. Positioning: proof-first release readiness.',
|
|
86
86
|
PRESENTATION_DELIVERY_CONTEXT: 'Mock release gate fixture.',
|
|
87
87
|
PRESENTATION_PAINPOINT_SOLUTION_MAP: [
|
|
@@ -6,10 +6,12 @@ import { buildScoutTeamPlan, normalizeScoutPolicy, routeRequiresScoutIntake, sco
|
|
|
6
6
|
import { readScoutGateStatus, readScoutResults } from '../scouts/scout-gate.mjs';
|
|
7
7
|
import { runFiveScoutIntake } from '../scouts/scout-runner.mjs';
|
|
8
8
|
import { readScoutProofEvidence } from '../scouts/scout-proof-evidence.mjs';
|
|
9
|
+
import { detectScoutEngines } from '../scouts/engines/scout-engine-detect.mjs';
|
|
10
|
+
import { selectScoutEngine } from '../scouts/engines/scout-engine-policy.mjs';
|
|
9
11
|
import { SCOUT_COUNT } from '../scouts/scout-schema.mjs';
|
|
10
12
|
import { flag, readFlagValue, resolveMissionId } from './command-utils.mjs';
|
|
11
13
|
|
|
12
|
-
const ACTIONS = new Set(['plan', 'run', 'status', 'consensus', 'handoff', 'validate', 'help', '--help', '-h']);
|
|
14
|
+
const ACTIONS = new Set(['plan', 'run', 'status', 'consensus', 'handoff', 'validate', 'engines', 'bench', 'help', '--help', '-h']);
|
|
13
15
|
|
|
14
16
|
export async function scoutsCommand(args = []) {
|
|
15
17
|
const root = await projectRoot();
|
|
@@ -18,10 +20,19 @@ export async function scoutsCommand(args = []) {
|
|
|
18
20
|
if (action === 'help' || action === '--help' || action === '-h') return scoutsHelp();
|
|
19
21
|
const json = flag(actionArgs, '--json');
|
|
20
22
|
const mock = flag(actionArgs, '--mock');
|
|
23
|
+
const strict = flag(actionArgs, '--strict');
|
|
24
|
+
const requestedEngine = readFlagValue(actionArgs, '--engine', 'auto');
|
|
25
|
+
const requireRealParallel = flag(actionArgs, '--require-real-parallel');
|
|
21
26
|
const force = flag(actionArgs, '--force-scouts') || flag(actionArgs, '--force');
|
|
22
27
|
const noScouts = flag(actionArgs, '--no-scouts');
|
|
23
28
|
const missionArg = actionArgs.find((arg) => !String(arg).startsWith('--')) || 'latest';
|
|
24
|
-
|
|
29
|
+
if (action === 'engines') {
|
|
30
|
+
const result = await detectScoutEngines(root, {});
|
|
31
|
+
if (json) return console.log(JSON.stringify(result, null, 2));
|
|
32
|
+
for (const engine of result.engines) console.log(`${engine.name}: ${engine.available ? 'available' : 'blocked'}${engine.reason ? ` (${engine.reason})` : ''}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const { id, dir, mission, created } = await resolveOrCreateScoutMission(root, missionArg, { mock, action, strict });
|
|
25
36
|
const context = await inferScoutContext(root, id, { route: readFlagValue(actionArgs, '--route', null), task: readFlagValue(actionArgs, '--task', null) });
|
|
26
37
|
if (action === 'plan') {
|
|
27
38
|
const parallelMode = flag(actionArgs, '--sequential') ? 'sequential_fallback' : 'parallel';
|
|
@@ -51,6 +62,8 @@ export async function scoutsCommand(args = []) {
|
|
|
51
62
|
task: context.task,
|
|
52
63
|
mode: mock ? 'mock' : 'manual',
|
|
53
64
|
parallel: !flag(actionArgs, '--sequential'),
|
|
65
|
+
engine: requestedEngine,
|
|
66
|
+
requireRealParallel,
|
|
54
67
|
mock
|
|
55
68
|
});
|
|
56
69
|
await setCurrent(root, {
|
|
@@ -75,6 +88,8 @@ export async function scoutsCommand(args = []) {
|
|
|
75
88
|
route: context.route,
|
|
76
89
|
scout_count: SCOUT_COUNT,
|
|
77
90
|
completed_scouts: results.filter((row) => row.status === 'done').length,
|
|
91
|
+
engine: gate.gate?.engine || null,
|
|
92
|
+
real_parallel: gate.gate?.real_parallel === true,
|
|
78
93
|
gate: gate.gate,
|
|
79
94
|
missing: gate.missing
|
|
80
95
|
};
|
|
@@ -112,13 +127,14 @@ export async function scoutsCommand(args = []) {
|
|
|
112
127
|
}
|
|
113
128
|
if (action === 'validate') {
|
|
114
129
|
let gate = await readScoutGateStatus(root, id);
|
|
115
|
-
if (!gate.ok && !
|
|
130
|
+
if (!gate.ok && !strict) {
|
|
116
131
|
const run = await runFiveScoutIntake(root, {
|
|
117
132
|
missionId: id,
|
|
118
133
|
route: context.route,
|
|
119
134
|
task: context.task,
|
|
120
135
|
mode: 'validate-fixture',
|
|
121
136
|
parallel: true,
|
|
137
|
+
engine: 'local-static',
|
|
122
138
|
mock: true
|
|
123
139
|
});
|
|
124
140
|
gate = { ok: run.gate?.passed === true, gate: run.gate, missing: run.gate?.blockers || [] };
|
|
@@ -136,13 +152,61 @@ export async function scoutsCommand(args = []) {
|
|
|
136
152
|
console.log(`Scout validation: ${result.ok ? 'pass' : 'blocked'}`);
|
|
137
153
|
return;
|
|
138
154
|
}
|
|
155
|
+
if (action === 'bench') {
|
|
156
|
+
const selection = await selectScoutEngine(root, {
|
|
157
|
+
requested: requestedEngine,
|
|
158
|
+
requireRealParallel,
|
|
159
|
+
missionId: id,
|
|
160
|
+
route: context.route,
|
|
161
|
+
mock
|
|
162
|
+
});
|
|
163
|
+
const parallelRun = await runFiveScoutIntake(root, {
|
|
164
|
+
missionId: id,
|
|
165
|
+
route: context.route,
|
|
166
|
+
task: context.task,
|
|
167
|
+
mode: 'bench-parallel',
|
|
168
|
+
parallel: true,
|
|
169
|
+
engine: selection.selected,
|
|
170
|
+
requireRealParallel,
|
|
171
|
+
mock
|
|
172
|
+
});
|
|
173
|
+
const sequentialRun = await runFiveScoutIntake(root, {
|
|
174
|
+
missionId: id,
|
|
175
|
+
route: context.route,
|
|
176
|
+
task: context.task,
|
|
177
|
+
mode: 'bench-sequential',
|
|
178
|
+
parallel: false,
|
|
179
|
+
engine: 'sequential-fallback',
|
|
180
|
+
mock: true
|
|
181
|
+
});
|
|
182
|
+
const sequentialMs = Number(sequentialRun.performance?.duration_ms || 0);
|
|
183
|
+
const parallelMs = Number(parallelRun.performance?.duration_ms || 0);
|
|
184
|
+
const result = {
|
|
185
|
+
schema: 'sks.scout-benchmark.v1',
|
|
186
|
+
engine: selection.selected,
|
|
187
|
+
real_parallel: selection.real_parallel === true,
|
|
188
|
+
sequential_ms: sequentialMs,
|
|
189
|
+
parallel_ms: parallelMs,
|
|
190
|
+
speedup: selection.real_parallel && parallelMs > 0 ? Number((sequentialMs / parallelMs).toFixed(2)) : null,
|
|
191
|
+
claim_allowed: selection.real_parallel === true && parallelRun.performance?.claim_allowed === true,
|
|
192
|
+
confidence: selection.real_parallel ? 'medium' : 'low',
|
|
193
|
+
notes: selection.real_parallel ? [] : ['mock/static benchmarks cannot claim real speedup']
|
|
194
|
+
};
|
|
195
|
+
await writeJsonAtomic(path.join(dir, 'scout-benchmark.json'), result);
|
|
196
|
+
if (json) return console.log(JSON.stringify(result, null, 2));
|
|
197
|
+
console.log(`Scout benchmark: ${result.claim_allowed ? 'claim allowed' : 'claim not allowed'}`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
139
200
|
}
|
|
140
201
|
|
|
141
202
|
async function resolveOrCreateScoutMission(root, missionArg, opts = {}) {
|
|
142
203
|
const resolved = await resolveMissionId(root, missionArg);
|
|
143
204
|
if (resolved) return { id: resolved, ...(await loadMission(root, resolved)), created: false };
|
|
205
|
+
if (opts.strict) {
|
|
206
|
+
throw new Error('No mission found for strict scout validation; strict mode never creates scout artifacts.');
|
|
207
|
+
}
|
|
144
208
|
if (!opts.mock && opts.action !== 'validate' && opts.action !== 'run' && opts.action !== 'plan') {
|
|
145
|
-
throw new Error('No mission found. Use sks scouts run latest --mock --json to create a fixture mission.');
|
|
209
|
+
throw new Error('No mission found. Use sks scouts run latest --engine local-static --mock --json to create a fixture mission.');
|
|
146
210
|
}
|
|
147
211
|
const created = await createMission(root, { mode: 'scouts', prompt: 'Five Scout fixture intake' });
|
|
148
212
|
return { id: created.id, dir: created.dir, mission: created.mission, created: true };
|
|
@@ -189,11 +253,15 @@ function scoutsHelp() {
|
|
|
189
253
|
|
|
190
254
|
Usage:
|
|
191
255
|
sks scouts plan latest --json
|
|
192
|
-
sks scouts run latest --
|
|
256
|
+
sks scouts run latest --engine auto --json
|
|
257
|
+
sks scouts run latest --engine local-static --mock --json
|
|
258
|
+
sks scouts run latest --require-real-parallel --json
|
|
193
259
|
sks scouts status latest --json
|
|
260
|
+
sks scouts engines --json
|
|
261
|
+
sks scouts bench latest --engine local-static --mock --json
|
|
194
262
|
sks scouts consensus latest --json
|
|
195
263
|
sks scouts handoff latest
|
|
196
|
-
sks scouts validate latest --json
|
|
264
|
+
sks scouts validate latest --strict --json
|
|
197
265
|
|
|
198
266
|
Alias:
|
|
199
267
|
sks scout run latest --json
|
|
@@ -12,12 +12,14 @@ export function runFeatureFixture(feature, {
|
|
|
12
12
|
} = {}) {
|
|
13
13
|
const fixture = feature.fixture || {};
|
|
14
14
|
const expected = normalizeExpectedArtifacts(fixture.expected_artifacts);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
15
|
+
const useHermeticRoot = fixture.root_mode !== 'source_checkout_required' && (execute || validateArtifacts || fixture.kind === 'execute_and_validate_artifacts');
|
|
16
|
+
const projectRoot = useHermeticRoot ? prepareHermeticFixtureRoot(root, tempRoot) : root;
|
|
17
|
+
const latestBefore = latestMissionId(projectRoot);
|
|
18
|
+
const execution = execute && commandArgs ? executeCommand(root, projectRoot, commandArgs, fixture) : null;
|
|
19
|
+
const latestAfter = execution?.mission_id || latestMissionId(projectRoot) || latestBefore;
|
|
18
20
|
const shouldValidateArtifacts = validateArtifacts && (fixture.kind === 'execute_and_validate_artifacts' || execution);
|
|
19
21
|
const artifacts = shouldValidateArtifacts
|
|
20
|
-
? expected.map((artifact) => inspectExpectedArtifact(
|
|
22
|
+
? expected.map((artifact) => inspectExpectedArtifact(projectRoot, tempRoot, artifact, { latestMissionId: latestAfter }))
|
|
21
23
|
: expected.map((artifact) => ({ path: artifact.path, requested_path: artifact.path, schema: artifact.schema || inferSchema(artifact.path), exists: null, schema_ok: null, content_ok: null, skipped: 'contract_only' }));
|
|
22
24
|
const artifactFailures = shouldValidateArtifacts
|
|
23
25
|
? artifacts.filter((artifact) => !artifact.exists || !artifact.schema_ok || !artifact.content_ok).map((artifact) => `${feature.id}:${artifact.path}:${artifact.failure || 'artifact_invalid'}`)
|
|
@@ -26,17 +28,20 @@ export function runFeatureFixture(feature, {
|
|
|
26
28
|
id: feature.id,
|
|
27
29
|
kind: fixture.kind || 'static',
|
|
28
30
|
command: fixture.command || null,
|
|
29
|
-
temp_root: tempRoot,
|
|
31
|
+
temp_root: useHermeticRoot ? projectRoot : tempRoot,
|
|
32
|
+
root_mode: useHermeticRoot ? 'hermetic_temp_project' : 'source_checkout_required',
|
|
30
33
|
latest_mission_id: latestAfter,
|
|
31
34
|
executed: Boolean(execution),
|
|
32
35
|
execution,
|
|
33
36
|
expected_artifacts: artifacts,
|
|
34
37
|
artifact_schema_validated: validateArtifacts,
|
|
35
|
-
|
|
38
|
+
no_plaintext_secrets: validateNoPlaintextSecrets(projectRoot),
|
|
39
|
+
ok: (!execution || execution.ok) && artifactFailures.length === 0 && validateNoPlaintextSecrets(projectRoot) && !(validateArtifacts && fixture.kind === 'execute_and_validate_artifacts' && expected.length && !execution),
|
|
36
40
|
failures: [
|
|
37
41
|
...(!fixture.command && fixture.status === 'pass' ? [`${feature.id}:fixture_command_missing`] : []),
|
|
38
42
|
...(validateArtifacts && fixture.kind === 'execute_and_validate_artifacts' && expected.length && !execution ? [`${feature.id}:command_not_executed_for_artifact_validation`] : []),
|
|
39
43
|
...(execution && !execution.ok ? [`${feature.id}:command_exit_${execution.status}`] : []),
|
|
44
|
+
...(validateNoPlaintextSecrets(projectRoot) ? [] : [`${feature.id}:plaintext_secret_detected`]),
|
|
40
45
|
...artifactFailures
|
|
41
46
|
]
|
|
42
47
|
};
|
|
@@ -52,12 +57,15 @@ export function writeFeatureFixtureReports(root, report) {
|
|
|
52
57
|
return { json: jsonPath, md: mdPath };
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
function executeCommand(
|
|
60
|
+
function executeCommand(sourceRoot, projectRoot, spec, fixture = {}) {
|
|
56
61
|
const normalized = Array.isArray(spec) ? { command: spec } : spec;
|
|
57
|
-
const setup =
|
|
58
|
-
|
|
62
|
+
const setup = [
|
|
63
|
+
...(fixture.root_mode === 'source_checkout_required' ? [] : [['setup', '--local-only', '--json']]),
|
|
64
|
+
...(normalized.setup || [])
|
|
65
|
+
];
|
|
66
|
+
const setupResults = setup.map((args) => spawnSks(sourceRoot, projectRoot, args));
|
|
59
67
|
const command = normalized.command || normalized.args || [];
|
|
60
|
-
const result = command.length ? spawnSks(
|
|
68
|
+
const result = command.length ? spawnSks(sourceRoot, projectRoot, command) : { status: 0, signal: null, ok: true, stdout_bytes: 0, stderr_bytes: 0, args: [] };
|
|
61
69
|
const missionId = result.mission_id || [...setupResults].reverse().find((row) => row.mission_id)?.mission_id || null;
|
|
62
70
|
return {
|
|
63
71
|
args: command,
|
|
@@ -71,9 +79,9 @@ function executeCommand(root, spec) {
|
|
|
71
79
|
};
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
function spawnSks(
|
|
75
|
-
const result = spawnSync(process.execPath, [path.join(
|
|
76
|
-
cwd:
|
|
82
|
+
function spawnSks(sourceRoot, projectRoot, args = []) {
|
|
83
|
+
const result = spawnSync(process.execPath, [path.join(sourceRoot, 'bin', 'sks.mjs'), ...args], {
|
|
84
|
+
cwd: projectRoot,
|
|
77
85
|
encoding: 'utf8',
|
|
78
86
|
timeout: 30_000,
|
|
79
87
|
env: { ...process.env, CI: 'true', SKS_SKIP_NPM_FRESHNESS_CHECK: '1' }
|
|
@@ -91,6 +99,26 @@ function spawnSks(root, args = []) {
|
|
|
91
99
|
};
|
|
92
100
|
}
|
|
93
101
|
|
|
102
|
+
function prepareHermeticFixtureRoot(sourceRoot, tempRoot) {
|
|
103
|
+
fs.mkdirSync(tempRoot, { recursive: true });
|
|
104
|
+
const packageFile = path.join(tempRoot, 'package.json');
|
|
105
|
+
if (!fs.existsSync(packageFile)) {
|
|
106
|
+
fs.writeFileSync(packageFile, `${JSON.stringify({ name: 'sks-hermetic-fixture', private: true, version: '0.0.0' }, null, 2)}\n`);
|
|
107
|
+
}
|
|
108
|
+
const readme = path.join(tempRoot, 'README.md');
|
|
109
|
+
if (!fs.existsSync(readme)) fs.writeFileSync(readme, '# SKS Hermetic Fixture\n');
|
|
110
|
+
copyFixtureFile(sourceRoot, tempRoot, 'test/fixtures/images/one-by-one.png');
|
|
111
|
+
return tempRoot;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function copyFixtureFile(sourceRoot, tempRoot, rel) {
|
|
115
|
+
const src = path.join(sourceRoot, rel);
|
|
116
|
+
const dest = path.join(tempRoot, rel);
|
|
117
|
+
if (!fs.existsSync(src) || fs.existsSync(dest)) return;
|
|
118
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
119
|
+
fs.copyFileSync(src, dest);
|
|
120
|
+
}
|
|
121
|
+
|
|
94
122
|
function parseJsonOutput(text = '') {
|
|
95
123
|
const trimmed = String(text || '').trim();
|
|
96
124
|
if (!trimmed) return null;
|
|
@@ -198,7 +226,7 @@ export function validateImageVoxelArtifact(file, { requireAnchors = true, requir
|
|
|
198
226
|
}
|
|
199
227
|
|
|
200
228
|
export function validateNoPlaintextSecrets(root) {
|
|
201
|
-
const secretPattern = /(sk-proj
|
|
229
|
+
const secretPattern = /(sk-proj-[A-Za-z0-9_-]{8,}|sk-clb-[A-Za-z0-9_-]{8,}|github_pat_[A-Za-z0-9_]{8,}|(?:CODEX_ACCESS_TOKEN|OPENAI_API_KEY)\s*[:=]\s*["']?(?:sk-[A-Za-z0-9_-]{8,}|[A-Za-z0-9_-]{32,}))/;
|
|
202
230
|
const reportDir = path.join(root, '.sneakoscope');
|
|
203
231
|
if (!fs.existsSync(reportDir)) return true;
|
|
204
232
|
const stack = [reportDir];
|
|
@@ -31,7 +31,7 @@ const FIXTURES = Object.freeze({
|
|
|
31
31
|
'cli-hproof': fixture('mock', 'sks hproof check latest', ['completion-proof.json'], 'pass'),
|
|
32
32
|
'cli-proof-field': fixture('static', 'sks proof-field scan --json --intent fixture', [], 'pass'),
|
|
33
33
|
'cli-recallpulse': fixture('mock', 'sks recallpulse status latest --json', ['recallpulse-report.json'], 'pass'),
|
|
34
|
-
'cli-scouts': fixture('execute_and_validate_artifacts', 'sks scouts run latest --mock --json', ['scout-team-plan.json', 'scout-consensus.json', 'scout-handoff.md', 'scout-gate.json'], 'pass'),
|
|
34
|
+
'cli-scouts': fixture('execute_and_validate_artifacts', 'sks scouts run latest --engine local-static --mock --json', ['scout-team-plan.json', 'scout-consensus.json', 'scout-handoff.md', 'scout-gate.json', 'scout-engine-result.json'], 'pass'),
|
|
35
35
|
'cli-scout': fixture('mock', 'sks scout status latest --json', ['scout-gate.json'], 'pass'),
|
|
36
36
|
'cli-gx': fixture('mock', 'sks gx validate fixture', ['gx-validation.json'], 'pass'),
|
|
37
37
|
'cli-perf': fixture('static', 'sks perf cold-start --json --iterations 1', [], 'pass'),
|
|
@@ -63,12 +63,58 @@ const FIXTURES = Object.freeze({
|
|
|
63
63
|
'route-db': fixture('execute_and_validate_artifacts', 'sks db check --sql "SELECT 1" --json', ['completion-proof.json', 'db-operation-report.json'], 'pass'),
|
|
64
64
|
'route-wiki': fixture('execute_and_validate_artifacts', 'sks wiki image-ingest test/fixtures/images/one-by-one.png --json', [{ path: 'completion-proof.json', schema: 'sks.completion-proof.v1' }, { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }], 'pass'),
|
|
65
65
|
'route-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'gx-validation.json'], 'pass'),
|
|
66
|
-
'route-five-scout-intake': fixture('mock', 'sks scouts validate latest --json', ['scout-team-plan.json', 'scout-consensus.json', 'scout-handoff.md', 'scout-gate.json'], 'pass'),
|
|
66
|
+
'route-five-scout-intake': fixture('mock', 'sks scouts validate latest --strict --json', ['scout-team-plan.json', 'scout-consensus.json', 'scout-handoff.md', 'scout-gate.json'], 'pass'),
|
|
67
67
|
'proof-scout-evidence': fixture('mock', 'sks team "fixture" --mock --json', ['completion-proof.json', 'scout-gate.json'], 'pass')
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
const STATIC_CONTRACT_FEATURES = new Set([
|
|
71
|
+
'cli-wizard',
|
|
72
|
+
'cli-bootstrap',
|
|
73
|
+
'cli-deps',
|
|
74
|
+
'cli-auth',
|
|
75
|
+
'cli-openclaw',
|
|
76
|
+
'cli-tmux',
|
|
77
|
+
'cli-mad',
|
|
78
|
+
'cli-auto-review',
|
|
79
|
+
'cli-commit',
|
|
80
|
+
'cli-commit-and-push',
|
|
81
|
+
'cli-context7',
|
|
82
|
+
'cli-all-features',
|
|
83
|
+
'cli-eval',
|
|
84
|
+
'cli-harness',
|
|
85
|
+
'cli-team',
|
|
86
|
+
'cli-reasoning',
|
|
87
|
+
'cli-profile',
|
|
88
|
+
'handler-$',
|
|
89
|
+
'handler-autoresearch',
|
|
90
|
+
'handler-autoreview',
|
|
91
|
+
'handler-computer-use',
|
|
92
|
+
'handler-cu',
|
|
93
|
+
'handler-dollars',
|
|
94
|
+
'handler-mad-sks',
|
|
95
|
+
'handler-postinstall',
|
|
96
|
+
'route-sks',
|
|
97
|
+
'route-commit',
|
|
98
|
+
'route-commit-and-push',
|
|
99
|
+
'route-help'
|
|
100
|
+
]);
|
|
101
|
+
|
|
70
102
|
export function fixtureForFeature(featureId) {
|
|
71
|
-
|
|
103
|
+
if (FIXTURES[featureId]) return FIXTURES[featureId];
|
|
104
|
+
if (STATIC_CONTRACT_FEATURES.has(featureId)) {
|
|
105
|
+
return fixture('static', `explicit static contract fixture: ${featureId}`, [], 'pass', {
|
|
106
|
+
quality: 'static_contract',
|
|
107
|
+
root_mode: 'source_checkout_required'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (String(featureId || '').startsWith('skill-')) {
|
|
111
|
+
return fixture('static', `skill contract: ${featureId}`, [], 'pass', { quality: 'static_contract', root_mode: 'source_checkout_required' });
|
|
112
|
+
}
|
|
113
|
+
return fixture('not_available', null, [], 'missing', {
|
|
114
|
+
quality: 'missing',
|
|
115
|
+
fallback_removed: true,
|
|
116
|
+
reason: 'No explicit fixture registered for this feature.'
|
|
117
|
+
});
|
|
72
118
|
}
|
|
73
119
|
|
|
74
120
|
export function fixtureSummary(features = []) {
|
|
@@ -96,6 +142,7 @@ export function validateFeatureFixtures(features = []) {
|
|
|
96
142
|
continue;
|
|
97
143
|
}
|
|
98
144
|
if (!['contract', 'execute', 'execute_and_validate_artifacts', 'mock', 'static', 'real_optional', 'not_available'].includes(fx.kind)) blockers.push(`${feature.id}:fixture_kind`);
|
|
145
|
+
if (!['real_optional', 'execute_and_validate_artifacts', 'execute', 'mock', 'static_contract', 'missing'].includes(fx.quality)) blockers.push(`${feature.id}:fixture_quality`);
|
|
99
146
|
if (!['pass', 'missing', 'blocked', 'not_required'].includes(fx.status)) blockers.push(`${feature.id}:fixture_status`);
|
|
100
147
|
if ((fx.kind === 'mock' || fx.kind === 'static') && !fx.command && fx.status !== 'not_required') blockers.push(`${feature.id}:fixture_command`);
|
|
101
148
|
if (!Array.isArray(fx.expected_artifacts)) blockers.push(`${feature.id}:fixture_expected_artifacts`);
|
|
@@ -103,6 +150,18 @@ export function validateFeatureFixtures(features = []) {
|
|
|
103
150
|
return { ok: blockers.length === 0, blockers };
|
|
104
151
|
}
|
|
105
152
|
|
|
106
|
-
function fixture(kind, command, expected_artifacts, status) {
|
|
107
|
-
|
|
153
|
+
function fixture(kind, command, expected_artifacts, status, extra = {}) {
|
|
154
|
+
const quality = extra.quality || (kind === 'static' ? 'static_contract' : kind);
|
|
155
|
+
const rootMode = extra.root_mode || (kind === 'execute_and_validate_artifacts' || kind === 'execute' || kind === 'mock' ? 'hermetic_temp_project' : 'source_checkout_required');
|
|
156
|
+
return {
|
|
157
|
+
kind,
|
|
158
|
+
quality,
|
|
159
|
+
root_mode: rootMode,
|
|
160
|
+
command,
|
|
161
|
+
expected_artifacts,
|
|
162
|
+
status,
|
|
163
|
+
explicit: true,
|
|
164
|
+
fallback_removed: true,
|
|
165
|
+
...extra
|
|
166
|
+
};
|
|
108
167
|
}
|
|
@@ -149,6 +149,7 @@ export function buildAllFeaturesSelftest(registry, opts = {}) {
|
|
|
149
149
|
checkRow('voxel_triwiki_contracts_present', registry.features.every((feature) => Boolean(feature.voxel_triwiki_integration)), missingFeatureField(registry, 'voxel_triwiki_integration')),
|
|
150
150
|
checkRow('failure_contracts_present', registry.features.every((feature) => Array.isArray(feature.known_gaps)), missingFeatureField(registry, 'known_gaps')),
|
|
151
151
|
checkRow('fixture_contracts_present', fixtures.ok, fixtures.blockers),
|
|
152
|
+
checkRow('fixture_fallback_removed', registry.features.every((feature) => feature.fixture?.fallback_removed === true && feature.fixture?.status !== 'missing'), registry.features.filter((feature) => feature.fixture?.fallback_removed !== true || feature.fixture?.status === 'missing').map((feature) => feature.id)),
|
|
152
153
|
checkRow('proof_fixture_contract_present', registry.features.some((feature) => feature.id === 'cli-proof' && feature.fixture?.status === 'pass'), ['cli-proof']),
|
|
153
154
|
checkRow('voxel_fixture_contract_present', registry.features.some((feature) => feature.id === 'cli-wiki' && feature.fixture?.expected_artifacts?.some((artifact) => expectedArtifactPath(artifact).includes('image-voxel-ledger'))), ['cli-wiki']),
|
|
154
155
|
checkRow('five_scout_intake_contract_present', registry.features.some((feature) => feature.id === 'route-five-scout-intake'), ['route-five-scout-intake']),
|
|
@@ -265,7 +266,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
|
|
|
265
266
|
'cli-wiki': ['wiki', 'image-ingest', 'test/fixtures/images/one-by-one.png', '--json'],
|
|
266
267
|
'cli-codex-lb': ['codex-lb', 'metrics', '--json'],
|
|
267
268
|
'cli-hooks': ['hooks', 'trust-report', '--json'],
|
|
268
|
-
'cli-scouts': ['scouts', 'run', 'latest', '--mock', '--json'],
|
|
269
|
+
'cli-scouts': ['scouts', 'run', 'latest', '--engine', 'local-static', '--mock', '--json'],
|
|
269
270
|
'cli-perf': ['perf', 'cold-start', '--json', '--iterations', '1'],
|
|
270
271
|
'cli-rust': ['rust', 'smoke', '--json'],
|
|
271
272
|
'route-team': ['team', 'fixture', '--mock', '--json'],
|
|
@@ -303,14 +304,15 @@ export function renderFeatureInventoryMarkdown(registry) {
|
|
|
303
304
|
'',
|
|
304
305
|
'## Stable / Beta / Labs Map',
|
|
305
306
|
'',
|
|
306
|
-
'| Feature | Category | Maturity | Commands / Routes | Fixture | Known Gaps |',
|
|
307
|
-
'| --- | --- | --- | --- | --- | --- |'
|
|
307
|
+
'| Feature | Category | Maturity | Commands / Routes | Fixture | Quality | Known Gaps |',
|
|
308
|
+
'| --- | --- | --- | --- | --- | --- | --- |'
|
|
308
309
|
];
|
|
309
310
|
for (const feature of registry.features) {
|
|
310
311
|
const commands = [...(feature.commands || []), ...(feature.aliases || [])].map(markdownTableCell).join('<br>');
|
|
311
312
|
const gaps = (feature.known_gaps || []).map(markdownTableCell).join('<br>') || 'none recorded';
|
|
312
313
|
const fixture = feature.fixture ? `${feature.fixture.kind}:${feature.fixture.status}` : 'missing';
|
|
313
|
-
|
|
314
|
+
const quality = feature.fixture?.quality || 'missing';
|
|
315
|
+
lines.push(`| \`${feature.id}\` | ${feature.category} | ${feature.maturity} | ${commands || '-'} | ${fixture} | ${quality} | ${gaps} |`);
|
|
314
316
|
}
|
|
315
317
|
lines.push('', '## Unmapped Coverage', '');
|
|
316
318
|
for (const [kind, values] of Object.entries(coverage.unmapped || {})) {
|
|
@@ -405,7 +407,7 @@ function routeFeature(route) {
|
|
|
405
407
|
function fiveScoutIntakeFeature() {
|
|
406
408
|
return baseFeature({
|
|
407
409
|
id: 'route-five-scout-intake',
|
|
408
|
-
commands: ['sks scouts run latest --mock --json'],
|
|
410
|
+
commands: ['sks scouts run latest --engine local-static --mock --json'],
|
|
409
411
|
aliases: ['sks scout run latest --json'],
|
|
410
412
|
category: 'proof-route',
|
|
411
413
|
maturity: 'beta',
|
package/src/core/fsx.mjs
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
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.9.
|
|
8
|
+
export const PACKAGE_VERSION = '0.9.18';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|