sneakoscope 6.0.2 → 6.0.3

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 (49) hide show
  1. package/README.md +3 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -0
  5. package/dist/bin/sks-dispatch.js +2 -0
  6. package/dist/cli/install-helpers.js +41 -6
  7. package/dist/commands/codex-app.js +8 -3
  8. package/dist/commands/codex-lb.js +37 -12
  9. package/dist/config/skills-manifest.json +57 -57
  10. package/dist/core/agents/agent-effort-policy.js +46 -33
  11. package/dist/core/agents/agent-orchestrator.js +1 -1
  12. package/dist/core/agents/agent-proof-evidence.js +16 -2
  13. package/dist/core/agents/agent-roster.js +2 -2
  14. package/dist/core/agents/native-cli-session-swarm.js +18 -1
  15. package/dist/core/agents/native-worker-backend-router.js +179 -108
  16. package/dist/core/agents/parallel-write-fixture.js +15 -10
  17. package/dist/core/agents/runtime-proof-summary.js +21 -3
  18. package/dist/core/codex-app/codex-app-restart.js +38 -8
  19. package/dist/core/codex-app/sks-menubar.js +92 -37
  20. package/dist/core/codex-app.js +26 -4
  21. package/dist/core/codex-control/codex-model-metadata.js +8 -4
  22. package/dist/core/codex-lb/codex-lb-env.js +78 -0
  23. package/dist/core/commands/basic-cli.js +15 -0
  24. package/dist/core/commands/naruto-command.js +37 -9
  25. package/dist/core/naruto/naruto-write-e2e.js +3 -4
  26. package/dist/core/provider/model-router.js +64 -3
  27. package/dist/core/release/package-size-budget.js +2 -0
  28. package/dist/core/routes/constants.js +1 -1
  29. package/dist/core/routes/model-mode-router.js +3 -0
  30. package/dist/core/routes.js +7 -9
  31. package/dist/core/update/update-migration-state.js +1 -1
  32. package/dist/core/update/update-notice.js +23 -0
  33. package/dist/core/update-check.js +46 -10
  34. package/dist/core/version.js +1 -1
  35. package/dist/scripts/codex-app-provider-model-ui-check.js +17 -2
  36. package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
  37. package/dist/scripts/gate-policy-audit-check.js +2 -2
  38. package/dist/scripts/legacy-upgrade-matrix-check.js +1 -1
  39. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +2 -1
  40. package/dist/scripts/naruto-shadow-clone-swarm-check.js +88 -50
  41. package/dist/scripts/npm-publish-performance-check.js +2 -1
  42. package/dist/scripts/packlist-performance-check.js +5 -2
  43. package/dist/scripts/release-dag-full-coverage-check.js +7 -7
  44. package/dist/scripts/release-gate-existence-audit.js +8 -9
  45. package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
  46. package/dist/scripts/sizecheck.js +7 -2
  47. package/dist/scripts/sks-menubar-install-check.js +7 -0
  48. package/dist/scripts/update-default-command-check.js +4 -2
  49. package/package.json +3 -16
@@ -4,16 +4,75 @@ const CATEGORY_POLICY = {
4
4
  agentic: { reasoning: 'high', serviceTier: 'fast' },
5
5
  ultrabrain: { reasoning: 'xhigh', serviceTier: 'standard' },
6
6
  verify: { reasoning: 'medium', serviceTier: 'fast' },
7
- review: { reasoning: 'high', serviceTier: 'fast' }
7
+ review: { reasoning: 'high', serviceTier: 'fast' },
8
+ e2e: { reasoning: 'xhigh', serviceTier: 'fast' },
9
+ refactor: { reasoning: 'max', serviceTier: 'fast' },
10
+ strategy: { reasoning: 'max', serviceTier: 'fast' }
8
11
  };
12
+ export const NARUTO_GPT56_MODELS = ['gpt-5.6-luna', 'gpt-5.6-terra', 'gpt-5.6-sol'];
13
+ const NARUTO_LUNA_WORK_RE = /(e2e|end[-\s]?to[-\s]?end|test_execution|ux_review|browser|chrome|computer[-\s]?use|computer\s+use|gui|visual\s+(?:qa|test|check)|cross[-\s]?app|playwright|selenium|puppeteer|브라우저|컴퓨터\s*유즈|화면\s*검증)/i;
14
+ const NARUTO_SOL_WORK_RE = /(refactor|re-?architect|architecture|architect|planning|\bplan\b|strategy|strategic|integrat|conflict[-_\s]?resolv|patch[-_\s]?rebase|rollback[-_\s]?plan|gpt[-_\s]?final|arbiter|리팩터|아키텍처|기획|전략|통합|충돌\s*해결|롤백\s*계획)/i;
15
+ const NARUTO_MAX_WORK_RE = /(complex|critical|high[-\s]?risk|security|database|migration|release|publish|forensic|flaky|failure|cross[-\s]?app|multi[-\s]?step|복잡|고위험|보안|데이터베이스|마이그레이션|릴리즈|포렌식|실패)/i;
9
16
  export async function routeModel(category, opts = {}) {
17
+ if (opts.narutoOnly) {
18
+ return routeNarutoGpt56Model({
19
+ category,
20
+ ...(opts.taskText !== undefined ? { taskText: opts.taskText } : {}),
21
+ ...(opts.riskText !== undefined ? { riskText: opts.riskText } : {}),
22
+ ...(opts.model !== undefined ? { explicitModel: opts.model } : {}),
23
+ ...(opts.availableModels !== undefined ? { availableModels: opts.availableModels } : {}),
24
+ ...(opts.availableModelEfforts !== undefined ? { availableModelEfforts: opts.availableModelEfforts } : {}),
25
+ degradedModels: opts.lbHealth?.degraded_models || []
26
+ });
27
+ }
10
28
  const policy = CATEGORY_POLICY[category] || CATEGORY_POLICY.standard;
11
29
  const model = String(opts.model || process.env.SKS_CODEX_MODEL || process.env.CODEX_MODEL || '').trim();
12
30
  const reasoning = opts.lbHealth?.quota_low && policy.reasoning === 'xhigh' ? 'high' : policy.reasoning;
13
31
  return { model, reasoning, serviceTier: policy.serviceTier };
14
32
  }
