sneakoscope 4.0.13 → 4.0.14

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 (101) hide show
  1. package/README.md +10 -2
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/cli/global-mode-router.js +2 -1
  7. package/dist/core/commands/mad-sks-command.js +3 -0
  8. package/dist/core/fsx.js +1 -1
  9. package/dist/core/providers/glm/bench/glm-bench-model-lock-proof.js +32 -3
  10. package/dist/core/providers/glm/bench/glm-benchmark-runner.js +29 -5
  11. package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
  12. package/dist/core/providers/glm/naruto/glm-naruto-critical-path.js +51 -0
  13. package/dist/core/providers/glm/naruto/glm-naruto-final-seal.js +9 -2
  14. package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +101 -15
  15. package/dist/core/providers/glm/naruto/glm-naruto-parallelism-summary.js +55 -0
  16. package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +92 -0
  17. package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +42 -0
  18. package/dist/core/providers/glm/naruto/glm-naruto-stage-scheduler.js +85 -0
  19. package/dist/core/providers/glm/naruto/glm-naruto-task-size-classifier.js +12 -0
  20. package/dist/core/providers/glm/naruto/glm-naruto-trace.js +4 -0
  21. package/dist/core/providers/glm/naruto/glm-naruto-verifier-output.js +5 -0
  22. package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +130 -44
  23. package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +6 -2
  24. package/dist/core/routes/model-mode-router.js +44 -0
  25. package/dist/core/version.js +1 -1
  26. package/package.json +24 -1
  27. package/dist/scripts/agent-dynamic-pool-fixture.js +0 -80
  28. package/dist/scripts/agent-native-release-gate.js +0 -274
  29. package/dist/scripts/agent-patch-swarm-gate-lib.js +0 -113
  30. package/dist/scripts/agent-real-codex-patch-envelope-smoke.js +0 -126
  31. package/dist/scripts/agent-route-blackbox-lib.js +0 -132
  32. package/dist/scripts/blackbox-command-import-smoke.js +0 -143
  33. package/dist/scripts/blackbox-global-shim.js +0 -77
  34. package/dist/scripts/blackbox-matrix.js +0 -70
  35. package/dist/scripts/blackbox-npx-one-shot.js +0 -69
  36. package/dist/scripts/blackbox-pack-install.js +0 -174
  37. package/dist/scripts/build-dist.js +0 -64
  38. package/dist/scripts/check-architecture.js +0 -135
  39. package/dist/scripts/check-cli-entrypoint.js +0 -43
  40. package/dist/scripts/check-command-module-budget.js +0 -25
  41. package/dist/scripts/check-dist-runtime.js +0 -100
  42. package/dist/scripts/check-feature-quality.js +0 -53
  43. package/dist/scripts/check-legacy-free.js +0 -66
  44. package/dist/scripts/check-package-boundary.js +0 -108
  45. package/dist/scripts/check-pipeline-budget.js +0 -69
  46. package/dist/scripts/check-pipeline-runtime.js +0 -25
  47. package/dist/scripts/check-publish-tag.js +0 -30
  48. package/dist/scripts/check-route-modularity.js +0 -82
  49. package/dist/scripts/check-runtime-schemas.js +0 -87
  50. package/dist/scripts/check-source-runtime.js +0 -4
  51. package/dist/scripts/check-ts-contracts.js +0 -69
  52. package/dist/scripts/check-ts-suppressions.js +0 -58
  53. package/dist/scripts/clean-dist.js +0 -8
  54. package/dist/scripts/codex-0140-feature-gate-lib.js +0 -14
  55. package/dist/scripts/codex-config-eperm-fixture.js +0 -32
  56. package/dist/scripts/codex-lb-missing-env-regression.js +0 -40
  57. package/dist/scripts/codex-native-runtime-e2e-fixture.js +0 -75
  58. package/dist/scripts/codex-project-config-policy-merge-regression.js +0 -92
  59. package/dist/scripts/core-skill-legacy-promotion-api-audit.js +0 -54
  60. package/dist/scripts/ensure-bin-executable.js +0 -10
  61. package/dist/scripts/fixtures/fake-codex-config-loader.js +0 -51
  62. package/dist/scripts/github-release-body-helper.js +0 -65
  63. package/dist/scripts/gpt-image-2-real-file-smoke.js +0 -448
  64. package/dist/scripts/hooks-no-unsupported-handlers.js +0 -15
  65. package/dist/scripts/hooks-runtime-replay-warning-zero-v2.js +0 -26
  66. package/dist/scripts/hooks-runtime-replay-warning-zero.js +0 -10
  67. package/dist/scripts/hooks-trust-warning-zero.js +0 -14
  68. package/dist/scripts/lib/codex-sdk-gate-lib.js +0 -92
  69. package/dist/scripts/lib/ensure-dist-fresh.js +0 -142
  70. package/dist/scripts/lib/git-worktree-fixture.js +0 -33
  71. package/dist/scripts/lib/mad-sks-actual-executor-check-lib.js +0 -255
  72. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +0 -79
  73. package/dist/scripts/lib/real-codex-parallel-gate.js +0 -94
  74. package/dist/scripts/lib/real-codex-parallel-proof-fixture.js +0 -55
  75. package/dist/scripts/lib/valid-png-fixture.js +0 -25
  76. package/dist/scripts/mad-sks-live-protected-core-smoke.js +0 -5
  77. package/dist/scripts/naruto-real-local-gpt-final-smoke.js +0 -25
  78. package/dist/scripts/perf-gate.js +0 -39
  79. package/dist/scripts/prepublish-release-check-or-fast.js +0 -121
  80. package/dist/scripts/release-3112-required-gates.js +0 -30
  81. package/dist/scripts/release-3113-required-gates.js +0 -25
  82. package/dist/scripts/release-4000-required-gates.js +0 -36
  83. package/dist/scripts/release-4001-required-gates.js +0 -13
  84. package/dist/scripts/release-4002-required-gates.js +0 -14
  85. package/dist/scripts/release-check-dynamic-execute.js +0 -259
  86. package/dist/scripts/release-check-dynamic.js +0 -107
  87. package/dist/scripts/release-check-stamp.js +0 -261
  88. package/dist/scripts/release-gate-dag-runner.js +0 -56
  89. package/dist/scripts/release-gate-existence-audit.js +0 -111
  90. package/dist/scripts/release-gate-planner.js +0 -34
  91. package/dist/scripts/release-gate-worker.js +0 -10
  92. package/dist/scripts/release-speed-summary.js +0 -67
  93. package/dist/scripts/repo-audit.js +0 -83
  94. package/dist/scripts/rust-smoke.js +0 -5
  95. package/dist/scripts/sizecheck.js +0 -146
  96. package/dist/scripts/sks-1-11-gate-lib.js +0 -78
  97. package/dist/scripts/sks-1-18-gate-lib.js +0 -55
  98. package/dist/scripts/tmux-removal-inventory.js +0 -36
  99. package/dist/scripts/write-build-manifest.js +0 -71
  100. package/dist/scripts/zellij-dashboard-watch.js +0 -41
  101. package/dist/scripts/zellij-right-column-geometry-proof.js +0 -162
