sneakoscope 4.0.11 → 4.0.13

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 (28) hide show
  1. package/README.md +7 -7
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/core/commands/glm-command.js +11 -5
  7. package/dist/core/fsx.js +1 -1
  8. package/dist/core/providers/glm/bench/glm-bench-comparison.js +48 -0
  9. package/dist/core/providers/glm/bench/glm-bench-fixture.js +65 -0
  10. package/dist/core/providers/glm/bench/glm-bench-model-lock-proof.js +24 -0
  11. package/dist/core/providers/glm/bench/glm-bench-report.js +75 -0
  12. package/dist/core/providers/glm/bench/glm-benchmark-runner.js +219 -0
  13. package/dist/core/providers/glm/bench/glm-benchmark-types.js +2 -0
  14. package/dist/core/providers/glm/bench/glm-direct-bench-runner.js +73 -0
  15. package/dist/core/providers/glm/naruto/glm-naruto-apply-transaction.js +54 -8
  16. package/dist/core/providers/glm/naruto/glm-naruto-bench.js +4 -118
  17. package/dist/core/providers/glm/naruto/glm-naruto-command.js +20 -3
  18. package/dist/core/providers/glm/naruto/glm-naruto-final-seal.js +75 -0
  19. package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +41 -5
  20. package/dist/core/providers/glm/naruto/glm-naruto-targeted-checks.js +76 -0
  21. package/dist/core/providers/glm/naruto/glm-naruto-trace.js +50 -0
  22. package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +61 -19
  23. package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +26 -4
  24. package/dist/core/providers/glm/naruto/glm-naruto-worker-scheduler.js +178 -0
  25. package/dist/core/providers/glm/naruto/glm-naruto-worktree-worker.js +34 -4
  26. package/dist/core/version.js +1 -1
  27. package/package.json +1 -1
  28. package/dist/core/providers/glm/glm-bench.js +0 -127
@@ -3,22 +3,28 @@ import path from 'node:path';
3
3
  import { sha256, writeJsonAtomic, writeTextAtomic } from '../../../fsx.js';
4
4
  import { parseUnifiedDiffPatch } from '../glm-patch-parser.js';
5
5
  import { combineGlmNarutoPatches } from './glm-naruto-combined-patch.js';