15
- export function categoryForWorkerRole(role) {
16
- const text = String(role || '').toLowerCase();
33
+ export function routeNarutoGpt56Model(input = {}) {
34
+ const category = input.category || 'agentic';
35
+ const text = `${category} ${String(input.taskText || '')}`;
36
+ const riskText = `${text} ${String(input.riskText || '')}`;
37
+ const explicit = normalizeNarutoGpt56Model(input.explicitModel);
38
+ const preferred = explicit
39
+ || (category === 'refactor' || category === 'strategy' || category === 'ultrabrain' || NARUTO_SOL_WORK_RE.test(text)
40
+ ? 'gpt-5.6-sol'
41
+ : category === 'e2e' || NARUTO_LUNA_WORK_RE.test(text)
42
+ ? 'gpt-5.6-luna'
43
+ : 'gpt-5.6-terra');
44
+ const available = input.availableModels == null
45
+ ? [...NARUTO_GPT56_MODELS]
46
+ : input.availableModels.map(normalizeNarutoGpt56Model).filter((model) => Boolean(model));
47
+ const degraded = new Set((input.degradedModels || []).map((model) => String(model).toLowerCase()));
48
+ const usable = available.filter((model) => !degraded.has(model));
49
+ const availableEfforts = input.availableModelEfforts == null ? null : input.availableModelEfforts[preferred] || [];
50
+ const maxWork = NARUTO_MAX_WORK_RE.test(riskText);
51
+ const intendedReasoning = preferred === 'gpt-5.6-sol'
52
+ ? 'max'
53
+ : preferred === 'gpt-5.6-luna'
54
+ ? maxWork ? 'max' : 'xhigh'
55
+ : preferred === 'gpt-5.6-terra'
56
+ ? maxWork || category === 'review' ? 'max' : 'xhigh'
57
+ : 'xhigh';
58
+ const model = usable.includes(preferred) && (availableEfforts == null || availableEfforts.includes(intendedReasoning)) ? preferred : '';
59
+ return { model, reasoning: intendedReasoning, serviceTier: 'fast' };
60
+ }
61
+ export function isNarutoGpt56Model(value) {
62
+ return normalizeNarutoGpt56Model(value) !== null;
63
+ }
64
+ function normalizeNarutoGpt56Model(value) {
65
+ const model = String(value || '').trim().toLowerCase();
66
+ return NARUTO_GPT56_MODELS.includes(model) ? model : null;
67
+ }
68
+ export function categoryForWorkerRole(role, taskText = '') {
69
+ const text = `${String(role || '')} ${String(taskText || '')}`.toLowerCase();
70
+ if (NARUTO_LUNA_WORK_RE.test(text))
71
+ return 'e2e';
72
+ if (/(refactor|re-?architect|리팩터|아키텍처)/i.test(text))
73
+ return 'refactor';
74
+ if (/(planning|\bplan\b|strategy|strategic|기획|전략)/i.test(text))
75
+ return 'strategy';
17
76
  if (/verif|test|qa/.test(text))
18
77
  return 'verify';
19
78
  if (/review|critic|judge/.test(text))
@@ -28,6 +87,8 @@ export function modelRouteReason(category, choice, opts = {}) {
28
87
  const model = choice.model || 'codex-selected';
29
88
  if (opts.explicit)
30
89
  return `${category}->${model} (explicit model preserved)`;
90
+ if (isNarutoGpt56Model(choice.model))
91
+ return `${category}->${model}@${choice.reasoning} (Naruto GPT-5.6 family policy)`;
31
92
  const suffix = opts.quotaLow ? 'quota discipline' : 'Codex catalog passthrough';
32
93
  return `${category}->${model} (${suffix})`;
33
94
  }
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_MAX_PACK_BYTES = 2414 * 1024;
2
+ //# sourceMappingURL=package-size-budget.js.map
@@ -47,5 +47,5 @@ export const RECOMMENDED_SKILLS = [
47
47
  // Route-level effort names are scheduling intents, not model compatibility
48
48
  // claims. Actual per-model options come from Codex runtime metadata; SKS never
49
49
  // derives them from a model slug.
50
- export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'ultra']);
50
+ export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
51
51
  //# sourceMappingURL=constants.js.map
@@ -3,6 +3,9 @@ export function resolveSksModelMode(args = []) {
3
3
  const hasGlm = normalized.includes('--glm');
4
4
  const hasMad = normalized.includes('--mad') || normalized.includes('--MAD') || normalized.includes('--mad-sks');
5
5
  const hasNaruto = normalized.includes('--naruto') || normalized.includes('naruto');
6
+ if (hasGlm && hasNaruto && !hasMad) {
7
+ return decision('classic-naruto', false, true, 'naruto_command_glm_override_forbidden', normalized);
8
+ }
6
9
  if (hasGlm && hasNaruto) {
7
10
  return decision('glm-naruto', true, false, 'explicit_--glm_with_naruto', normalized);
8
11
  }
@@ -1162,19 +1162,17 @@ export function routeReasoning(route, prompt = '') {
1162
1162
  return reasoning('medium', 'simple_fulfillment');
1163
1163
  }
1164
1164
  function narutoRouteReasoning(text = '') {
1165
- if (/(frontier|autoresearch|novelty|hypothesis|falsify|forensic|from-chat-img|가설|포렌식)/i.test(text))
1166
- return reasoning('xhigh', 'naruto_research_or_forensic_signal');
1167
- if (/(research|current docs?|library|framework|sdk|api|database|supabase|sql|migration|security|permission|mad|release|publish|deploy|commit|push|architecture|algorithm|리서치|문서|데이터베이스|마이그레이션|보안|권한|배포|커밋|푸쉬)/i.test(text))
1168
- return reasoning('high', 'naruto_knowledge_safety_or_release_signal');
1169
- if (/(tmux|zellij|terminal|cli|cmd|warp|tool(?:\s|-)?call|hook|router|routing|pipeline|multi[-\s]?pane|pane|process|config|터미널|라우팅|파이프라인|훅|도구|툴)/i.test(text))
1170
- return reasoning('medium', 'naruto_tooling_or_runtime_signal');
1171
- if (/(tiny|simple|small|one[-\s]?line|typo|copy|label|spacing|rename|text|readme|docs?|간단|단순|오타|문구|라벨|간격|색상)/i.test(text))
1172
- return reasoning('low', 'naruto_simple_bounded_work_signal');
1173
- return reasoning('medium', 'naruto_default_balanced_reasoning');
1165
+ if (/(frontier|autoresearch|novelty|hypothesis|falsify|forensic|from-chat-img|research|current docs?|library|framework|sdk|api|database|supabase|sql|migration|security|permission|mad|release|publish|deploy|commit|push|architecture|algorithm|strategy|planning|integration|refactor|가설|포렌식|리서치|문서|데이터베이스|마이그레이션|보안|권한|배포|커밋|푸쉬|전략|기획|통합|리팩터)/i.test(text))
1166
+ return reasoning('max', 'naruto_complex_or_high_risk_signal');
1167
+ return reasoning('xhigh', 'naruto_gpt56_baseline');
1174
1168
  }
1175
1169
  export function reasoningProfileName(effort) {
1176
1170
  if (effort === 'low')
1177
1171
  return 'sks-task-low';
1172
+ if (effort === 'ultra')
1173
+ return 'sks-research-ultra';
1174
+ if (effort === 'max')
1175
+ return 'sks-research-max';
1178
1176
  if (effort === 'xhigh')
1179
1177
  return 'sks-research-xhigh';
1180
1178
  if (effort === 'high')
@@ -342,7 +342,7 @@ async function runMenubarRetargetStage(root) {
342
342
  if (typeof text !== 'string')
343
343
  return { ok: true, status: 'ok', actions: ['menubar_action_script_absent'], blockers: [], warnings: [] };
344
344
  const desired = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
345
- const line = `SKS_ENTRY=${JSON.stringify(desired)}`;
345
+ const line = `SKS_ENTRY='${desired.replace(/'/g, `'\\''`)}'`;
346
346
  const actions = [];
347
347
  const next = /^\s*SKS_ENTRY\s*=.*$/m.test(text)
348
348
  ? text.replace(/^\s*SKS_ENTRY\s*=.*$/m, line)
@@ -3,6 +3,29 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { ensureDir, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
5
5
  export const SKS_UPDATE_NOTICE_SCHEMA = 'sks.update-notice.v1';
6
+ export async function persistSksUpdateNoticeFromVersions(input) {
7
+ const packageName = input.packageName || 'sneakoscope';
8
+ const latestVersion = input.latestVersion || null;
9
+ const updateAvailable = Boolean(latestVersion && compareVersions(latestVersion, input.currentVersion) > 0);
10
+ const notice = {
11
+ schema: SKS_UPDATE_NOTICE_SCHEMA,
12
+ checked_at: nowIso(),
13
+ package_name: packageName,
14
+ current_version: input.currentVersion,
15
+ latest_version: latestVersion,
16
+ update_available: updateAvailable,
17
+ source: input.error ? 'error' : 'cache',
18
+ cache_ttl_ms: 6 * 60 * 60 * 1000,
19
+ message: updateAvailable
20
+ ? `SKS ${latestVersion} is available; current ${input.currentVersion}.`
21
+ : `SKS ${input.currentVersion} is current enough.`,
22
+ ...(input.error ? { error: input.error } : {})
23
+ };
24
+ const cachePath = path.join(os.homedir(), '.sneakoscope', 'cache', 'update-notice.json');
25
+ await ensureDir(path.dirname(cachePath));
26
+ await writeJsonAtomic(cachePath, notice);
27
+ return notice;
28
+ }
6
29
  export async function checkSksUpdateNotice(input = {}) {
7
30
  const env = input.env || process.env;
8
31
  const packageName = input.packageName || env.SKS_UPDATE_NOTICE_PACKAGE || 'sneakoscope';
@@ -6,7 +6,7 @@ import { PACKAGE_VERSION, packageRoot, readJson, runProcess, throttleLines, whic
6
6
  import { createRequestedScopeContract } from './safety/requested-scope-contract.js';
7
7
  import { guardedPackageInstall, guardContextForRoute } from './safety/mutation-guard.js';
8
8
  import { isUpdateMigrationReceiptCurrent, resolveInstalledSksEntrypoint, runPackageLocalDoctor, writeProjectUpdateMigrationReceipt } from './update/update-migration-state.js';
9
- import { installSksMenuBar } from './codex-app/sks-menubar.js';
9
+ import { installSksMenuBar, sksMenuBarPaths } from './codex-app/sks-menubar.js';
10
10
  import { reconcileSkills } from './init/skills.js';
11
11
  import { codexHookTrustDoctor } from './codex-hooks/codex-hook-trust-doctor.js';
12
12
  import { readCodexHookActualState } from './codex-hooks/codex-hook-actual-discovery.js';
@@ -367,7 +367,7 @@ export async function runSksUpdateNow(options = {}) {
367
367
  migrationCurrent = isUpdateMigrationReceiptCurrent(projectReceipt);
368
368
  stage('project_receipt', migrationCurrent, migrationCurrent ? 'current' : 'failed', { root: projectReceiptRoot });
369
369
  if (migrationCurrent)
370
- sksMenuBar = await installUpdateSksMenuBar({ root: projectReceiptRoot, env, stage, quiet: machineOutput });
370
+ sksMenuBar = await installUpdateSksMenuBar({ root: projectReceiptRoot, env, stage, quiet: machineOutput, entrypoint: newBinary });
371
371
  await runUpdateGlobalSkillsReconcile(stage, {
372
372
  quiet: machineOutput,
373
373
  newPackageRoot: newBinary ? path.resolve(path.dirname(newBinary), '..', '..') : null
@@ -709,18 +709,20 @@ function buildUpdateNowResult(input) {
709
709
  error: input.error
710
710
  };
711
711
  }
712
- async function installUpdateSksMenuBar(input) {
712
+ export async function installUpdateSksMenuBar(input) {
713
713
  if (input.env.SKS_UPDATE_SKIP_SKS_MENUBAR === '1') {
714
714
  input.stage('sks_menubar', true, 'skipped', { reason: 'SKS_UPDATE_SKIP_SKS_MENUBAR=1' });
715
715
  return null;
716
716
  }
717
- const work = installSksMenuBar({
718
- root: input.root,
719
- apply: true,
720
- launch: true,
721
- env: input.env,
722
- quiet: input.quiet === true
723
- }).catch((err) => ({
717
+ const work = (input.entrypoint
718
+ ? installSksMenuBarFromEntrypoint(input.entrypoint, input)
719
+ : installSksMenuBar({
720
+ root: input.root,
721
+ apply: true,
722
+ launch: true,
723
+ env: input.env,
724
+ quiet: input.quiet === true
725
+ })).catch((err) => ({
724
726
  schema: 'sks.codex-app-sks-menubar.v1',
725
727
  ok: false,
726
728
  apply: true,
@@ -753,6 +755,30 @@ async function installUpdateSksMenuBar(input) {
753
755
  });
754
756
  return result;
755
757
  }
758
+ async function installSksMenuBarFromEntrypoint(entrypoint, input) {
759
+ const run = await runProcess(process.execPath, [entrypoint, 'menubar', 'install', '--json'], {
760
+ cwd: input.root,
761
+ env: {
762
+ ...input.env,
763
+ SKS_DISABLE_UPDATE_CHECK: '1',
764
+ SKS_UPDATE_MIGRATION_GATE_DISABLED: '1'
765
+ },
766
+ timeoutMs: updateDoctorTimeoutMs(input.env),
767
+ maxOutputBytes: 128 * 1024
768
+ });
769
+ const output = String(run.stdout || '').trim();
770
+ let parsed = null;
771
+ try {
772
+ parsed = JSON.parse(output);
773
+ }
774
+ catch {
775
+ parsed = null;
776
+ }
777
+ if (run.code !== 0 || parsed?.schema !== 'sks.codex-app-sks-menubar.v1') {
778
+ throw new Error(String(run.stderr || output || `updated SKS menu bar installer exited ${run.code}`).trim());
779
+ }
780
+ return parsed;
781
+ }
756
782
  async function detectNpmGlobalRoot(npmBin, env, opts = {}) {
757
783
  const result = await runProcess(npmBin, ['root', '--global', '--silent'], {
758
784
  env,
@@ -820,6 +846,16 @@ async function runFinalUpdateVerification(input) {
820
846
  detail: `expected ${input.installVersion}, got ${skillsManifest?.version || 'missing'}`,
821
847
  remediation: 'Run: sks doctor --fix --yes'
822
848
  });
849
+ if (process.platform === 'darwin' && input.env.SKS_UPDATE_SKIP_SKS_MENUBAR !== '1') {
850
+ const menuStampPath = sksMenuBarPaths(home, input.projectReceiptRoot).build_stamp_path;
851
+ const menuStamp = await readJson(menuStampPath, null).catch(() => null);
852
+ verification.push({
853
+ id: 'sks_menubar_version',
854
+ ok: menuStamp?.package_version === input.installVersion,
855
+ detail: `expected ${input.installVersion}, got ${menuStamp?.package_version || 'missing'}`,
856
+ remediation: `Run: ${process.execPath} ${input.newBinary} menubar install --json`
857
+ });
858
+ }
823
859
  return verification;
824
860
  }
825
861
  function verificationError(verification) {
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '6.0.2';
1
+ export const PACKAGE_VERSION = '6.0.3';
2
2
  //# sourceMappingURL=version.js.map
@@ -17,7 +17,19 @@ await fs.writeFile(path.join(home, '.codex', 'sks-codex-lb.env'), "export CODEX_
17
17
  const ready = await codexProviderModelUiStatus({
18
18
  home,
19
19
  cwd,
20
- env: { HOME: home, OPENROUTER_API_KEY: 'sk-or-fixture' }
20
+ env: { HOME: home, OPENROUTER_API_KEY: 'sk-or-fixture' },
21
+ codexLbModelCatalog: {
22
+ schema: 'sks.codex-lb-model-catalog.v1',
23
+ ok: true,
24
+ status: 'ready',
25
+ models: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'],
26
+ model_efforts: {
27
+ 'gpt-5.6-sol': ['xhigh', 'max', 'ultra'],
28
+ 'gpt-5.6-terra': ['xhigh', 'max', 'ultra'],
29
+ 'gpt-5.6-luna': ['xhigh', 'max']
30
+ },
31
+ blockers: []
32
+ }
21
33
  });
22
34
  const readyText = formatCodexAppStatus(statusFixture(ready));
23
35
  const ok = missing.ok === false
@@ -34,7 +46,10 @@ const ok = missing.ok === false
34
46
  && ready.glm.profiles_present.length === GLM_CODEX_CONFIG_REASONING_PROFILES.length
35
47
  && ready.codex_lb.provider_present === true
36
48
  && ready.codex_lb.key_present === true
37
- && /Provider UI:\s*ok/.test(readyText)
49
+ && ready.codex_lb.model_catalog_ok === true
50
+ && ready.codex_lb.expected_models_present === true
51
+ && /Provider UI:\s*config\/catalog ready/.test(readyText)
52
+ && ready.desktop_picker_verified === false
38
53
  && readyText.includes(`GLM Model: ok ${GLM_52_OPENROUTER_MODEL}`)
39
54
  && /codex-lb Key:\s*configured/.test(readyText)
40
55
  && !JSON.stringify({ missing, ready, missingText, readyText }).includes('sk-codex-lb-fixture');
@@ -49,7 +49,7 @@ const c7Fixture = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-doctor-fix-c7-'))
49
49
  const c7Home = path.join(c7Fixture, 'home');
50
50
  await fs.mkdir(path.join(c7Fixture, '.codex'), { recursive: true });
51
51
  await fs.mkdir(c7Home, { recursive: true });
52
- await fs.writeFile(path.join(c7Fixture, '.codex', 'config.toml'), '[mcp_servers.context7]\ncommand = "npx"\nargs = ["-y", "@upstash/context7-mcp@latest"]\n');
52
+ await fs.writeFile(path.join(c7Fixture, '.codex', 'config.toml'), '# Sneakoscope managed fixture\n[mcp_servers.context7]\ncommand = "npx"\nargs = ["-y", "@upstash/context7-mcp@latest"]\n');
53
53
  const c7Run = runDoctorFix(c7Fixture, c7Home, { SKS_FAKE_CODEX_CONFIG_CONTEXT7_STDIO_CONFLICT: '1' });
54
54
  const c7Parsed = parseLastJson(c7Run.stdout || '{}');
55
55
  const c7ConfigText = await fs.readFile(path.join(c7Fixture, '.codex', 'config.toml'), 'utf8').catch(() => '');
@@ -26,9 +26,9 @@ const directCodexConfigWrites = scanDirectCodexConfigWrites();
26
26
  const commandGateContract = scanCommandGateContract();
27
27
  const directMkdtempCalls = scanDirectMkdtempCalls();
28
28
  const rustTempdirCalls = scanRustTempdirCalls();
29
- if (releaseGates.length > 220)
29
+ if (releaseGates.length > 200)
30
30
  blockers.push(`release_preset_gate_budget_exceeded:${releaseGates.length}`);
31
- if (gates.length > 220)
31
+ if (gates.length > 200)
32
32
  blockers.push(`release_manifest_gate_budget_exceeded:${gates.length}`);
33
33
  if (Object.keys(pkg.scripts || {}).length > 100)
34
34
  blockers.push(`package_script_budget_exceeded:${Object.keys(pkg.scripts || {}).length}`);
@@ -121,7 +121,7 @@ try {
121
121
  const projectConfigPath = path.join(projectCodexDir, 'config.toml');
122
122
  // Machine-local keys/tables (model_provider, [profiles.*]) move out to CODEX_HOME;
123
123
  // project-scoped settings (sandbox_mode, [features]) must be PRESERVED in place.
124
- fs.writeFileSync(projectConfigPath, 'model_provider = "codex-lb"\nsandbox_mode = "workspace-write"\n\n[features]\nhooks = true\n', 'utf8');
124
+ fs.writeFileSync(projectConfigPath, '# Sneakoscope managed fixture\nmodel_provider = "codex-lb"\nsandbox_mode = "workspace-write"\n\n[features]\nhooks = true\n', 'utf8');
125
125
  const result = await policy.splitCodexProjectConfigPolicy(projectRoot, {
126
126
  apply: true,
127
127
  codexHome: tmpHome2,
@@ -63,7 +63,8 @@ export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName,
63
63
  assertGate(result.ok === true, 'native CLI session swarm run must pass', report);
64
64
  assertGate(proof.spawned_worker_process_count >= agents, 'spawned native worker process count below requested agents', report);
65
65
  assertGate(proof.unique_worker_session_count >= agents, 'unique worker session count below requested agents', report);
66
- assertGate(proof.unique_slot_count >= agents, 'unique slot count below requested agents', report);
66
+ const expectedSlots = Math.min(agents, Number(proof.target_active_slots || agents));
67
+ assertGate(proof.unique_slot_count >= expectedSlots, 'unique slot count below effective concurrency target', report);
67
68
  assertGate(Array.isArray(proof.process_ids) && proof.process_ids.length >= agents, 'process ids missing from native CLI proof', report);
68
69
  assertGate(proof.close_report_count >= agents, 'worker close report count below requested agents', report);
69
70
  assertGate(noSubagent.ok === true && noSubagent.subagent_events_counted_as_worker_sessions === false, 'no-subagent scaling policy must pass', report);
@@ -29,31 +29,30 @@ assertGate(fullRoster.max_agents === 100, 'clone roster max_agents must be 100',
29
29
  // 4) Over-cap is clamped, not crashed.
30
30
  const overCap = roster.buildNarutoCloneRoster({ clones: 500, maxAgentCount: schema.MAX_NARUTO_AGENT_COUNT });
31
31
  assertGate(overCap.agent_count === 100, 'clones above the ceiling clamp to 100', { agent_count: overCap.agent_count });
32
- // 4b) Dynamic per-clone effort policy (like team mode), capped + always fast.
33
- // A neutral/no-tool prompt yields a low/medium MIX: read-only personas -> low, writers -> medium.
32
+ // 4b) Naruto-only GPT-5.6 family policy. The old low/medium cap is gone.
34
33
  const effortRoster = roster.buildNarutoCloneRoster({ clones: 24, prompt: 'review and summarize the current findings', maxAgentCount: schema.MAX_NARUTO_AGENT_COUNT });
35
34
  const efforts = new Set(effortRoster.roster.map((row) => row.reasoning_effort));
36
35
  const modelTiers = new Set(effortRoster.roster.map((row) => row.model_tier));
37
36
  const modelEfforts = new Set(effortRoster.roster.map((row) => row.model_reasoning_effort));
38
37
  const tiers = new Set(effortRoster.roster.map((row) => row.service_tier));
39
38
  const fastFlags = new Set(effortRoster.roster.map((row) => row.fast_mode));
40
- assertGate([...efforts].every((e) => e === 'low' || e === 'medium'), 'naruto clone efforts must be only low/medium (never high/xhigh)', { efforts: [...efforts] });
41
- assertGate(efforts.has('low') && efforts.has('medium'), 'naruto effort must be dynamic: a no-tool prompt must yield both low and medium clones', { efforts: [...efforts] });
42
- assertGate([...modelTiers].every((tier) => tier === 'codex-selected-low' || tier === 'codex-selected-medium'), 'Naruto must inherit the Codex-selected model and vary only reasoning effort', { modelTiers: [...modelTiers] });
43
- assertGate(modelTiers.has('codex-selected-low') && modelTiers.has('codex-selected-medium'), 'Naruto effort allocation must dynamically mix low and medium without changing model IDs', { modelTiers: [...modelTiers] });
44
- assertGate([...modelEfforts].every((effort) => effort === 'low' || effort === 'medium'), 'ordinary Naruto clone model reasoning must stay low/medium', { modelEfforts: [...modelEfforts] });
39
+ assertGate([...efforts].every((effort) => effort === 'xhigh' || effort === 'max'), 'Naruto clone efforts must use only xhigh/max', { efforts: [...efforts] });
40
+ assertGate([...effortRoster.roster].every((row) => ['gpt-5.6-luna', 'gpt-5.6-terra', 'gpt-5.6-sol'].includes(row.model)), 'Naruto workers must use only the GPT-5.6 trio', { models: [...new Set(effortRoster.roster.map((row) => row.model))] });
41
+ assertGate([...modelTiers].every((tier) => /gpt-5\.6-(?:luna|terra|sol)-(?:xhigh|max)/.test(tier)), 'Naruto model tiers must record exact GPT-5.6 model and effort', { modelTiers: [...modelTiers] });
42
+ assertGate([...modelEfforts].every((effort) => effort === 'xhigh' || effort === 'max'), 'Naruto model reasoning must use xhigh/max', { modelEfforts: [...modelEfforts] });
45
43
  assertGate([...tiers].length === 1 && tiers.has('fast'), 'every naruto clone must be fast service tier', { tiers: [...tiers] });
46
44
  assertGate([...fastFlags].length === 1 && fastFlags.has(true), 'every naruto clone must have fast_mode=true', { fastFlags: [...fastFlags] });
47
- // 4c) Unit rule: no tool use -> low; any tool use -> medium; always fast.
48
- const simple = effortPolicy.decideNarutoCloneEffort({ readonly: true, prompt: 'simple docs overview' });
49
- const toolWrite = effortPolicy.decideNarutoCloneEffort({ readonly: false, prompt: 'add a feature' });
50
- const toolReadCmd = effortPolicy.decideNarutoCloneEffort({ readonly: true, prompt: 'run the build and apply the migration' });
51
- const simpleWrite = effortPolicy.decideNarutoCloneEffort({ readonly: false, prompt: 'simple one-line typo fix' });
52
- assertGate(simple.reasoning_effort === 'low' && simple.service_tier === 'fast', 'no-tool read-only work must be low + fast', { simple });
53
- assertGate(simple.model === '' && simple.model_reasoning_effort === 'low', 'no-tool read-only work must inherit the Codex model at low effort', { simple });
54
- assertGate(toolWrite.reasoning_effort === 'medium' && toolWrite.service_tier === 'fast' && toolWrite.model === '' && toolWrite.model_reasoning_effort === 'medium', 'writing clone must inherit the Codex model at medium effort', { toolWrite });
55
- assertGate(simpleWrite.model === '' && simpleWrite.model_reasoning_effort === 'low', 'simple write work must inherit the Codex model at low effort', { simpleWrite });
56
- assertGate(toolReadCmd.reasoning_effort === 'medium' && toolReadCmd.service_tier === 'fast' && toolReadCmd.model_reasoning_effort === 'high', 'risky read-only command/migration work must escalate model reasoning to high', { toolReadCmd });
45
+ // 4c) Deterministic work-kind matrix.
46
+ const coding = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'implementer', naruto_role: 'implementer' }, prompt: 'implement a normal coding feature' });
47
+ const criticalCoding = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'implementer', naruto_role: 'implementer' }, prompt: 'implement a critical security migration' });
48
+ const refactor = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'integrator', naruto_role: 'integrator' }, prompt: 'refactor the architecture and integration plan' });
49
+ const e2e = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'browser e2e verifier', naruto_role: 'verifier' }, prompt: 'run browser e2e tests' });
50
+ const forensicGui = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'Computer Use GUI verifier', naruto_role: 'verifier' }, prompt: 'forensic cross-app failure verification' });
51
+ assertGate(coding.model === 'gpt-5.6-terra' && coding.model_reasoning_effort === 'xhigh', 'ordinary coding must use Terra xhigh', { coding });
52
+ assertGate(criticalCoding.model === 'gpt-5.6-terra' && criticalCoding.model_reasoning_effort === 'max', 'critical coding must use Terra max', { criticalCoding });
53
+ assertGate(refactor.model === 'gpt-5.6-sol' && refactor.model_reasoning_effort === 'max', 'refactor/architecture/integration must use Sol max', { refactor });
54
+ assertGate(e2e.model === 'gpt-5.6-luna' && e2e.model_reasoning_effort === 'xhigh', 'browser E2E must use Luna xhigh', { e2e });
55
+ assertGate(forensicGui.model === 'gpt-5.6-luna' && forensicGui.model_reasoning_effort === 'max', 'forensic Computer Use GUI verification must use Luna max', { forensicGui });
57
56
  // 5) System-safe concurrency: never spawn the whole count at once; throttle to host capacity.
58
57
  const fakeSafe = roster.systemSafeNarutoConcurrency({ backend: 'fake' });
59
58
  const heavySafe = roster.systemSafeNarutoConcurrency({ backend: 'codex-sdk' });
@@ -91,47 +90,60 @@ const run = spawnSync(process.execPath, [
91
90
  '--json',
92
91
  '--no-open-zellij'
93
92
  ], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 780000, maxBuffer: 8 * 1024 * 1024 });
94
- assertGate(run.status === 0, 'sks naruto run must exit 0', { status: run.status, stderr: tail(run.stderr) });
95
93
  const parsed = parseJson(run.stdout);
96
- assertGate(parsed !== null, 'sks naruto run must emit JSON', { stdout: tail(run.stdout) });
97
- assertGate(parsed.ok === true, 'naruto run must be ok', { ok: parsed.ok });
98
- assertGate(parsed.mode === 'NARUTO' && parsed.jutsu === 'kage_bunshin_no_jutsu', 'naruto run must report NARUTO mode', { mode: parsed.mode, jutsu: parsed.jutsu });
99
- assertGate(parsed.clones === proofClones, 'clone fan-out must use the requested queued count', { clones: parsed.clones });
100
- assertGate(parsed.max_clones === 100, 'naruto run must advertise the 100 ceiling', { max_clones: parsed.max_clones });
101
- assertGate(parsed.proof === 'passed', 'naruto run proof must pass', { proof: parsed.proof });
102
- // Throttle invariant: active concurrency never exceeds the requested count nor the host cap.
103
- assertGate(parsed.target_active_slots >= 1 && parsed.target_active_slots <= proofClones, 'active slots must be in [1, clones]', { target_active_slots: parsed.target_active_slots });
104
- assertGate(parsed.target_active_slots <= fakeSafe.cap, 'active slots must be throttled to the system-safe cap', { target_active_slots: parsed.target_active_slots, cap: fakeSafe.cap });
105
- assertGate(parsed.target_active_slots <= proofConcurrency && parsed.target_active_slots <= 4, 'release proof must keep Naruto live slots desktop-safe while queued fan-out remains larger', { target_active_slots: parsed.target_active_slots, proofConcurrency });
106
- // Task 9.1: fan-out (clones) and live concurrency (target_active_slots) are reported
107
- // distinctly, and concurrency_capped truthfully reflects "N clones, running M at a time".
108
- assertGate(typeof parsed.concurrency_capped === 'boolean', 'naruto run must report concurrency_capped', { concurrency_capped: parsed.concurrency_capped });
109
- assertGate(parsed.concurrency_capped === (parsed.clones > parsed.target_active_slots), 'concurrency_capped must reflect clones > live slots', { clones: parsed.clones, target_active_slots: parsed.target_active_slots, concurrency_capped: parsed.concurrency_capped });
110
- assertGate(parsed.system && Number(parsed.system.safe_concurrency) >= 1, 'naruto run must report system safe_concurrency (host-derived cap)', { system: parsed.system });
111
- assertGate(parsed.work_graph?.write_allowed_count > 0 && parsed.work_graph?.mixed_work_kinds?.length > 1, 'naruto run must report a mixed work graph with write-capable items', { work_graph: parsed.work_graph });
112
- assertGate(Number(parsed.work_graph?.parallel_write_wave_count || 0) > 0, 'naruto run must expose at least one parallel write-capable wave with non-overlapping leases', { work_graph: parsed.work_graph });
113
- assertGate(parsed.role_distribution?.verifier_only === false, 'naruto run proof/status must distinguish active worker roles beyond verifier-only', { role_distribution: parsed.role_distribution });
114
- assertGate(Number(parsed.role_distribution?.implementation_like_ratio || 0) >= 0.4, 'naruto run must include implementation-like role distribution', { role_distribution: parsed.role_distribution });
115
- const commandGraphPath = path.join(root, '.sneakoscope', 'missions', parsed.mission_id, 'agents', 'naruto-work-graph.json');
116
- const commandGraph = JSON.parse(fs.readFileSync(commandGraphPath, 'utf8'));
117
- const writePaths = commandGraph.work_items.filter((row) => row.write_allowed).flatMap((row) => row.write_paths);
118
- assertGate(new Set(writePaths).size > 1, 'naruto command default patch-envelope leases must be per work item, not one shared write path', { sample: writePaths.slice(0, 8), unique: new Set(writePaths).size });
119
- assertGate(commandGraph.active_waves.some((wave) => wave.write_paths.length > 1), 'naruto command graph must contain a parallel write wave when route-local patch envelopes do not overlap', { waves: commandGraph.active_waves.slice(0, 3) });
120
- const state = parsed.run?.scheduler?.state || parsed.run?.scheduler || {};
121
- assertGate(Number(state.completed_count) === Number(parsed.work_graph?.total_work_items || 0) && Number(state.completed_count) >= proofClones, 'all clone work items must complete despite throttling', { completed_count: state.completed_count, total_work_items: parsed.work_graph?.total_work_items, proofClones });
122
- assertGate(parsed.target_active_slots <= 4, 'explicit --concurrency cannot bypass the desktop-safe cap', { target_active_slots: parsed.target_active_slots });
94
+ assertGate(parsed !== null, 'sks naruto run must emit JSON', { stdout: tail(run.stdout), stderr: tail(run.stderr) });
95
+ let state = {};
96
+ withMissionRouteClose(parsed, childEnv, () => {
97
+ assertGate(run.status === 0, 'sks naruto run must exit 0', { status: run.status, stderr: tail(run.stderr) });
98
+ assertGate(parsed.ok === true, 'naruto run must be ok', { ok: parsed.ok });
99
+ assertGate(parsed.mode === 'NARUTO' && parsed.jutsu === 'kage_bunshin_no_jutsu', 'naruto run must report NARUTO mode', { mode: parsed.mode, jutsu: parsed.jutsu });
100
+ assertGate(parsed.clones === proofClones, 'clone fan-out must use the requested queued count', { clones: parsed.clones });
101
+ assertGate(parsed.max_clones === 100, 'naruto run must advertise the 100 ceiling', { max_clones: parsed.max_clones });
102
+ assertGate(parsed.proof === 'passed', 'naruto run proof must pass', { proof: parsed.proof });
103
+ // Throttle invariant: active concurrency never exceeds the requested count nor the host cap.
104
+ assertGate(parsed.target_active_slots >= 1 && parsed.target_active_slots <= proofClones, 'active slots must be in [1, clones]', { target_active_slots: parsed.target_active_slots });
105
+ assertGate(parsed.target_active_slots <= fakeSafe.cap, 'active slots must be throttled to the system-safe cap', { target_active_slots: parsed.target_active_slots, cap: fakeSafe.cap });
106
+ assertGate(parsed.target_active_slots <= proofConcurrency && parsed.target_active_slots <= 4, 'release proof must keep Naruto live slots desktop-safe while queued fan-out remains larger', { target_active_slots: parsed.target_active_slots, proofConcurrency });
107
+ // Task 9.1: fan-out (clones) and live concurrency (target_active_slots) are reported
108
+ // distinctly, and concurrency_capped truthfully reflects "N clones, running M at a time".
109
+ assertGate(typeof parsed.concurrency_capped === 'boolean', 'naruto run must report concurrency_capped', { concurrency_capped: parsed.concurrency_capped });
110
+ assertGate(parsed.concurrency_capped === (parsed.clones > parsed.target_active_slots), 'concurrency_capped must reflect clones > live slots', { clones: parsed.clones, target_active_slots: parsed.target_active_slots, concurrency_capped: parsed.concurrency_capped });
111
+ assertGate(parsed.system && Number(parsed.system.safe_concurrency) >= 1, 'naruto run must report system safe_concurrency (host-derived cap)', { system: parsed.system });
112
+ assertGate(parsed.work_graph?.write_allowed_count > 0 && parsed.work_graph?.mixed_work_kinds?.length > 1, 'naruto run must report a mixed work graph with write-capable items', { work_graph: parsed.work_graph });
113
+ assertGate(Number(parsed.work_graph?.parallel_write_wave_count || 0) > 0, 'naruto run must expose at least one parallel write-capable wave with non-overlapping leases', { work_graph: parsed.work_graph });
114
+ assertGate(parsed.role_distribution?.verifier_only === false, 'naruto run proof/status must distinguish active worker roles beyond verifier-only', { role_distribution: parsed.role_distribution });
115
+ assertGate(Number(parsed.role_distribution?.implementation_like_ratio || 0) >= 0.4, 'naruto run must include implementation-like role distribution', { role_distribution: parsed.role_distribution });
116
+ assertGate(parsed.local_worker?.auto_select_eligible === false, 'normal Naruto must never auto-select a local/Ollama backend', { local_worker: parsed.local_worker });
117
+ const commandGraphPath = path.join(root, '.sneakoscope', 'missions', parsed.mission_id, 'agents', 'naruto-work-graph.json');
118
+ const commandGraph = JSON.parse(fs.readFileSync(commandGraphPath, 'utf8'));
119
+ const writePaths = commandGraph.work_items.filter((row) => row.write_allowed).flatMap((row) => row.write_paths);
120
+ assertGate(new Set(writePaths).size > 1, 'naruto command default patch-envelope leases must be per work item, not one shared write path', { sample: writePaths.slice(0, 8), unique: new Set(writePaths).size });
121
+ assertGate(commandGraph.active_waves.some((wave) => wave.write_paths.length > 1), 'naruto command graph must contain a parallel write wave when route-local patch envelopes do not overlap', { waves: commandGraph.active_waves.slice(0, 3) });
122
+ state = parsed.run?.scheduler?.state || parsed.run?.scheduler || {};
123
+ assertGate(Number(state.completed_count) === Number(parsed.work_graph?.total_work_items || 0) && Number(state.completed_count) >= proofClones, 'all clone work items must complete despite throttling', { completed_count: state.completed_count, total_work_items: parsed.work_graph?.total_work_items, proofClones });
124
+ assertGate(parsed.target_active_slots <= 4, 'explicit --concurrency cannot bypass the desktop-safe cap', { target_active_slots: parsed.target_active_slots });
125
+ });
123
126
  // 7) A small request is never inflated; live pressure may still reduce it.
124
127
  const small = spawnSync(process.execPath, [cli, 'naruto', 'run', 'tiny', '--clones', '2', '--backend', 'fake', '--work-items', '2', '--json', '--no-open-zellij'], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 600000, maxBuffer: 4 * 1024 * 1024 });
125
128
  const smallParsed = parseJson(small.stdout);
126
- assertGate(small.status === 0 && smallParsed?.target_active_slots >= 1 && smallParsed?.target_active_slots <= 2, 'a 2-clone run must stay within [1, 2] active workers', { status: small.status, target_active_slots: smallParsed?.target_active_slots });
129
+ withMissionRouteClose(smallParsed, childEnv, () => {
130
+ assertGate(small.status === 0 && smallParsed?.target_active_slots >= 1 && smallParsed?.target_active_slots <= 2, 'a 2-clone run must stay within [1, 2] active workers', {
131
+ status: small.status,
132
+ target_active_slots: smallParsed?.target_active_slots,
133
+ stdout: tail(small.stdout),
134
+ stderr: tail(small.stderr)
135
+ });
136
+ });
127
137
  // 8) Naruto clones always run fast. Per-route --no-fast / standard-tier requests
128
138
  // are intentionally not honored for shadow clones.
129
139
  const standardOptOut = spawnSync(process.execPath, [cli, 'naruto', 'run', 'fast opt-out ignored', '--clones', '2', '--backend', 'fake', '--work-items', '2', '--no-fast', '--service-tier', 'standard', '--json', '--no-open-zellij'], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 600000, maxBuffer: 4 * 1024 * 1024 });
130
140
  const standardOptOutParsed = parseJson(standardOptOut.stdout);
131
- assertGate(standardOptOut.status === 0 && standardOptOutParsed?.fast_mode_policy?.fast_mode === true && standardOptOutParsed?.fast_mode_policy?.service_tier === 'fast', 'naruto --no-fast / --service-tier standard must still run clones in fast mode', {
132
- status: standardOptOut.status,
133
- fast_mode_policy: standardOptOutParsed?.fast_mode_policy,
134
- stderr: tail(standardOptOut.stderr)
141
+ withMissionRouteClose(standardOptOutParsed, childEnv, () => {
142
+ assertGate(standardOptOut.status === 0 && standardOptOutParsed?.fast_mode_policy?.fast_mode === true && standardOptOutParsed?.fast_mode_policy?.service_tier === 'fast', 'naruto --no-fast / --service-tier standard must still run clones in fast mode', {
143
+ status: standardOptOut.status,
144
+ fast_mode_policy: standardOptOutParsed?.fast_mode_policy,
145
+ stderr: tail(standardOptOut.stderr)
146
+ });
135
147
  });
136
148
  emitGate('naruto:shadow-clone-swarm', {
137
149
  max_naruto_agent_count: schema.MAX_NARUTO_AGENT_COUNT,
@@ -162,4 +174,30 @@ function tail(value, limit = 2000) {
162
174
  const text = String(value || '');
163
175
  return text.length <= limit ? text : text.slice(-limit);
164
176
  }
177
+ function closeMission(missionId, env) {
178
+ assertGate(Boolean(missionId), 'naruto proof mission id missing before route close', { missionId });
179
+ const closed = spawnSync(process.execPath, [cli, 'route', 'close', '--mission', String(missionId), '--json'], {
180
+ cwd: root,
181
+ env,
182
+ encoding: 'utf8',
183
+ timeout: 30000,
184
+ maxBuffer: 1024 * 1024
185
+ });
186
+ const parsedClose = parseJson(closed.stdout);
187
+ assertGate(closed.status === 0 && parsedClose?.ok === true, 'naruto proof mission route close must succeed', {
188
+ missionId,
189
+ status: closed.status,
190
+ stdout: tail(closed.stdout),
191
+ stderr: tail(closed.stderr)
192
+ });
193
+ }
194
+ function withMissionRouteClose(parsedResult, env, verify) {
195
+ try {
196
+ return verify();
197
+ }
198
+ finally {
199
+ if (parsedResult?.mission_id)
200
+ closeMission(parsedResult.mission_id, env);
201
+ }
202
+ }
165
203
  //# sourceMappingURL=naruto-shadow-clone-swarm-check.js.map
@@ -3,9 +3,10 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { spawnSync } from 'node:child_process';
6
+ import { DEFAULT_MAX_PACK_BYTES } from '../core/release/package-size-budget.js';
6
7
  import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
7
8
  const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 2100);
8
- const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || 2410 * 1024);
9
+ const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || DEFAULT_MAX_PACK_BYTES);
9
10
  const BUDGET_MS = Number(process.env.SKS_PACK_BUDGET_MS || 30000);
10
11
  function runNpmPack() {
11
12
  const npmCli = process.env.npm_execpath; // set when invoked via `npm run`
@@ -4,6 +4,7 @@ import fs from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
6
  import { spawnSync } from 'node:child_process';
7
+ import { DEFAULT_MAX_PACK_BYTES } from '../core/release/package-size-budget.js';
7
8
  import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
8
9
  const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 2100);
9
10
  const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 10 * 1024 * 1024);
@@ -22,8 +23,10 @@ const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 10 * 1024 * 10
22
23
  // checks; keep those scripts publishable because package contract validation
23
24
  // rejects scripts that point at files absent from the tarball. 5.12.0 adds ops
24
25
  // maturity diagnostics, migration/recovery smokes, and their package-referenced
25
- // gates, pushing the packed tarball to ~2400 KiB.
26
- const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || 2410 * 1024);
26
+ // gates, pushing the packed tarball to ~2400 KiB. 6.0.3 adds the menu restart
27
+ // lifecycle and Naruto GPT-5.6 routing enforcement, bringing the measured
28
+ // packed size to ~2411 KiB; keep the next 1 KiB boundary as the narrow cap.
29
+ const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || DEFAULT_MAX_PACK_BYTES);
27
30
  const SURFACE_MAX_PACKED = Number(process.env.SKS_PACKAGE_SURFACE_MAX_PACK_BYTES || 25_000_000);
28
31
  const SURFACE_MAX_FILES = Number(process.env.SKS_PACKAGE_SURFACE_MAX_FILES || 2500);
29
32
  function runNpmPack() {