package/README.md CHANGED
@@ -35,9 +35,17 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **4.0.12** seals GLM Naruto's production runtime path: worktree workers apply extracted unified diffs only, patch workers launch through a bounded adaptive scheduler, live bench compares true direct GLM against Naruto worker counts, final apply runs dirty-tree and targeted-check guards, and stop-gates reference a final seal artifact.
38
+ SKS **4.0.14** seals GLM Naruto real parallelism while preserving the existing GPT/Codex/MAD `sks --mad` route. GLM mode stays locked to OpenRouter `z-ai/glm-5.2`; non-GLM MAD does not require OpenRouter, does not select GLM, and does not enter the GLM Naruto scheduler.
39
39
 
40
- What changed in 4.0.12:
40
+ What changed in 4.0.14:
41
+
42
+ - **Real stage parallelism evidence.** GLM Naruto records bounded parallel stage timelines, overlap ratios, parallelism summaries, critical-path metrics, and speed diagnosis artifacts.
43
+ - **Parallel gate/verifier/worktree stages.** Candidate gate, worktree materialization, and verifier checks no longer have to run candidate-by-candidate when multiple candidates are available.
44
+ - **Requirement coverage seal.** GLM Naruto writes a requirement ledger and candidate coverage artifacts, and the final seal blocks when required requirements remain uncovered.
45
+ - **MAD route isolation.** `sks --mad` without `--glm` remains the GPT/Codex/MAD route and does not resolve OpenRouter or run GLM-specific benchmark/Naruto code.
46
+ - **Benchmark proof honesty.** GLM benchmark proof now reports request-summary availability separately from case-level model lock checks and fixes the no-mutation proof boolean.
47
+
48
+ What changed in 4.0.13:
41
49
 
42
50
  - **Extracted worktree patches.** `--worktree` parses `<sks_patch_candidate>` and records candidate/extracted patch hashes before any worker worktree apply.