6
+ import { runGlmNarutoTargetedChecks } from './glm-naruto-targeted-checks.js';
6
7
  export async function runGlmNarutoApplyTransaction(input) {
7
8
  const preStatus = await gitText(input.cwd, ['status', '--short']);
8
9
  const preDiff = await gitText(input.cwd, ['diff', '--binary']);
9
10
  const patch = combineGlmNarutoPatches(input.envelopes, input.selectedPatchIds);
10
11
  const parsed = parseUnifiedDiffPatch(patch);
12
+ const dirtyTouchedPaths = await dirtyPaths(input.cwd, parsed.touchedPaths);
11
13
  const patchPath = path.join(input.artifactDir, 'selected-combined.patch');
12
14
  await writeTextAtomic(patchPath, patch);
13
15
  const blockers = [];
14
16
  let applyCheckPassed = false;
15
17
  let applyPassed = false;
18
+ let targetedChecksPassed = null;
16
19
  let rollbackAttempted = false;
17
20
  let rollbackPassed = null;
18
21
  let finalStatus = 'blocked';
19
22
  if (!patch.trim()) {
20
23
  blockers.push('combined_patch_empty');
21
24
  }
25
+ else if (dirtyTouchedPaths.length > 0 && !input.allowDirtyApply) {
26
+ blockers.push(`dirty_touched_paths_before_apply:${dirtyTouchedPaths.join(',')}`);
27
+ }
22
28
  else {
23
29
  const checked = await gitApply(input.cwd, patch, ['apply', '--check', '--whitespace=nowarn', '-']);
24
30
  applyCheckPassed = checked.code === 0;
@@ -28,16 +34,41 @@ export async function runGlmNarutoApplyTransaction(input) {
28
34
  const applied = await gitApply(input.cwd, patch, ['apply', '--whitespace=nowarn', '-']);
29
35
  applyPassed = applied.code === 0;
30
36
  if (applyPassed) {
31
- finalStatus = 'applied';
37
+ const targeted = await runGlmNarutoTargetedChecks({
38
+ cwd: input.cwd,
39
+ touchedPaths: parsed.touchedPaths,
40
+ artifactDir: input.artifactDir,
41
+ ...(input.strictChecks !== undefined ? { strictChecks: input.strictChecks } : {})
42
+ });
43
+ targetedChecksPassed = targeted.ok;
44
+ if (targeted.ok) {
45
+ finalStatus = 'applied';
46
+ }
47
+ else {
48
+ blockers.push(...targeted.blockers);
49
+ if (!input.noRollback) {
50
+ rollbackAttempted = true;
51
+ const rollback = await gitApply(input.cwd, patch, ['apply', '-R', '--whitespace=nowarn', '-']);
52
+ rollbackPassed = rollback.code === 0;
53
+ finalStatus = rollbackPassed ? 'rolled_back' : 'blocked';
54
+ if (!rollbackPassed)
55
+ blockers.push(rollback.stderr || rollback.stdout || 'rollback_reverse_patch_failed');
56
+ }
57
+ else {
58
+ finalStatus = 'blocked';
59
+ }
60
+ }
32
61
  }
33
62
  else {
34
63
  blockers.push(applied.stderr || applied.stdout || 'git_apply_failed');
35
- rollbackAttempted = true;
36
- const rollback = await gitApply(input.cwd, patch, ['apply', '-R', '--whitespace=nowarn', '-']);
37
- rollbackPassed = rollback.code === 0;
38
- finalStatus = rollbackPassed ? 'rolled_back' : 'blocked';
39
- if (!rollbackPassed)
40
- blockers.push(rollback.stderr || rollback.stdout || 'rollback_reverse_patch_failed');
64
+ if (!input.noRollback) {
65
+ rollbackAttempted = true;
66
+ const rollback = await gitApply(input.cwd, patch, ['apply', '-R', '--whitespace=nowarn', '-']);
67
+ rollbackPassed = rollback.code === 0;
68
+ finalStatus = rollbackPassed ? 'rolled_back' : 'blocked';
69
+ if (!rollbackPassed)
70
+ blockers.push(rollback.stderr || rollback.stdout || 'rollback_reverse_patch_failed');
71
+ }
41
72
  }
42
73
  }
43
74
  }
@@ -47,12 +78,15 @@ export async function runGlmNarutoApplyTransaction(input) {
47
78
  mission_id: input.missionId,
48
79
  selected_patch_ids: input.selectedPatchIds,
49
80
  touched_paths: parsed.touchedPaths,
81
+ dirty_touched_paths_before_apply: dirtyTouchedPaths,
82
+ dirty_policy: input.allowDirtyApply ? 'allow' : 'block',
50
83
  pre_status: preStatus,
51
84
  pre_diff_sha256: sha256(preDiff),
85
+ post_diff_sha256: sha256(postDiff),
52
86
  combined_patch_sha256: sha256(patch),
53
87
  apply_check_passed: applyCheckPassed,
54
88
  apply_passed: applyPassed,
55
- targeted_checks_passed: null,
89
+ targeted_checks_passed: targetedChecksPassed,
56
90
  rollback_attempted: rollbackAttempted,
57
91
  rollback_passed: rollbackPassed,
58
92
  final_status: finalStatus,
@@ -76,6 +110,18 @@ function gitText(cwd, args) {
76
110
  child.on('close', () => resolve(stdout));
77
111
  });
78
112
  }
