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.
Files changed (36) hide show
  1. package/README.md +11 -6
  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/package.json +8 -5
  6. package/src/core/commands/command-utils.mjs +1 -1
  7. package/src/core/commands/ppt-command.mjs +1 -1
  8. package/src/core/commands/scouts-command.mjs +74 -6
  9. package/src/core/feature-fixture-runner.mjs +42 -14
  10. package/src/core/feature-fixtures.mjs +64 -5
  11. package/src/core/feature-registry.mjs +7 -5
  12. package/src/core/fsx.mjs +1 -1
  13. package/src/core/pipeline/active-context.mjs +3 -0
  14. package/src/core/pipeline/pipeline-plan-writer.mjs +8 -0
  15. package/src/core/pipeline/plan-schema.mjs +4 -0
  16. package/src/core/pipeline/prompt-context.mjs +6 -0
  17. package/src/core/pipeline/route-prep.mjs +3 -0
  18. package/src/core/pipeline/scout-stage-policy.mjs +4 -0
  19. package/src/core/pipeline/stage-policy.mjs +4 -0
  20. package/src/core/pipeline/stop-gate.mjs +10 -0
  21. package/src/core/pipeline/validation.mjs +3 -0
  22. package/src/core/pipeline-runtime.mjs +1693 -0
  23. package/src/core/pipeline.mjs +34 -1693
  24. package/src/core/scouts/engines/codex-app-subagent-engine.mjs +9 -0
  25. package/src/core/scouts/engines/codex-exec-parallel-engine.mjs +52 -0
  26. package/src/core/scouts/engines/local-static-engine.mjs +10 -0
  27. package/src/core/scouts/engines/scout-engine-base.mjs +118 -0
  28. package/src/core/scouts/engines/scout-engine-detect.mjs +63 -0
  29. package/src/core/scouts/engines/scout-engine-policy.mjs +76 -0
  30. package/src/core/scouts/engines/sequential-fallback-engine.mjs +10 -0
  31. package/src/core/scouts/engines/tmux-lane-engine.mjs +9 -0
  32. package/src/core/scouts/scout-proof-evidence.mjs +10 -0
  33. package/src/core/scouts/scout-readonly-guard.mjs +80 -0
  34. package/src/core/scouts/scout-runner.mjs +197 -23
  35. package/src/core/scouts/scout-schema.mjs +4 -2
  36. 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.17` removes the old route command monolith, adds a read-only five-scout intake before serious route implementation, automatically seals serious route fixture paths with Completion Proof, binds visual/UI claims to Image Voxel TriWiki anchors and relations, and release-gates real command fixtures, route modularity, Rust fallback parity, and DB safety evidence.
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
  ![Sneakoscope Codex architecture and pipeline](https://raw.githubusercontent.com/mandarange/Sneakoscope-Codex/dev/docs/assets/sneakoscope-architecture-pipeline.jpg)
8
8
 
9
- ## 0.9.17 Current Release
9
+ ## 0.9.18 Current Release
10
10
 
11
- `0.9.17` completes the route modularity cleanup that started in 0.9.14. `src/core/commands/route-cli.mjs` is gone; every serious route now owns a focused command module, and release checks fail if the monolith returns or if command modules drift past their budget.
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
- - Serious routes run or validate five read-only scouts for code surface, verification, safety/DB, visual/Voxel evidence, and simplification/integration before proof is written.
17
- - E2E route tests run actual route commands such as `sks team fixture --mock --json`, `sks qa-loop run <mission> --mock --json`, and `sks wiki image-ingest ... --json`.
18
- - Feature fixtures execute deterministic allowlisted commands and validate artifacts generated by those commands, including mission-local proof, visual ledgers, DB reports, and GX/Wiki evidence.
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)
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "0.9.17"
79
+ version = "0.9.18"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "0.9.17"
3
+ version = "0.9.18"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -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.17"),
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.17",
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
- "scouts:selftest": "node ./bin/sks.mjs scouts run latest --mock --json",
53
- "scouts:check": "node ./bin/sks.mjs scouts validate latest --json",
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 packcheck && npm run route-modularity:check && npm run command-budget:check && npm run feature:check && npm run all-features:selftest && 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",
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.17 route evidence.',
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
- const { id, dir, mission, created } = await resolveOrCreateScoutMission(root, missionArg, { mock, action });
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 && !flag(actionArgs, '--strict')) {
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 --mock --json
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 latestBefore = latestMissionId(root);
16
- const execution = execute && commandArgs ? executeCommand(root, commandArgs) : null;
17
- const latestAfter = execution?.mission_id || latestMissionId(root) || latestBefore;
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(root, tempRoot, artifact, { latestMissionId: latestAfter }))
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
- ok: (!execution || execution.ok) && artifactFailures.length === 0 && !(validateArtifacts && fixture.kind === 'execute_and_validate_artifacts' && expected.length && !execution),
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(root, spec) {
60
+ function executeCommand(sourceRoot, projectRoot, spec, fixture = {}) {
56
61
  const normalized = Array.isArray(spec) ? { command: spec } : spec;
57
- const setup = normalized.setup || [];
58
- const setupResults = setup.map((args) => spawnSks(root, args));
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(root, command) : { status: 0, signal: null, ok: true, stdout_bytes: 0, stderr_bytes: 0, args: [] };
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(root, args = []) {
75
- const result = spawnSync(process.execPath, [path.join(root, 'bin', 'sks.mjs'), ...args], {
76
- cwd: root,
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-|sk-clb-|github_pat_|CODEX_ACCESS_TOKEN|OPENAI_API_KEY)/;
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
- return FIXTURES[featureId] || fixture('static', 'sks features check --json', [], 'pass');
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
- return { kind, command, expected_artifacts, status };
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
- lines.push(`| \`${feature.id}\` | ${feature.category} | ${feature.maturity} | ${commands || '-'} | ${fixture} | ${gaps} |`);
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.17';
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
 
@@ -0,0 +1,3 @@
1
+ export {
2
+ activeRouteContext
3
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,8 @@
1
+ export {
2
+ PIPELINE_PLAN_ARTIFACT,
3
+ PIPELINE_PLAN_SCHEMA_VERSION,
4
+ routePrompt,
5
+ buildPipelinePlan,
6
+ writePipelinePlan,
7
+ validatePipelinePlan
8
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,4 @@
1
+ export {
2
+ PIPELINE_PLAN_ARTIFACT,
3
+ PIPELINE_PLAN_SCHEMA_VERSION
4
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,6 @@
1
+ export {
2
+ promptPipelineContext,
3
+ dfixQuickContext,
4
+ answerOnlyContext,
5
+ computerUseFastContext
6
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,3 @@
1
+ export {
2
+ prepareRoute
3
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,4 @@
1
+ export {
2
+ buildPipelinePlan,
3
+ validatePipelinePlan
4
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,4 @@
1
+ export {
2
+ buildPipelinePlan,
3
+ validatePipelinePlan
4
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,10 @@
1
+ export {
2
+ recordContext7Evidence,
3
+ recordSubagentEvidence,
4
+ subagentEvidence,
5
+ hasSubagentEvidence,
6
+ context7Evidence,
7
+ hasContext7DocsEvidence,
8
+ projectGateStatus,
9
+ evaluateStop
10
+ } from '../pipeline-runtime.mjs';
@@ -0,0 +1,3 @@
1
+ export {
2
+ validatePipelinePlan
3
+ } from '../pipeline-runtime.mjs';