43
51
  - **Adaptive scheduler.** Patch workers use a finite launch queue with provider-health backpressure and retry-once handling for retryable 429/5xx/idle-timeout failures.
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "4.0.12"
79
+ version = "4.0.14"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "4.0.12"
3
+ version = "4.0.14"
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 4.0.12"),
7
+ Some("--version") => println!("sks-rs 4.0.14"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '4.0.13';
2
+ const FAST_PACKAGE_VERSION = '4.0.14';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -6,8 +6,9 @@ export function detectGlobalMode(args = []) {
6
6
  const hasGlm = args.includes('--glm');
7
7
  if (hasMad && hasGlm)
8
8
  return { kind: 'mad-glm', args: stripGlobalModeFlags(args) };
9
- if (hasGlm && !hasMad)
9
+ if (hasGlm && !hasMad && String(args[0]).startsWith('-')) {
10
10
  return { kind: 'glm-without-mad', args: stripGlobalModeFlags(args) };
11
+ }
11
12
  return null;
12
13
  }
13
14
  export function stripGlobalModeFlags(args) {
@@ -26,12 +26,15 @@ import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-i
26
26
  import { repairZellijForSks } from '../zellij/zellij-self-heal.js';
27
27
  import { buildMadGlmLaunchArtifact, buildMadGlmLaunchProfileNoWrite, resolveMadGlmLaunchKey, writeMadGlmCodexWrapper } from '../providers/glm/glm-mad-launch.js';
28
28
  import { GLM_MAD_MODE } from '../providers/glm/glm-52-settings.js';
29
+ import { assertNonGlmMadRoute } from '../routes/model-mode-router.js';
29
30
  export async function madHighCommand(args = [], deps = {}) {
30
31
  const subcommand = firstSubcommand(args);
31
32
  if (subcommand)
32
33
  return madSksSubcommand(subcommand, args.filter((arg) => String(arg) !== subcommand));
33
34
  const rawArgs = (args || []).map((arg) => String(arg));
34
35
  const glmMadLaunch = isMadGlmLaunch(rawArgs, deps);
36
+ if (!glmMadLaunch)
37
+ assertNonGlmMadRoute(rawArgs.includes('--mad') ? rawArgs : ['--mad', ...rawArgs]);
35
38
  const glmOnlyFlagBlockers = findGlmOnlyMadFlagBlockers(rawArgs, glmMadLaunch);
36
39
  if (glmOnlyFlagBlockers.length) {
37
40
  const result = {
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.0.13';
8
+ export const PACKAGE_VERSION = '4.0.14';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -1,7 +1,10 @@
1
1
  import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
2
- export function buildGlmBenchModelLockProof(cases) {
2
+ export function buildGlmBenchModelLockProof(cases, proofInput = {}) {
3
3
  const checkedCases = cases.map((c) => c.runner_id);
4
4
  const mismatches = [];
5
+ const requestSummaries = proofInput.requestSummaries ?? [];
6
+ let fallbackArraysFound = 0;
7
+ let openaiKeyUsed = false;
5
8
  for (const caseResult of cases) {
6
9
  if (caseResult.model !== GLM_52_OPENROUTER_MODEL) {
7
10
  mismatches.push(`${caseResult.runner_id}: model is ${caseResult.model}, expected ${GLM_52_OPENROUTER_MODEL}`);
@@ -10,13 +13,39 @@ export function buildGlmBenchModelLockProof(cases) {
10
13
  mismatches.push(`${caseResult.runner_id}: gpt_fallback_allowed is not false`);
11
14
  }
12
15
  }
16
+ for (const summary of requestSummaries) {
17
+ if (summary.model !== undefined && summary.model !== GLM_52_OPENROUTER_MODEL) {
18
+ mismatches.push(`request-summary:${String(summary.worker_id ?? summary.runner_id ?? 'unknown')}: model is ${String(summary.model)}`);
19
+ }
20
+ const models = Array.isArray(summary.models) ? summary.models : [];
21
+ const fallbackModelsCount = typeof summary.fallback_models_count === 'number' ? summary.fallback_models_count : models.length;
22
+ if (fallbackModelsCount > 0)
23
+ fallbackArraysFound += 1;
24
+ if (summary.openai_key_used === true || summary.authorization_source === 'openai')
25
+ openaiKeyUsed = true;
26
+ if (summary.gpt_fallback_allowed !== undefined && summary.gpt_fallback_allowed !== false) {
27
+ mismatches.push(`request-summary:${String(summary.worker_id ?? summary.runner_id ?? 'unknown')}: gpt_fallback_allowed is not false`);
28
+ }
29
+ }
30
+ if (fallbackArraysFound > 0)
31
+ mismatches.push(`fallback_arrays_found:${fallbackArraysFound}`);
32
+ if (openaiKeyUsed)
33
+ mismatches.push('openai_key_used');
34
+ const requestSummaryStatus = requestSummaries.length > 0 ? 'checked' : 'unavailable';
13
35
  return {
14
36
  schema: 'sks.glm-bench-model-lock-proof.v1',
15
37
  checked_cases: checkedCases,
16
38
  model: GLM_52_OPENROUTER_MODEL,
17
39
  gpt_fallback_allowed: false,
18
- fallback_arrays_found: 0,
19
- openai_key_used: false,
40
+ request_summary_status: requestSummaryStatus,
41
+ request_summaries_checked: requestSummaries.length,
42
+ request_summaries_unavailable: Math.max(0, cases.length - requestSummaries.length),
43
+ naruto_request_summaries_checked: requestSummaries.filter((summary) => String(summary.worker_id ?? '').startsWith('worker-')).length,
44
+ direct_trace_checked: proofInput.directTraceChecked === true,
45
+ fallback_arrays_found: fallbackArraysFound,
46
+ openai_key_used: openaiKeyUsed,
47
+ fallback_array_scan: requestSummaryStatus,
48
+ openai_key_usage_scan: requestSummaryStatus,
20
49
  mismatches,
21
50
  passed: mismatches.length === 0
22
51
  };
@@ -115,19 +115,22 @@ export async function runGlmBenchmark(root, args = [], deps = {}) {
115
115
  }
116
116
  await cleanupFixture(sharedFixture);
117
117
  const comparison = computeGlmBenchmarkComparison(cases);
118
- const modelLockProof = buildGlmBenchModelLockProof(cases);
118
+ const modelLockProof = buildGlmBenchModelLockProof(cases, {
119
+ requestSummaries: await collectRequestSummaries(cases),
120
+ directTraceChecked: cases.some((c) => c.runner_id === 'direct-glm-speed' && c.artifacts.trace_path !== null)
121
+ });
119
122
  const userCwdAfter = await captureGitStatus(userCwd);
120
123
  const userCwdUnchanged = userCwdBefore === userCwdAfter;
121
124
  const noMutationProof = {
122
125
  schema: 'sks.glm-bench-no-mutation-proof.v1',
123
- user_cwd_unchanged: userCwdUnchanged ? true : true,
126
+ user_cwd_unchanged: userCwdUnchanged,
124
127
  fixture_mutated_only_under_apply_temp: !applyTemp,
125
128
  cases_report_no_mutation: true,
126
129
  passed: userCwdUnchanged && cases.every((c) => c.mutation_performed === false)
127
130
  };
128
131
  const result = {
129
132
  schema: 'sks.glm-benchmark-result.v1',
130
- version: '4.0.13',
133
+ version: '4.0.14',
131
134
  generated_at: nowIso(),
132
135
  status: 'live',
133
136
  model: GLM_52_OPENROUTER_MODEL,
@@ -154,7 +157,7 @@ export async function runGlmBenchmark(root, args = [], deps = {}) {
154
157
  function dryRunResult(root, startedMs) {
155
158
  return {
156
159
  schema: 'sks.glm-benchmark-result.v1',
157
- version: '4.0.13',
160
+ version: '4.0.14',
158
161
  generated_at: nowIso(),
159
162
  status: 'dry_run',
160
163
  model: GLM_52_OPENROUTER_MODEL,
@@ -177,7 +180,7 @@ function dryRunResult(root, startedMs) {
177
180
  function blockedResult(root, warnings) {
178
181
  return {
179
182
  schema: 'sks.glm-benchmark-result.v1',
180
- version: '4.0.13',
183
+ version: '4.0.14',
181
184
  generated_at: nowIso(),
182
185
  status: 'blocked',
183
186
  model: GLM_52_OPENROUTER_MODEL,
@@ -207,6 +210,27 @@ async function readWorkerTraces(artifactDir) {
207
210
  return [];
208
211
  }
209
212
  }
213
+ async function collectRequestSummaries(cases) {
214
+ const summaries = [];
215
+ for (const caseResult of cases) {
216
+ const dir = caseResult.artifacts.mission_artifact_dir;
217
+ if (!dir)
218
+ continue;
219
+ try {
220
+ const workerRoot = path.join(dir, 'workers');
221
+ const workerIds = await fsp.readdir(workerRoot);
222
+ for (const workerId of workerIds) {
223
+ try {
224
+ const summary = JSON.parse(await fsp.readFile(path.join(workerRoot, workerId, 'request-summary.json'), 'utf8'));
225
+ summaries.push(summary);
226
+ }
227
+ catch { }
228
+ }
229
+ }
230
+ catch { }
231
+ }
232
+ return summaries;
233
+ }
210
234
  async function captureGitStatus(cwd) {
211
235
  return new Promise((resolve) => {
212
236
  const child = spawn('git', ['status', '--short'], { cwd, stdio: ['ignore', 'pipe', 'ignore'] });
@@ -1,2 +1,2 @@
1
- export const GLM_BENCHMARK_VERSION = '4.0.13';
1
+ export const GLM_BENCHMARK_VERSION = '4.0.14';
2
2
  //# sourceMappingURL=glm-benchmark-types.js.map
@@ -0,0 +1,51 @@
1
+ import path from 'node:path';
2
+ import { writeJsonAtomic, writeTextAtomic } from '../../../fsx.js';
3
+ export function buildGlmNarutoCriticalPathMetrics(input) {
4
+ const stageMs = (stage) => {
5
+ const matches = input.stages.filter((metric) => metric.stage === stage);
6
+ if (!matches.length)
7
+ return null;
8
+ return matches.reduce((sum, metric) => sum + metric.wall_clock_ms, 0);
9
+ };
10
+ const values = {
11
+ decomposition: input.decompositionMs,
12
+ patch_generation: stageMs('patch_generation') ?? 0,
13
+ worktree_materialization: stageMs('worktree_materialization'),
14
+ candidate_gate: stageMs('candidate_gate') ?? 0,
15
+ verifier: stageMs('verifier'),
16
+ conflict_merge: input.conflictMergeMs,
17
+ final_apply: input.finalApplyMs,
18
+ final_seal: input.finalSealMs
19
+ };
20
+ const slowest = Object.entries(values)
21
+ .filter(([, value]) => typeof value === 'number')
22
+ .sort((a, b) => Number(b[1]) - Number(a[1]))[0]?.[0] ?? 'unknown';
23
+ return {
24
+ schema: 'sks.glm-naruto-critical-path.v1',
25
+ total_wall_clock_ms: Math.max(0, input.totalWallClockMs),
26
+ stage_wall_clock_ms: values,
27
+ slowest_stage: slowest,
28
+ parallelism_warnings: input.parallelismWarnings
29
+ };
30
+ }
31
+ export async function writeGlmNarutoCriticalPathArtifacts(input) {
32
+ const dir = path.join(input.root, '.sneakoscope', 'glm-naruto', input.missionId);
33
+ await writeJsonAtomic(path.join(dir, 'critical-path.json'), input.metrics);
34
+ await writeTextAtomic(path.join(dir, 'speed-diagnosis.md'), renderSpeedDiagnosis(input.metrics));
35
+ }
36
+ function renderSpeedDiagnosis(metrics) {
37
+ return [
38
+ '# GLM Naruto Speed Diagnosis',
39
+ '',
40
+ `Total wall clock: ${metrics.total_wall_clock_ms}ms`,
41
+ `Slowest stage: ${metrics.slowest_stage}`,
42
+ '',
43
+ '## Stage Wall Clock',
44
+ ...Object.entries(metrics.stage_wall_clock_ms).map(([stage, ms]) => `- ${stage}: ${ms === null ? 'not_run' : `${ms}ms`}`),
45
+ '',
46
+ '## Parallelism Warnings',
47
+ metrics.parallelism_warnings.length ? metrics.parallelism_warnings.map((warning) => `- ${warning}`).join('\n') : '- none',
48
+ ''
49
+ ].join('\n');
50
+ }
51
+ //# sourceMappingURL=glm-naruto-critical-path.js.map
@@ -12,7 +12,8 @@ export async function writeGlmNarutoFinalSeal(input) {
12
12
  secretOk: input.secretAudit.ok,
13
13
  mismatches,
14
14
  isolationViolations,
15
- queueDrained: input.scheduler.queue_drained
15
+ queueDrained: input.scheduler.queue_drained,
16
+ requirementCoveragePassed: input.requirementCoverage?.passed ?? true
16
17
  });
17
18
  const seal = {
18
19
  schema: 'sks.glm-naruto-final-seal.v1',
@@ -41,6 +42,12 @@ export async function writeGlmNarutoFinalSeal(input) {
41
42
  verifier_passed: input.envelopes.filter((env) => env.verification_passed === true).length,
42
43
  selected: input.selectedPatchIds
43
44
  },
45
+ requirement_coverage: {
46
+ required_total: input.requirementCoverage?.required_total ?? 0,
47
+ required_covered: input.requirementCoverage?.required_covered ?? 0,
48
+ passed: input.requirementCoverage?.passed ?? true,
49
+ uncovered_required_requirements: input.requirementCoverage?.uncovered_required_requirements ?? []
50
+ },
44
51
  apply: {
45
52
  attempted: input.applyTransaction !== null,
46
53
  transaction_path: input.applyTransaction ? path.join(input.artifactDir, 'apply-transaction.json') : null,
@@ -62,7 +69,7 @@ export async function writeGlmNarutoFinalSeal(input) {
62
69
  return { seal, path: out, passed: seal.status === 'passed' };
63
70
  }
64
71
  function finalSealStatus(input) {
65
- if (!input.secretOk || input.mismatches.length > 0 || input.isolationViolations.length > 0 || !input.queueDrained)
72
+ if (!input.secretOk || input.mismatches.length > 0 || input.isolationViolations.length > 0 || !input.queueDrained || !input.requirementCoveragePassed)
66
73
  return 'blocked';
67
74
  if (input.result.ok)
68
75
  return 'passed';
@@ -21,6 +21,11 @@ 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
23
  import { writeGlmNarutoFinalSeal } from './glm-naruto-final-seal.js';
24
+ import { runGlmNarutoStageScheduler } from './glm-naruto-stage-scheduler.js';
25
+ import { buildGlmNarutoParallelismSummary, metricFromStageResult, writeGlmNarutoParallelismArtifacts } from './glm-naruto-parallelism-summary.js';
26
+ import { buildGlmNarutoRequirementCoverageSummary, enrichGlmNarutoCandidateRequirementCoverage, inferCandidateRequirementCoverage } from './glm-naruto-requirement-coverage.js';
27
+ import { buildGlmNarutoRequirementLedger } from './glm-naruto-requirement-ledger.js';
28
+ import { buildGlmNarutoCriticalPathMetrics, writeGlmNarutoCriticalPathArtifacts } from './glm-naruto-critical-path.js';
24
29
  import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
25
30
  export async function runGlmNarutoMission(input) {
26
31
  const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
@@ -34,8 +39,15 @@ export async function runGlmNarutoMission(input) {
34
39
  result: missionResult(missionId, input.task, 'blocked', 'glm_missing_openrouter_key', 0, startedMs, [], [], ['glm_missing_openrouter_key'], [])
35
40
  });
36
41
  }
42
+ const decompositionStartedMs = Date.now();
37
43
  const mentionedPaths = extractMentionedPaths(input.task);
38
44
  const gitStatus = await readGitStatus(cwd);
45
+ const requirementLedger = buildGlmNarutoRequirementLedger({
46
+ missionId,
47
+ task: input.task,
48
+ mentionedPaths,
49
+ ...(gitStatus !== undefined ? { gitStatus } : {})
50
+ });
39
51
  const gitRoot = await getGitRoot(cwd);
40
52
  const baseCommit = gitRoot ? await getGitHead(cwd) : null;
41
53
  const isolationPolicy = resolveGlmNarutoIsolationPolicy({
@@ -76,6 +88,7 @@ export async function runGlmNarutoMission(input) {
76
88
  });
77
89
  }
78
90
  const laneMix = computeInitialLaneMix(graph);
91
+ const decompositionMs = Date.now() - decompositionStartedMs;
79
92
  const strategies = planShardCandidates(graph);
80
93
  const strategyMap = new Map();
81
94
  for (const entry of strategies) {
@@ -115,6 +128,8 @@ export async function runGlmNarutoMission(input) {
115
128
  let failedShardIds = poolResult.failedShardIds;
116
129
  let repairWaves = 0;
117
130
  let schedulerSummary = poolResult.schedulerSummary;
131
+ const stageMetrics = [...poolResult.stageMetrics];
132
+ const stageEvents = [...poolResult.stageEvents];
118
133
  // Repair wave if needed
119
134
  if (failedShardIds.length > 0 && repairWaves < GLM_NARUTO_LIMITS.max_repair_waves) {
120
135
  const repairPlan = planRepairWave({
@@ -136,9 +151,12 @@ export async function runGlmNarutoMission(input) {
136
151
  isolationMode: isolationPolicy.selected,
137
152
  cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
138
153
  baseCommit,
139
- health: healthTracker
154
+ health: healthTracker,
155
+ stageName: 'repair_generation'
140
156
  });
141
157
  envelopes = [...envelopes, ...repairPool.envelopes];
158
+ stageMetrics.push(...repairPool.stageMetrics);
159
+ stageEvents.push(...repairPool.stageEvents);
142
160
  schedulerSummary = {
143
161
  max_observed_active_workers: Math.max(schedulerSummary.max_observed_active_workers, repairPool.schedulerSummary.max_observed_active_workers),
144
162
  backpressure_events: schedulerSummary.backpressure_events + repairPool.schedulerSummary.backpressure_events,
@@ -156,13 +174,22 @@ export async function runGlmNarutoMission(input) {
156
174
  if (passedEnvelopes.length > 0 && !input.skipVerifier) {
157
175
  verifierWaveRun = true;
158
176
  const verifyApiKey = key.key;
159
- const verifyResults = await Promise.allSettled(passedEnvelopes.map((env) => runVerifierWorker({
160
- apiKey: verifyApiKey,
161
- missionId,
162
- workerId: env.worker_id,
163
- envelope: env,
164
- timeoutMs: 120_000,
165
- })));
177
+ const verifierStage = await runGlmNarutoStageScheduler({
178
+ stage: 'verifier',
179
+ jobs: passedEnvelopes.map((env) => ({ id: env.worker_id, stage: 'verifier', input: env })),
180
+ max_active: Math.min(8, passedEnvelopes.length),
181
+ timeout_ms: 120_000,
182
+ runJob: (job) => runVerifierWorker({
183
+ apiKey: verifyApiKey,
184
+ missionId,
185
+ workerId: job.input.worker_id,
186
+ envelope: job.input,
187
+ timeoutMs: 120_000,
188
+ })
189
+ });
190
+ stageMetrics.push(metricFromStageResult(verifierStage));
191
+ stageEvents.push(...verifierStage.events);
192
+ const verifyResults = verifierStage.results;
166
193
  const verifiedEnvelopes = [];
167
194
  for (let vi = 0; vi < passedEnvelopes.length; vi++) {
168
195
  const env = passedEnvelopes[vi];
@@ -192,8 +219,11 @@ export async function runGlmNarutoMission(input) {
192
219
  });
193
220
  passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
194
221
  }
195
- const warnings = input.skipVerifier && passedEnvelopes.length > 0 ? ['verifier_skipped_by_flag'] : [];
222
+ envelopes = envelopes.map((env) => enrichGlmNarutoCandidateRequirementCoverage({ envelope: env, ledger: requirementLedger }));
223
+ passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
224
+ const verifierWarnings = input.skipVerifier && passedEnvelopes.length > 0 ? ['verifier_skipped_by_flag'] : [];
196
225
  // Build conflict graph and merge plan
226
+ const conflictMergeStartedMs = Date.now();
197
227
  const nodes = passedEnvelopes.map((env) => ({
198
228
  patch_id: env.worker_id,
199
229
  shard_id: env.shard_id,
@@ -227,32 +257,59 @@ export async function runGlmNarutoMission(input) {
227
257
  useJudge: input.useJudge || false,
228
258
  xhighFinalizer: input.xhighFinalizer || false
229
259
  });
260
+ const conflictMergeMs = Date.now() - conflictMergeStartedMs;
261
+ const candidateRequirementCoverage = envelopes.map((env) => inferCandidateRequirementCoverage({ envelope: env, ledger: requirementLedger }));
262
+ const preliminarySelectedPatchIds = mergePlan.selected_patches;
263
+ const requirementCoverageSummary = buildGlmNarutoRequirementCoverageSummary({
264
+ missionId,
265
+ ledger: requirementLedger,
266
+ envelopes,
267
+ selectedPatchIds: preliminarySelectedPatchIds
268
+ });
269
+ const selectedPatchIds = requirementCoverageSummary.passed ? preliminarySelectedPatchIds : [];
230
270
  // Apply winning merge plan
231
271
  let appliedPatches = 0;
232
272
  let applyResult = null;
233
273
  let applyTransaction = null;
234
274
  const artifactDir = path.join(cwd, '.sneakoscope', 'glm-naruto', missionId);
235
- if (!input.noApply && mergePlan.selected_patches.length > 0) {
275
+ let finalApplyMs = null;
276
+ if (!input.noApply && selectedPatchIds.length > 0) {
277
+ const finalApplyStartedMs = Date.now();
236
278
  const transactionResult = await runGlmNarutoApplyTransaction({
237
279
  cwd,
238
280
  missionId,
239
281
  envelopes,
240
- selectedPatchIds: mergePlan.selected_patches,
282
+ selectedPatchIds,
241
283
  artifactDir,
242
284
  ...(input.allowDirtyApply !== undefined ? { allowDirtyApply: input.allowDirtyApply } : {}),
243
285
  ...(input.noRollback !== undefined ? { noRollback: input.noRollback } : {}),
244
286
  ...(input.strictChecks !== undefined ? { strictChecks: input.strictChecks } : {})
245
287
  });
288
+ finalApplyMs = Date.now() - finalApplyStartedMs;
246
289
  applyTransaction = transactionResult.transaction;
247
290
  appliedPatches = transactionResult.ok ? transactionResult.applied.length : 0;
248
291
  applyResult = { ok: transactionResult.ok, applied: transactionResult.applied, ...(transactionResult.transaction.blockers[0] ? { blocker: transactionResult.transaction.blockers[0] } : {}) };
249
292
  }
250
- const terminalState = appliedPatches > 0 ? 'completed' : passedEnvelopes.length > 0 ? 'partial_candidates' : 'blocked';
293
+ const coverageBlocked = !requirementCoverageSummary.passed;
294
+ const terminalState = appliedPatches > 0
295
+ ? 'completed'
296
+ : passedEnvelopes.length > 0 ? 'partial_candidates' : 'blocked';
251
297
  const terminationReason = appliedPatches > 0
252
298
  ? 'completed_merge_applied'
253
299
  : applyResult && !applyResult.ok
254
300
  ? 'apply_transaction_failed'
255
- : passedEnvelopes.length > 0 ? 'partial_no_apply' : 'no_gate_passed_candidates';
301
+ : coverageBlocked
302
+ ? 'required_requirement_coverage_missing'
303
+ : passedEnvelopes.length > 0 ? 'partial_no_apply' : 'no_gate_passed_candidates';
304
+ const parallelismSummary = buildGlmNarutoParallelismSummary({
305
+ metrics: stageMetrics,
306
+ totalWallClockMs: Date.now() - startedMs
307
+ });
308
+ const warnings = [
309
+ ...verifierWarnings,
310
+ ...parallelismSummary.blockers,
311
+ ...(coverageBlocked ? ['required_requirement_coverage_missing'] : [])
312
+ ];
256
313
  const summary = buildMissionSummary({
257
314
  missionId,
258
315
  startedMs,
@@ -282,7 +339,9 @@ export async function runGlmNarutoMission(input) {
282
339
  failed_shards: summary.failed_shards,
283
340
  repair_waves: summary.repair_waves,
284
341
  budget_used_ms: summary.budget_used_ms,
285
- blockers: terminalState === 'blocked' ? ['no_gate_passed_candidates'] : (applyResult && !applyResult.ok ? [applyResult.blocker || 'apply_transaction_failed'] : []),
342
+ blockers: coverageBlocked
343
+ ? requirementCoverageSummary.uncovered_required_requirements.map((id) => `required_requirement_uncovered:${id}`)
344
+ : terminalState === 'blocked' ? ['no_gate_passed_candidates'] : (applyResult && !applyResult.ok ? [applyResult.blocker || 'apply_transaction_failed'] : []),
286
345
  warnings
287
346
  };
288
347
  const writtenArtifactDir = await writeMissionArtifacts({
@@ -304,6 +363,15 @@ export async function runGlmNarutoMission(input) {
304
363
  missionResult: result,
305
364
  envelopes
306
365
  });
366
+ await writeJsonAtomic(path.join(writtenArtifactDir, 'requirement-ledger.json'), requirementLedger).catch(() => undefined);
367
+ await writeJsonAtomic(path.join(writtenArtifactDir, 'candidate-requirement-coverage.json'), candidateRequirementCoverage).catch(() => undefined);
368
+ await writeJsonAtomic(path.join(writtenArtifactDir, 'requirement-coverage-summary.json'), requirementCoverageSummary).catch(() => undefined);
369
+ await writeGlmNarutoParallelismArtifacts({
370
+ root: cwd,
371
+ missionId,
372
+ summary: parallelismSummary,
373
+ events: stageEvents
374
+ }).catch(() => undefined);
307
375
  const secretAudit = await auditGlmNarutoArtifactsForSecrets(path.join(cwd, '.sneakoscope', 'glm-naruto', missionId)).catch((err) => ({
308
376
  schema: 'sks.glm-naruto-secret-audit.v1',
309
377
  ok: false,
@@ -313,6 +381,7 @@ export async function runGlmNarutoMission(input) {
313
381
  }));
314
382
  await writeJsonAtomic(path.join(writtenArtifactDir, 'secret-audit.json'), secretAudit).catch(() => undefined);
315
383
  const predictedStopGatePath = path.join(cwd, '.sneakoscope', 'missions', missionId, 'stop-gate.json');
384
+ const finalSealStartedMs = Date.now();
316
385
  const finalSeal = await writeGlmNarutoFinalSeal({
317
386
  artifactDir: writtenArtifactDir,
318
387
  missionId,
@@ -321,7 +390,8 @@ export async function runGlmNarutoMission(input) {
321
390
  traces: traceState.workerTraces,
322
391
  isolationPolicy,
323
392
  scheduler: schedulerSummary,
324
- selectedPatchIds: mergePlan.selected_patches,
393
+ selectedPatchIds,
394
+ requirementCoverage: requirementCoverageSummary,
325
395
  applyTransaction,
326
396
  secretAudit,
327
397
  stopGatePath: predictedStopGatePath,
@@ -332,6 +402,17 @@ export async function runGlmNarutoMission(input) {
332
402
  passed: false,
333
403
  error: err instanceof Error ? err.message : String(err)
334
404
  }));
405
+ const finalSealMs = Date.now() - finalSealStartedMs;
406
+ const criticalPath = buildGlmNarutoCriticalPathMetrics({
407
+ totalWallClockMs: Date.now() - startedMs,
408
+ stages: parallelismSummary.stages,
409
+ decompositionMs,
410
+ conflictMergeMs,
411
+ finalApplyMs,
412
+ finalSealMs,
413
+ parallelismWarnings: parallelismSummary.blockers
414
+ });
415
+ await writeGlmNarutoCriticalPathArtifacts({ root: cwd, missionId, metrics: criticalPath }).catch(() => undefined);
335
416
  // 4.0.9: Write canonical stop-gate artifacts for hook resolution.
336
417
  await writeFinalStopGate({
337
418
  root: cwd,
@@ -350,6 +431,11 @@ export async function runGlmNarutoMission(input) {
350
431
  model_guard_enforced: true,
351
432
  final_seal_passed: finalSeal.passed,
352
433
  final_seal_path: finalSeal.path,
434
+ required_coverage_passed: requirementCoverageSummary.passed,
435
+ uncovered_required_count: requirementCoverageSummary.uncovered_required_requirements.length,
436
+ coverage_ledger_path: path.join(writtenArtifactDir, 'requirement-coverage-summary.json'),
437
+ parallelism_summary_path: path.join(writtenArtifactDir, 'parallelism-summary.json'),
438
+ critical_path_path: path.join(writtenArtifactDir, 'critical-path.json'),
353
439
  proof_required: false,
354
440
  proof_passed: true,
355
441
  reflection_required: false,
@@ -0,0 +1,55 @@
1
+ import path from 'node:path';
2
+ import { writeJsonAtomic, writeTextAtomic } from '../../../fsx.js';
3
+ export function metricFromStageResult(result) {
4
+ return {
5
+ stage: result.stage,
6
+ job_count: result.results.length,
7
+ max_observed_active: result.max_observed_active,
8
+ wall_clock_ms: result.wall_clock_ms,
9
+ sum_job_duration_ms: result.sum_job_duration_ms,
10
+ overlap_ratio: result.overlap_ratio
11
+ };
12
+ }
13
+ export function createStageParallelismMetric(input) {
14
+ return {
15
+ ...input,
16
+ wall_clock_ms: Math.max(0, input.wall_clock_ms),
17
+ sum_job_duration_ms: Math.max(0, input.sum_job_duration_ms),
18
+ overlap_ratio: input.wall_clock_ms > 0 ? input.sum_job_duration_ms / input.wall_clock_ms : input.overlap_ratio
19
+ };
20
+ }
21
+ export function buildGlmNarutoParallelismSummary(input) {
22
+ const grouped = new Map();
23
+ for (const metric of input.metrics) {
24
+ const current = grouped.get(metric.stage);
25
+ if (!current) {
26
+ grouped.set(metric.stage, createStageParallelismMetric(metric));
27
+ continue;
28
+ }
29
+ grouped.set(metric.stage, createStageParallelismMetric({
30
+ stage: metric.stage,
31
+ job_count: current.job_count + metric.job_count,
32
+ max_observed_active: Math.max(current.max_observed_active, metric.max_observed_active),
33
+ wall_clock_ms: current.wall_clock_ms + metric.wall_clock_ms,
34
+ sum_job_duration_ms: current.sum_job_duration_ms + metric.sum_job_duration_ms,
35
+ overlap_ratio: 1
36
+ }));
37
+ }
38
+ const stages = [...grouped.values()];
39
+ const blockers = stages
40
+ .filter((stage) => stage.job_count > 1 && stage.overlap_ratio <= 1.1)
41
+ .map((stage) => `glm_parallelism_not_effective:${stage.stage}`);
42
+ return {
43
+ schema: 'sks.glm-naruto-parallelism-summary.v1',
44
+ stages,
45
+ total_wall_clock_ms: Math.max(0, input.totalWallClockMs),
46
+ parallelism_effective: blockers.length === 0,
47
+ blockers
48
+ };
49
+ }
50
+ export async function writeGlmNarutoParallelismArtifacts(input) {
51
+ const dir = path.join(input.root, '.sneakoscope', 'glm-naruto', input.missionId);
52
+ await writeTextAtomic(path.join(dir, 'stage-timeline.jsonl'), input.events.map((event) => JSON.stringify(event)).join('\n') + (input.events.length ? '\n' : ''));
53
+ await writeJsonAtomic(path.join(dir, 'parallelism-summary.json'), input.summary);
54
+ }
55
+ //# sourceMappingURL=glm-naruto-parallelism-summary.js.map