113
+ async function dirtyPaths(cwd, paths) {
114
+ if (paths.length === 0)
115
+ return [];
116
+ const status = await gitText(cwd, ['status', '--short', '--', ...paths]);
117
+ return status.split(/\r?\n/)
118
+ .map((line) => line.trimEnd())
119
+ .filter(Boolean)
120
+ .map((line) => {
121
+ const raw = line.slice(3).trim();
122
+ return raw.includes(' -> ') ? raw.split(' -> ').pop().trim() : raw;
123
+ });
124
+ }
79
125
  function gitApply(cwd, patch, args) {
80
126
  return new Promise((resolve) => {
81
127
  const child = spawn('git', [...args], { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
@@ -1,121 +1,7 @@
1
- import { nowIso } from '../../../fsx.js';
2
- import path from 'node:path';
3
- import fsp from 'node:fs/promises';
4
- import os from 'node:os';
5
- import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
6
- import { resolveOpenRouterApiKey } from '../../openrouter/openrouter-secret-store.js';
1
+ import { runGlmBenchmark } from '../bench/glm-benchmark-runner.js';
2
+ import { runGlmDirectSpeedRun } from '../glm-direct-run.js';
7
3
  import { runGlmNarutoMission } from './glm-naruto-orchestrator.js';
8
- import { summarizeGlmNarutoWorkerMetrics } from './glm-naruto-metrics.js';
9
- export async function runGlmNarutoBench(root, args = []) {
10
- const live = args.includes('--live');
11
- const execute = args.includes('--execute');
12
- const started = Date.now();
13
- if (execute && !live) {
14
- return blocked(root, ['execute_requires_live_flag']);
15
- }
16
- if (!live) {
17
- return {
18
- schema: 'sks.glm-naruto-bench.v1',
19
- version: '4.0.11',
20
- generated_at: nowIso(),
21
- status: 'dry_run',
22
- model: GLM_52_OPENROUTER_MODEL,
23
- gpt_fallback_allowed: false,
24
- summary: {
25
- simulated_workers: 12,
26
- simulated_waves: 3,
27
- simulated_patch_candidates: 24,
28
- simulated_gate_passed: 18,
29
- simulated_mergeable: 12,
30
- wall_clock_ms: Date.now() - started
31
- },
32
- warnings: ['dry_run_no_live_api_calls']
33
- };
34
- }
35
- const key = await resolveOpenRouterApiKey({ env: process.env });
36
- if (!key.key)
37
- return blocked(root, ['live_bench_requires_openrouter_key']);
38
- const fixture = await fsp.mkdtemp(path.join(os.tmpdir(), 'sks-glm-naruto-live-bench-'));
39
- await fsp.mkdir(path.join(fixture, 'src'), { recursive: true });
40
- await fsp.writeFile(path.join(fixture, 'src', 'bench-target.ts'), 'export const value = 1;\n', 'utf8');
41
- const cases = [];
42
- for (const workers of [1, 4, 8, 12]) {
43
- const caseStarted = Date.now();
44
- const result = await runGlmNarutoMission({
45
- cwd: fixture,
46
- task: 'Change src/bench-target.ts so value is 2. Return the smallest patch only.',
47
- args: ['--bench', '--live', '--no-apply'],
48
- missionId: `glm-naruto-live-bench-${workers}-${Date.now()}`,
49
- maxWorkers: workers,
50
- noApply: true
51
- });
52
- const traces = await readWorkerTraces(result.artifact_dir);
53
- const metrics = summarizeGlmNarutoWorkerMetrics(traces);
54
- cases.push({
55
- name: workers === 1 ? 'direct single GLM' : `GLM Naruto ${workers} workers`,
56
- workers,
57
- wall_clock_ms: Date.now() - caseStarted,
58
- p50_ttft_ms: metrics.p50_ttft_ms,
59
- p90_ttft_ms: metrics.p90_ttft_ms,
60
- p50_total_ms: metrics.p50_total_ms,
61
- p90_total_ms: metrics.p90_total_ms,
62
- candidate_count: result.patch_candidates,
63
- gate_pass_rate: result.patch_candidates ? result.gate_passed_candidates / result.patch_candidates : 0,
64
- verifier_pass_rate: metrics.verifier_pass_rate,
65
- merge_success: result.mergeable_candidates > 0,
66
- cached_tokens_sum: metrics.cached_tokens_sum,
67
- cache_write_tokens_sum: metrics.cache_write_tokens_sum,
68
- reasoning_tokens_sum: metrics.reasoning_tokens_sum,
69
- workers_completed: metrics.workers_completed,
70
- workers_failed: metrics.workers_failed
71
- });
72
- }
73
- return {
74
- schema: 'sks.glm-naruto-bench.v1',
75
- version: '4.0.11',
76
- generated_at: nowIso(),
77
- status: 'live',
78
- model: GLM_52_OPENROUTER_MODEL,
79
- gpt_fallback_allowed: false,
80
- cases,
81
- summary: {
82
- simulated_workers: Math.max(...cases.map((row) => row.workers)),
83
- simulated_waves: cases.length,
84
- simulated_patch_candidates: cases.reduce((sum, row) => sum + row.candidate_count, 0),
85
- simulated_gate_passed: cases.reduce((sum, row) => sum + Math.round(row.candidate_count * row.gate_pass_rate), 0),
86
- simulated_mergeable: cases.filter((row) => row.merge_success).length,
87
- wall_clock_ms: Date.now() - started
88
- },
89
- warnings: ['live_bench_no_apply_temp_repo']
90
- };
91
- }
92
- function blocked(root, warnings) {
93
- return {
94
- schema: 'sks.glm-naruto-bench.v1',
95
- version: '4.0.11',
96
- generated_at: nowIso(),
97
- status: 'blocked',
98
- model: GLM_52_OPENROUTER_MODEL,
99
- gpt_fallback_allowed: false,
100
- summary: {
101
- simulated_workers: 0,
102
- simulated_waves: 0,
103
- simulated_patch_candidates: 0,
104
- simulated_gate_passed: 0,
105
- simulated_mergeable: 0,
106
- wall_clock_ms: 0
107
- },
108
- warnings
109
- };
110
- }
111
- async function readWorkerTraces(artifactDir) {
112
- if (!artifactDir)
113
- return [];
114
- try {
115
- return JSON.parse(await fsp.readFile(path.join(artifactDir, 'worker-traces.json'), 'utf8'));
116
- }
117
- catch {
118
- return [];
119
- }
4
+ export async function runGlmNarutoBench(root, args = [], deps = {}) {
5
+ return runGlmBenchmark(root, args, deps);
120
6
  }
121
7
  //# sourceMappingURL=glm-naruto-bench.js.map
@@ -4,13 +4,24 @@ import { runGlmNarutoMission } from './glm-naruto-orchestrator.js';
4
4
  import { runGlmNarutoBench } from './glm-naruto-bench.js';
5
5
  export async function glmNarutoCommand(args = []) {
6
6
  if (flag(args, '--bench')) {
7
+ // --compare is an alias; the benchmark always compares direct vs Naruto.
7
8
  const result = await runGlmNarutoBench(process.cwd(), args);
8
9
  if (flag(args, '--json'))
9
10
  printJson(result);
10
11
  else if (result.status === 'blocked')
11
- console.error(`GLM Naruto bench blocked: ${result.warnings.join(', ')}`);
12
- else
13
- console.log(`GLM Naruto bench: ${result.status} workers=${result.summary.simulated_workers} candidates=${result.summary.simulated_patch_candidates}`);
12
+ console.error(`GLM benchmark blocked: ${result.warnings.join(', ')}`);
13
+ else if (result.status === 'dry_run')
14
+ console.log(`GLM benchmark: dry-run (use --live for real measurement)`);
15
+ else {
16
+ const direct = result.cases.find((c) => c.implementation_path === 'direct-glm');
17
+ const best = result.comparison.best_naruto_runner_id;
18
+ console.log(`GLM benchmark: ${result.status} (${result.cases.length} cases)`);
19
+ if (direct)
20
+ console.log(` Direct GLM: ${direct.wall_clock_ms}ms`);
21
+ if (result.comparison.best_naruto_wall_clock_ms !== null)
22
+ console.log(` Best Naruto: ${best} at ${result.comparison.best_naruto_wall_clock_ms}ms`);
23
+ console.log(` Recommendation: ${result.comparison.recommendation}`);
24
+ }
14
25
  return result;
15
26
  }
16
27
  const positional = positionalArgs(args).map(String);
@@ -46,6 +57,9 @@ export async function glmNarutoCommand(args = []) {
46
57
  const allowPatchEnvelopeFallback = flag(args, '--allow-patch-envelope-fallback');
47
58
  const noApply = flag(args, '--no-apply');
48
59
  const skipVerifier = flag(args, '--skip-verifier');
60
+ const allowDirtyApply = flag(args, '--allow-dirty-apply');
61
+ const noRollback = flag(args, '--no-rollback');
62
+ const strictChecks = flag(args, '--strict-checks');
49
63
  const mergeStrategy = readOption(args, '--merge-strategy', 'deterministic');
50
64
  const result = await runGlmNarutoMission({
51
65
  cwd: process.cwd(),
@@ -62,6 +76,9 @@ export async function glmNarutoCommand(args = []) {
62
76
  cleanupWorktrees,
63
77
  noApply: noApply || flag(args, '--dry-run'),
64
78
  skipVerifier,
79
+ allowDirtyApply,
80
+ noRollback,
81
+ strictChecks,
65
82
  mergeStrategy
66
83
  });
67
84
  if (flag(args, '--json')) {
@@ -0,0 +1,75 @@
1
+ import path from 'node:path';
2
+ import { writeJsonAtomic } from '../../../fsx.js';
3
+ import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
4
+ export async function writeGlmNarutoFinalSeal(input) {
5
+ const mismatches = [
6
+ ...input.envelopes.filter((env) => env.model !== GLM_52_OPENROUTER_MODEL || env.gpt_fallback_allowed !== false).map((env) => `envelope:${env.worker_id}`),
7
+ ...input.traces.filter((trace) => trace.model !== GLM_52_OPENROUTER_MODEL).map((trace) => `trace:${trace.worker_id}`)
8
+ ];
9
+ const isolationViolations = input.isolationPolicy.selected === 'blocked' ? input.isolationPolicy.blockers : [];
10
+ const status = finalSealStatus({
11
+ result: input.result,
12
+ secretOk: input.secretAudit.ok,
13
+ mismatches,
14
+ isolationViolations,
15
+ queueDrained: input.scheduler.queue_drained
16
+ });
17
+ const seal = {
18
+ schema: 'sks.glm-naruto-final-seal.v1',
19
+ mission_id: input.missionId,
20
+ status,
21
+ model_lock: {
22
+ model: GLM_52_OPENROUTER_MODEL,
23
+ gpt_fallback_allowed: false,
24
+ requests_checked: input.envelopes.length + input.traces.length,
25
+ mismatches
26
+ },
27
+ isolation: {
28
+ selected: input.isolationPolicy.selected,
29
+ workers_write_main_workspace: false,
30
+ violations: isolationViolations
31
+ },
32
+ scheduler: {
33
+ bounded: true,
34
+ max_observed_active_workers: input.scheduler.max_observed_active_workers,
35
+ queue_drained: input.scheduler.queue_drained,
36
+ backpressure_events: input.scheduler.backpressure_events
37
+ },
38
+ candidates: {
39
+ total: input.envelopes.length,
40
+ gate_passed: input.envelopes.filter((env) => env.status === 'gate_passed').length,
41
+ verifier_passed: input.envelopes.filter((env) => env.verification_passed === true).length,
42
+ selected: input.selectedPatchIds
43
+ },
44
+ apply: {
45
+ attempted: input.applyTransaction !== null,
46
+ transaction_path: input.applyTransaction ? path.join(input.artifactDir, 'apply-transaction.json') : null,
47
+ final_status: input.applyTransaction?.final_status ?? null,
48
+ rollback_attempted: input.applyTransaction?.rollback_attempted ?? false,
49
+ rollback_passed: input.applyTransaction?.rollback_passed ?? null
50
+ },
51
+ secret_audit: {
52
+ ok: input.secretAudit.ok,
53
+ findings: input.secretAudit.findings ?? []
54
+ },
55
+ stop_gate: {
56
+ path: input.stopGatePath,
57
+ passed: input.stopGatePassed
58
+ }
59
+ };
60
+ const out = path.join(input.artifactDir, 'final-seal.json');
61
+ await writeJsonAtomic(out, seal);
62
+ return { seal, path: out, passed: seal.status === 'passed' };
63
+ }
64
+ function finalSealStatus(input) {
65
+ if (!input.secretOk || input.mismatches.length > 0 || input.isolationViolations.length > 0 || !input.queueDrained)
66
+ return 'blocked';
67
+ if (input.result.ok)
68
+ return 'passed';
69
+ if (input.result.status === 'partial_candidates')
70
+ return 'partial';
71
+ if (input.result.status === 'blocked')
72
+ return 'blocked';
73
+ return 'failed';
74
+ }
75
+ //# sourceMappingURL=glm-naruto-final-seal.js.map
@@ -20,6 +20,7 @@ import { getGitHead, getGitRoot } from './glm-naruto-worktree-manager.js';
20
20
  import { buildGlmNarutoCandidateScoreboard } from './glm-naruto-scoreboard.js';
21
21
  import { runGlmNarutoApplyTransaction } from './glm-naruto-apply-transaction.js';
22
22
  import { finalizeGlmNarutoTerminal } from './glm-naruto-terminal.js';
23
+ import { writeGlmNarutoFinalSeal } from './glm-naruto-final-seal.js';
23
24
  import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
24
25
  export async function runGlmNarutoMission(input) {
25
26
  const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
@@ -94,7 +95,8 @@ export async function runGlmNarutoMission(input) {
94
95
  strategies: strategyMap,
95
96
  isolationMode: isolationPolicy.selected,
96
97
  cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
97
- baseCommit
98
+ baseCommit,
99
+ health: healthTracker
98
100
  });
99
101
  for (const trace of poolResult.traces) {
100
102
  traceState = recordWorkerTrace(traceState, trace);
@@ -112,6 +114,7 @@ export async function runGlmNarutoMission(input) {
112
114
  let envelopes = poolResult.envelopes;
113
115
  let failedShardIds = poolResult.failedShardIds;
114
116
  let repairWaves = 0;
117
+ let schedulerSummary = poolResult.schedulerSummary;
115
118
  // Repair wave if needed
116
119
  if (failedShardIds.length > 0 && repairWaves < GLM_NARUTO_LIMITS.max_repair_waves) {
117
120
  const repairPlan = planRepairWave({
@@ -132,9 +135,15 @@ export async function runGlmNarutoMission(input) {
132
135
  strategies: new Map(repairPlan.shardsToRepair.map((s) => [s.id, [s.strategy]])),
133
136
  isolationMode: isolationPolicy.selected,
134
137
  cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
135
- baseCommit
138
+ baseCommit,
139
+ health: healthTracker
136
140
  });
137
141
  envelopes = [...envelopes, ...repairPool.envelopes];
142
+ schedulerSummary = {
143
+ max_observed_active_workers: Math.max(schedulerSummary.max_observed_active_workers, repairPool.schedulerSummary.max_observed_active_workers),
144
+ backpressure_events: schedulerSummary.backpressure_events + repairPool.schedulerSummary.backpressure_events,
145
+ queue_drained: schedulerSummary.queue_drained && repairPool.schedulerSummary.queue_drained
146
+ };
138
147
  for (const trace of repairPool.traces) {
139
148
  traceState = recordWorkerTrace(traceState, trace);
140
149
  }
@@ -229,7 +238,10 @@ export async function runGlmNarutoMission(input) {
229
238
  missionId,
230
239
  envelopes,
231
240
  selectedPatchIds: mergePlan.selected_patches,
232
- artifactDir
241
+ artifactDir,
242
+ ...(input.allowDirtyApply !== undefined ? { allowDirtyApply: input.allowDirtyApply } : {}),
243
+ ...(input.noRollback !== undefined ? { noRollback: input.noRollback } : {}),
244
+ ...(input.strictChecks !== undefined ? { strictChecks: input.strictChecks } : {})
233
245
  });
234
246
  applyTransaction = transactionResult.transaction;
235
247
  appliedPatches = transactionResult.ok ? transactionResult.applied.length : 0;
@@ -300,13 +312,33 @@ export async function runGlmNarutoMission(input) {
300
312
  findings: [`audit_failed:${err instanceof Error ? err.message : String(err)}`]
301
313
  }));
302
314
  await writeJsonAtomic(path.join(writtenArtifactDir, 'secret-audit.json'), secretAudit).catch(() => undefined);
315
+ const predictedStopGatePath = path.join(cwd, '.sneakoscope', 'missions', missionId, 'stop-gate.json');
316
+ const finalSeal = await writeGlmNarutoFinalSeal({
317
+ artifactDir: writtenArtifactDir,
318
+ missionId,
319
+ result,
320
+ envelopes,
321
+ traces: traceState.workerTraces,
322
+ isolationPolicy,
323
+ scheduler: schedulerSummary,
324
+ selectedPatchIds: mergePlan.selected_patches,
325
+ applyTransaction,
326
+ secretAudit,
327
+ stopGatePath: predictedStopGatePath,
328
+ stopGatePassed: result.ok && secretAudit.ok
329
+ }).catch((err) => ({
330
+ seal: null,
331
+ path: path.join(writtenArtifactDir, 'final-seal.json'),
332
+ passed: false,
333
+ error: err instanceof Error ? err.message : String(err)
334
+ }));
303
335
  // 4.0.9: Write canonical stop-gate artifacts for hook resolution.
304
336
  await writeFinalStopGate({
305
337
  root: cwd,
306
338
  missionId,
307
339
  route: 'GLM_NARUTO',
308
340
  routeCommand: '$Naruto',
309
- status: result.ok && secretAudit.ok ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok ? 'blocked' : 'failed'),
341
+ status: result.ok && secretAudit.ok && finalSeal.passed ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok || !finalSeal.passed ? 'blocked' : 'failed'),
310
342
  terminal: terminalState === 'completed' || terminalState === 'blocked',
311
343
  terminalState,
312
344
  evidence: {
@@ -316,12 +348,16 @@ export async function runGlmNarutoMission(input) {
316
348
  per_worker_artifacts: true,
317
349
  verifier_wave_run: verifierWaveRun,
318
350
  model_guard_enforced: true,
351
+ final_seal_passed: finalSeal.passed,
352
+ final_seal_path: finalSeal.path,
319
353
  proof_required: false,
320
354
  proof_passed: true,
321
355
  reflection_required: false,
322
356
  reflection_passed: 'not_required',
323
357
  },
324
- blockers: secretAudit.ok ? (result.blockers || []) : ['glm_naruto_secret_leak_detected'],
358
+ blockers: secretAudit.ok
359
+ ? (finalSeal.passed ? (result.blockers || []) : [...(result.blockers || []), 'glm_naruto_final_seal_not_passed'])
360
+ : ['glm_naruto_secret_leak_detected'],
325
361
  nativeGateFile: 'termination.json',
326
362
  }).catch(() => null);
327
363
  return { ...result, artifact_dir: writtenArtifactDir };
@@ -0,0 +1,76 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fsp from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { exists, writeJsonAtomic } from '../../../fsx.js';
5
+ export async function runGlmNarutoTargetedChecks(input) {
6
+ const checks = [];
7
+ const touchedPaths = [...new Set(input.touchedPaths)].sort();
8
+ const diffCheck = await runProcess(input.cwd, ['git', 'diff', '--check']);
9
+ checks.push({
10
+ id: 'git_diff_check',
11
+ ok: diffCheck.code === 0,
12
+ command: ['git', 'diff', '--check'],
13
+ ...(diffCheck.code === 0 ? {} : { message: diffCheck.stderr || diffCheck.stdout || 'git diff --check failed' })
14
+ });
15
+ for (const rel of touchedPaths.filter((file) => /\.(?:json)$/i.test(file))) {
16
+ const absolute = path.join(input.cwd, rel);
17
+ try {
18
+ JSON.parse(await fsp.readFile(absolute, 'utf8'));
19
+ checks.push({ id: 'json_parse', ok: true, path: rel });
20
+ }
21
+ catch (err) {
22
+ checks.push({ id: 'json_parse', ok: false, path: rel, message: err instanceof Error ? err.message : String(err) });
23
+ }
24
+ }
25
+ for (const rel of touchedPaths.filter((file) => /\.(?:js|mjs|cjs)$/i.test(file))) {
26
+ const nodeCheck = await runProcess(input.cwd, ['node', '--check', rel]);
27
+ checks.push({
28
+ id: 'node_check',
29
+ ok: nodeCheck.code === 0,
30
+ path: rel,
31
+ command: ['node', '--check', rel],
32
+ ...(nodeCheck.code === 0 ? {} : { message: nodeCheck.stderr || nodeCheck.stdout || 'node --check failed' })
33
+ });
34
+ }
35
+ const tsTouched = touchedPaths.filter((file) => /\.(?:ts|tsx)$/i.test(file));
36
+ const tscThreshold = input.tscFileThreshold ?? 3;
37
+ if (tsTouched.length > 0 && (input.strictChecks || tsTouched.length <= tscThreshold)) {
38
+ const tscBin = path.join(input.cwd, 'node_modules', '.bin', 'tsc');
39
+ const tsconfig = path.join(input.cwd, 'tsconfig.json');
40
+ if (await exists(tscBin) && await exists(tsconfig)) {
41
+ const tsc = await runProcess(input.cwd, [tscBin, '-p', 'tsconfig.json', '--noEmit']);
42
+ checks.push({
43
+ id: 'tsc_no_emit',
44
+ ok: tsc.code === 0,
45
+ command: [tscBin, '-p', 'tsconfig.json', '--noEmit'],
46
+ ...(tsc.code === 0 ? {} : { message: tsc.stderr || tsc.stdout || 'tsc --noEmit failed' })
47
+ });
48
+ }
49
+ else {
50
+ checks.push({ id: 'tsc_no_emit', ok: true, skipped: true, message: 'tsc_or_tsconfig_unavailable' });
51
+ }
52
+ }
53
+ const blockers = checks.filter((check) => !check.ok).map((check) => `${check.id}${check.path ? `:${check.path}` : ''}`);
54
+ const result = {
55
+ schema: 'sks.glm-naruto-targeted-checks.v1',
56
+ ok: blockers.length === 0,
57
+ touched_paths: touchedPaths,
58
+ checks,
59
+ blockers
60
+ };
61
+ if (input.artifactDir)
62
+ await writeJsonAtomic(path.join(input.artifactDir, 'targeted-checks.json'), result);
63
+ return result;
64
+ }
65
+ function runProcess(cwd, command) {
66
+ return new Promise((resolve) => {
67
+ const child = spawn(command[0], command.slice(1), { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
68
+ let stdout = '';
69
+ let stderr = '';
70
+ child.stdout.on('data', (chunk) => { stdout += String(chunk); });
71
+ child.stderr.on('data', (chunk) => { stderr += String(chunk); });
72
+ child.on('close', (code) => resolve({ code, stdout, stderr }));
73
+ child.on('error', (err) => resolve({ code: 1, stdout, stderr: err.message }));
74
+ });
75
+ }
76
+ //# sourceMappingURL=glm-naruto-targeted-checks.js.map
@@ -40,6 +40,14 @@ export async function writeMissionArtifacts(input) {
40
40
  await writeJsonAtomic(path.join(dir, 'apply-result.json'), sanitizeArtifact(input.applyResult));
41
41
  if (input.applyTransaction)
42
42
  await writeJsonAtomic(path.join(dir, 'apply-transaction.json'), sanitizeArtifact(input.applyTransaction));
43
+ if (input.mergePlan) {
44
+ await writeTextAtomic(path.join(dir, 'merge-rationale.md'), renderMergeRationale({
45
+ mergePlan: input.mergePlan,
46
+ conflictGraph: input.conflictGraph,
47
+ candidateScoreboard: input.candidateScoreboard,
48
+ applyTransaction: input.applyTransaction
49
+ }));
50
+ }
43
51
  if (input.verificationSummary)
44
52
  await writeJsonAtomic(path.join(dir, 'verification-summary.json'), sanitizeArtifact(input.verificationSummary));
45
53
  if (input.missionResult)
@@ -85,6 +93,48 @@ export async function writeMissionArtifacts(input) {
85
93
  }
86
94
  return dir;
87
95
  }
96
+ function renderMergeRationale(input) {
97
+ const plan = input.mergePlan;
98
+ const graph = input.conflictGraph;
99
+ const scoreboard = input.candidateScoreboard;
100
+ const tx = input.applyTransaction;
101
+ const selected = Array.isArray(plan?.selected_patches) ? plan.selected_patches : [];
102
+ const scores = Array.isArray(scoreboard?.scores) ? scoreboard.scores : [];
103
+ const edges = Array.isArray(graph?.edges) ? graph.edges : [];
104
+ const rejected = scores
105
+ .filter((score) => !selected.includes(score.patch_id))
106
+ .map((score) => `- ${score.patch_id}: score=${score.total_score}; disqualified=${Boolean(score.disqualified)}; reasons=${(score.disqualification_reasons || []).join(', ') || 'none'}`);
107
+ const selectedRows = selected.map((patchId) => {
108
+ const score = scores.find((row) => row.patch_id === patchId);
109
+ return `- ${patchId}: score=${score?.total_score ?? 'n/a'}; components=${JSON.stringify(score?.components ?? {})}`;
110
+ });
111
+ const conflictRows = edges.map((edge) => `- ${edge.left_patch_id} vs ${edge.right_patch_id}: ${edge.reason}`);
112
+ return [
113
+ '# GLM Naruto Merge Rationale',
114
+ '',
115
+ `Mission: ${plan?.mission_id ?? 'unknown'}`,
116
+ `Strategy: ${plan?.strategy ?? 'unknown'}`,
117
+ `Rationale: ${plan?.rationale ?? 'not_recorded'}`,
118
+ '',
119
+ '## Selected Patch IDs',
120
+ selectedRows.length ? selectedRows.join('\n') : '- none',
121
+ '',
122
+ '## Rejected Patch IDs',
123
+ rejected.length ? rejected.join('\n') : '- none',
124
+ '',
125
+ '## Conflicts',
126
+ conflictRows.length ? conflictRows.join('\n') : '- none',
127
+ '',
128
+ '## Apply Transaction',
129
+ `- final_status: ${tx?.final_status ?? 'not_attempted'}`,
130
+ `- apply_passed: ${tx?.apply_passed ?? false}`,
131
+ `- targeted_checks_passed: ${tx?.targeted_checks_passed ?? null}`,
132
+ `- rollback_attempted: ${tx?.rollback_attempted ?? false}`,
133
+ `- rollback_passed: ${tx?.rollback_passed ?? null}`,
134
+ `- blockers: ${(tx?.blockers || []).join(', ') || 'none'}`,
135
+ ''
136
+ ].join('\n');
137
+ }
88
138
  function sanitizeArtifact(value) {
89
139
  return JSON.parse(JSON.stringify(value, (key, raw) => {
90
140
  if (isSecretLikeKey(key) && typeof raw === 'string' && raw.trim() && !isAllowedRedaction(raw))