sneakoscope 5.1.2 → 5.3.0

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 (58) hide show
  1. package/README.md +1 -1
  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/commands/doctor.js +32 -3
  7. package/dist/config/skills-manifest.json +58 -58
  8. package/dist/core/agents/agent-lifecycle.js +5 -3
  9. package/dist/core/agents/native-cli-worker.js +0 -1
  10. package/dist/core/codex-app/sks-menubar.js +462 -130
  11. package/dist/core/commands/basic-cli.js +2 -4
  12. package/dist/core/commands/menubar-command.js +2 -1
  13. package/dist/core/doctor/doctor-dirty-planner.js +51 -7
  14. package/dist/core/fsx.js +1 -1
  15. package/dist/core/naruto/naruto-real-worker-child.js +1 -1
  16. package/dist/core/update/update-migration-state.js +23 -3
  17. package/dist/core/version.js +1 -1
  18. package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
  19. package/dist/scripts/doctor-context7-mcp-repair-blackbox.js +16 -0
  20. package/dist/scripts/doctor-fix-production-blackbox.js +26 -0
  21. package/dist/scripts/doctor-startup-config-repair-blackbox.js +13 -0
  22. package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +14 -0
  23. package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
  24. package/dist/scripts/project-skill-dedupe-blackbox.js +36 -0
  25. package/dist/scripts/release-gate-script-parity-check.js +44 -16
  26. package/dist/scripts/release-metadata-1-19-check.js +1 -1
  27. package/dist/scripts/sks-menubar-install-check.js +57 -20
  28. package/dist/scripts/supabase-secret-preservation-blackbox.js +29 -0
  29. package/dist/scripts/update-preserves-supabase-keys-blackbox.js +27 -0
  30. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
  31. package/package.json +8 -65
  32. package/bench/tasks/t01-off-by-one/repo/package.json +0 -9
  33. package/bench/tasks/t01-off-by-one/repo/src/pagination.js +0 -5
  34. package/bench/tasks/t01-off-by-one/repo/test.js +0 -5
  35. package/bench/tasks/t01-off-by-one/task.json +0 -13
  36. package/bench/tasks/t02-signature-cochange/repo/package.json +0 -9
  37. package/bench/tasks/t02-signature-cochange/repo/src/admin.js +0 -5
  38. package/bench/tasks/t02-signature-cochange/repo/src/audit.js +0 -5
  39. package/bench/tasks/t02-signature-cochange/repo/src/labels.js +0 -3
  40. package/bench/tasks/t02-signature-cochange/repo/src/profile.js +0 -5
  41. package/bench/tasks/t02-signature-cochange/repo/test.js +0 -9
  42. package/bench/tasks/t02-signature-cochange/task.json +0 -12
  43. package/bench/tasks/t03-type-puzzle/repo/package.json +0 -9
  44. package/bench/tasks/t03-type-puzzle/repo/src/config.js +0 -4
  45. package/bench/tasks/t03-type-puzzle/repo/test.js +0 -6
  46. package/bench/tasks/t03-type-puzzle/task.json +0 -13
  47. package/bench/tasks/t04-refactor-preserve/repo/package.json +0 -9
  48. package/bench/tasks/t04-refactor-preserve/repo/src/cart.js +0 -8
  49. package/bench/tasks/t04-refactor-preserve/repo/test.js +0 -4
  50. package/bench/tasks/t04-refactor-preserve/task.json +0 -12
  51. package/bench/tasks/t05-performance/repo/package.json +0 -9
  52. package/bench/tasks/t05-performance/repo/src/pairs.js +0 -9
  53. package/bench/tasks/t05-performance/repo/test.js +0 -11
  54. package/bench/tasks/t05-performance/task.json +0 -12
  55. package/bench/tasks/t06-mistake-rule/repo/package.json +0 -9
  56. package/bench/tasks/t06-mistake-rule/repo/src/loader.js +0 -6
  57. package/bench/tasks/t06-mistake-rule/repo/test.js +0 -7
  58. package/bench/tasks/t06-mistake-rule/task.json +0 -12
@@ -1,12 +1,10 @@
1
1
  import path from 'node:path';
2
- import os from 'node:os';
3
- import fsp from 'node:fs/promises';
4
2
  import { spawnSync } from 'node:child_process';
5
3
  import { COMMANDS, LEGACY_COMMAND_ALIASES } from '../../cli/command-registry.js';
6
4
  import { flag } from '../../cli/args.js';
7
5
  import { printJson, sksTextLogo } from '../../cli/output.js';
8
6
  import { ui as cliUi } from '../../cli/cli-theme.js';
9
- import { PACKAGE_VERSION, ensureDir, exists, nowIso, projectRoot, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
7
+ import { PACKAGE_VERSION, ensureDir, exists, nowIso, projectRoot, readJson, sksRoot, tmpdir, writeJsonAtomic } from '../fsx.js';
10
8
  import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, USAGE_TOPICS, routePrompt, routeReasoning, reasoningInstruction } from '../routes.js';
11
9
  import { initProject, normalizeInstallScope, sksCommandPrefix } from '../init.js';
12
10
  import { buildFeatureRegistry, validateFeatureRegistry } from '../feature-registry.js';
@@ -283,7 +281,7 @@ export async function postinstallCommand(args = []) {
283
281
  export async function selftestCommand(args = []) {
284
282
  process.env.CI = 'true';
285
283
  const root = await projectRoot();
286
- const tmp = await fsp.mkdtemp(path.join(os.tmpdir(), 'sks-selftest-'));
284
+ const tmp = tmpdir('sks-selftest-');
287
285
  await ensureDir(tmp);
288
286
  const registry = await buildFeatureRegistry({ root });
289
287
  const coverage = validateFeatureRegistry(registry);
@@ -97,7 +97,8 @@ function printStatus(result) {
97
97
  console.log(`Running: ${result.running ? 'yes' : 'no'}`);
98
98
  console.log(`Launchd: ${result.launchd.ok ? result.launchd.state || 'present' : result.launchd.error || 'missing'}`);
99
99
  console.log(`Version: ${result.build_stamp?.package_version || 'unknown'} (package ${result.package_version})`);
100
- console.log(`Target: ${result.action_target.sks_entry || 'missing'} ${result.action_target.sks_entry_exists ? '(ok)' : '(missing)'}`);
100
+ console.log(`Target: ${result.action_target.sks_entry || 'runtime'} ${result.action_target.ok ? '(smoke ok)' : '(smoke failed)'}`);
101
+ console.log(`Codex sync:${result.codex_sync.bundle_id || 'disabled'} ${result.codex_sync.codex_running === null ? '' : result.codex_sync.codex_running ? '(running)' : '(not running)'}`);
101
102
  console.log(`Signature: ${result.signature.identifier || 'unknown'} ${result.signature.ok ? '(ok)' : '(check)'}`);
102
103
  if (result.warnings.length) {
103
104
  console.log('Warnings:');
@@ -1,8 +1,10 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
3
4
  import { MANAGED_ASSET_VERSION } from '../managed-assets/managed-assets-manifest.js';
4
5
  import { PACKAGE_VERSION, packageRoot, sha256 } from '../fsx.js';
5
6
  import { hashJson } from '../triwiki/triwiki-cache-key.js';
7
+ import { SKS_MENUBAR_LABEL, sksMenuBarPaths } from '../codex-app/sks-menubar.js';
6
8
  export const DOCTOR_DIRTY_PLAN_SCHEMA = 'sks.doctor-dirty-plan.v2';
7
9
  export function planDoctorDirtyRepair(root, phaseIds) {
8
10
  const phases = phaseIds.map((id) => {
@@ -10,25 +12,30 @@ export function planDoctorDirtyRepair(root, phaseIds) {
10
12
  const inputHash = phaseInputHash(root, id);
11
13
  const postcheckRequired = phaseRequiresPostcheck(id);
12
14
  const markerState = readMarker(marker);
15
+ const runtimeProbeFailed = runtimeProbeFailures(root, id);
13
16
  if (!markerState)
14
- return { id, status: 'dirty', reason: 'no_clean_marker', input_hash: inputHash, last_clean_proof_id: null, postcheck_required: postcheckRequired, postcheck_pending: false };
17
+ return { id, status: 'dirty', reason: 'no_clean_marker', input_hash: inputHash, last_clean_proof_id: null, postcheck_required: postcheckRequired, postcheck_pending: false, runtime_probe_failed: runtimeProbeFailed };
15
18
  if (markerState.input_hash !== inputHash) {
16
- return { id, status: 'dirty', reason: 'input_hash_changed', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false };
19
+ return { id, status: 'dirty', reason: 'input_hash_changed', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false, runtime_probe_failed: runtimeProbeFailed };
17
20
  }
18
21
  if (markerState.proof_id && !proofExists(root, markerState.proof_id)) {
19
- return { id, status: 'dirty', reason: 'clean_proof_missing', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false };
22
+ return { id, status: 'dirty', reason: 'clean_proof_missing', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false, runtime_probe_failed: runtimeProbeFailed };
20
23
  }
21
24
  if (!markerState.repair_applied) {
22
- return { id, status: 'dirty', reason: 'repair_not_applied', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false };
25
+ return { id, status: 'dirty', reason: 'repair_not_applied', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false, runtime_probe_failed: runtimeProbeFailed };
26
+ }
27
+ if (runtimeProbeFailed.length) {
28
+ return { id, status: 'dirty', reason: `runtime_probe_failed:${runtimeProbeFailed.join(',')}`, input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired, postcheck_pending: false, runtime_probe_failed: runtimeProbeFailed };
23
29
  }
24
30
  return {
25
31
  id,
26
- status: 'clean',
32
+ status: markerState.postcheck_pending ? 'dirty' : 'clean',
27
33
  reason: postcheckRequired && markerState.postcheck_pending ? 'matching_repair_marker_postcheck_pending' : 'matching_clean_proof',
28
34
  input_hash: inputHash,
29
35
  last_clean_proof_id: markerState.proof_id,
30
36
  postcheck_required: postcheckRequired,
31
- postcheck_pending: postcheckRequired && markerState.postcheck_pending
37
+ postcheck_pending: postcheckRequired && markerState.postcheck_pending,
38
+ runtime_probe_failed: []
32
39
  };
33
40
  });
34
41
  const plan = {
@@ -37,6 +44,7 @@ export function planDoctorDirtyRepair(root, phaseIds) {
37
44
  phases,
38
45
  dirty_count: phases.filter((phase) => phase.status === 'dirty').length,
39
46
  clean_count: phases.filter((phase) => phase.status === 'clean').length,
47
+ runtime_probe_failed: phases.flatMap((phase) => phase.runtime_probe_failed.map((failure) => `${phase.id}:${failure}`)),
40
48
  semantic_dirty_plan_path: dirtyPlanPath(root)
41
49
  };
42
50
  writeDirtyPlan(root, plan);
@@ -236,7 +244,43 @@ function proofExists(root, proofId) {
236
244
  }
237
245
  }
238
246
  function phaseRequiresPostcheck(id) {
239
- return /zellij|context7|startup|supabase|native|secret/i.test(id);
247
+ return /zellij|context7|startup|supabase|native|secret|menubar/i.test(id);
248
+ }
249
+ function runtimeProbeFailures(root, id) {
250
+ if (!id.includes('menubar') || process.platform !== 'darwin')
251
+ return [];
252
+ const failures = [];
253
+ const paths = sksMenuBarPaths(process.env.HOME, root);
254
+ if (!fs.existsSync(paths.executable_path))
255
+ failures.push('menubar_app_missing');
256
+ if (!fs.existsSync(paths.action_script_path))
257
+ failures.push('action_script_missing');
258
+ else {
259
+ try {
260
+ fs.accessSync(paths.action_script_path, fs.constants.X_OK);
261
+ }
262
+ catch {
263
+ failures.push('action_script_not_executable');
264
+ }
265
+ }
266
+ const launchctl = process.env.SKS_MENUBAR_LAUNCHCTL || '/bin/launchctl';
267
+ if (!fs.existsSync(launchctl)) {
268
+ failures.push('launchctl_missing');
269
+ return failures;
270
+ }
271
+ const uid = typeof process.getuid === 'function' ? process.getuid() : null;
272
+ const service = `${uid === null ? 'gui' : `gui/${uid}`}/${SKS_MENUBAR_LABEL}`;
273
+ const result = spawnSync(launchctl, ['print', service], {
274
+ encoding: 'utf8',
275
+ timeout: 500,
276
+ maxBuffer: 32 * 1024
277
+ });
278
+ const text = `${result.stdout || ''}\n${result.stderr || ''}`;
279
+ if (result.error)
280
+ failures.push(`launchctl_probe_error:${result.error.message}`);
281
+ else if (result.status !== 0 || !(/\bstate = running\b|\bpid = \d+\b/.test(text)))
282
+ failures.push('launchd_not_running');
283
+ return failures;
240
284
  }
241
285
  function writeDirtyPlan(root, plan) {
242
286
  const file = dirtyPlanPath(root);
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 = '5.1.2';
8
+ export const PACKAGE_VERSION = '5.3.0';
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() {
@@ -136,7 +136,7 @@ function startDeadmanHeartbeat(input) {
136
136
  schema: 'sks.naruto-actual-worker-heartbeat.v1',
137
137
  ts: nowIso(),
138
138
  item_id: input.intake.item.id,
139
- status: 'blocked',
139
+ status: 'timed_out',
140
140
  elapsed_ms: elapsedMs,
141
141
  progress: null
142
142
  }, 2 * 1024 * 1024).catch(() => undefined);
@@ -7,6 +7,7 @@ import { enforceRetention } from '../retention.js';
7
7
  import { COMMANDS } from '../../cli/command-registry.js';
8
8
  import { reconcileSkills } from '../init/skills.js';
9
9
  import { codexHookTrustDoctor } from '../codex-hooks/codex-hook-trust-doctor.js';
10
+ import { writeCodexConfigGuarded } from '../codex/codex-config-guard.js';
10
11
  export const UPDATE_MIGRATION_SCHEMA = 'sks.project-migration-receipt.v2';
11
12
  export const INSTALLATION_EPOCH_SCHEMA = 'sks.installation-epoch.v1';
12
13
  export function installationEpochPath() {
@@ -367,9 +368,28 @@ async function runConfigFastModeNormalizeStage() {
367
368
  });
368
369
  if (!hasTopLevel)
369
370
  next = insertTopLevelTomlKey(next, `default_profile = ${JSON.stringify(misplaced)}`);
370
- if (next !== text)
371
- await writeTextAtomic(configPath, `${next.trim()}\n`);
372
- return { ok: true, status: 'ok', actions: ['normalized_fastmode_default_profile'], blockers: [], warnings: [], detail: { config_path: configPath, default_profile: misplaced } };
371
+ let guardResult = null;
372
+ if (next !== text) {
373
+ guardResult = await writeCodexConfigGuarded({
374
+ configPath,
375
+ before: text,
376
+ mutate: () => `${next.trim()}\n`,
377
+ cause: 'project-update-fastmode-default-profile',
378
+ backupTag: 'project-update-fastmode-default-profile',
379
+ preserveFastUiKeys: true
380
+ });
381
+ if (!guardResult.ok) {
382
+ return {
383
+ ok: false,
384
+ status: 'failed',
385
+ actions: ['normalize_fastmode_default_profile_blocked'],
386
+ blockers: [`codex_config_guard:${guardResult.status}`],
387
+ warnings: [],
388
+ detail: { config_path: configPath, default_profile: misplaced, guard: guardResult }
389
+ };
390
+ }
391
+ }
392
+ return { ok: true, status: 'ok', actions: ['normalized_fastmode_default_profile'], blockers: [], warnings: [], detail: { config_path: configPath, default_profile: misplaced, guard: guardResult } };
373
393
  }
374
394
  async function runHookTrustRefreshStage(root) {
375
395
  const result = await codexHookTrustDoctor(root, { fix: true, managed: true, actual: true });
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '5.1.2';
1
+ export const PACKAGE_VERSION = '5.3.0';
2
2
  //# sourceMappingURL=version.js.map
@@ -272,7 +272,7 @@ function normalizeStatus(value) {
272
272
  const text = String(value || '').toLowerCase();
273
273
  if (text === 'closed' || text === 'done' || text === 'passed')
274
274
  return 'completed';
275
- if (text === 'blocked' || text === 'error')
275
+ if (text === 'blocked' || text === 'error' || text === 'timed_out')
276
276
  return 'failed';
277
277
  if (text === 'coding')
278
278
  return 'running';
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check-lib.js';
5
+ import { repairContext7Mcp } from '../core/doctor/context7-mcp-repair.js';
6
+ const root = await makeTempRoot('sks-context7-blackbox-');
7
+ const configPath = path.join(root, '.codex', 'config.toml');
8
+ await writeText(configPath, '[mcp_servers.context7]\ncommand = "npx"\n');
9
+ const repaired = await repairContext7Mcp({ root, apply: true });
10
+ const text = await fs.readFile(configPath, 'utf8');
11
+ assertGate(repaired.ok === true && text.includes('https://mcp.context7.com/mcp'), 'Context7 blackbox must write remote URL', { repaired, text });
12
+ await writeText(configPath, '[mcp_servers.context7]\ndisabled = true\ncommand = "npx"\n');
13
+ const disabled = await repairContext7Mcp({ root, apply: true });
14
+ assertGate(disabled.after_transport === 'disabled' && disabled.repaired === false && disabled.disabled_preserved === true, 'Context7 blackbox must preserve explicitly disabled server', disabled);
15
+ emitGate('doctor:context7-mcp-repair-blackbox');
16
+ //# sourceMappingURL=doctor-context7-mcp-repair-blackbox.js.map
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check-lib.js';
4
+ import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
5
+ import { repairContext7Mcp } from '../core/doctor/context7-mcp-repair.js';
6
+ import { repairSupabaseMcp } from '../core/doctor/supabase-mcp-repair.js';
7
+ import { runDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
8
+ import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
9
+ const root = await makeTempRoot('sks-doctor-production-');
10
+ await writeText(path.join(root, '.codex', 'config.toml'), '[mcp_servers.context7]\ncommand = "npx"\n\n[mcp_servers.supabase]\nurl = "https://supabase.example/mcp"\nread_only = true\n\n[agents.analysis_scout]\nconfig_file = ".codex/agents/stale.toml"\nmessage_role_prefix = "legacy"\n');
11
+ const startup = await repairCodexStartupConfig({ root, apply: true });
12
+ const context7 = await repairContext7Mcp({ root, apply: true });
13
+ const supabase = await repairSupabaseMcp({ root, apply: true });
14
+ const tx = await runDoctorFixTransaction({ root, phases: [
15
+ { id: 'preflight', run: async () => ({ id: 'preflight', ok: true }) },
16
+ { id: 'codex_startup_config_repair', run: async () => ({ id: 'codex_startup_config_repair', ok: startup.ok, repaired: true, blockers: startup.blockers, rollback_evidence: 'fixture_config_backup' }) },
17
+ { id: 'context7_mcp_repair', run: async () => ({ id: 'context7_mcp_repair', ok: context7.ok, repaired: context7.repaired, blockers: context7.blockers, rollback_evidence: 'fixture_context7_backup' }) },
18
+ { id: 'supabase_mcp_repair', required_for_ready: false, run: async () => ({ id: 'supabase_mcp_repair', ok: supabase.ok, manual_required: supabase.manual_required, required_for_ready: false, blockers: supabase.blockers, warnings: supabase.warnings, rollback_evidence: 'optional_supabase_fixture' }) },
19
+ { id: 'postcheck', run: async () => ({ id: 'postcheck', ok: true }) }
20
+ ] });
21
+ const postcheck = doctorRepairPostcheck(tx);
22
+ assertGate(tx.schema === 'sks.doctor-fix-transaction.v2' && tx.raw_secret_values_recorded === false, 'doctor transaction must write production schema without raw secrets', tx);
23
+ assertGate(tx.phases.every((phase) => typeof phase.duration_ms === 'number' && phase.started_at && phase.completed_at), 'doctor transaction phases must include runtime evidence', tx);
24
+ assertGate(postcheck.ok === true, 'doctor repair postcheck must pass when only manual-required optional phases remain', postcheck);
25
+ emitGate('doctor:fix-production-blackbox', { phases: tx.phases.length });
26
+ //# sourceMappingURL=doctor-fix-production-blackbox.js.map
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check-lib.js';
5
+ import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
6
+ const root = await makeTempRoot('sks-startup-config-blackbox-');
7
+ await writeText(path.join(root, '.codex', 'config.toml'), '[agents.analysis_scout]\nconfig_file = ".codex/agents/stale.toml"\nmessage_role_prefix = "legacy"\n');
8
+ const report = await repairCodexStartupConfig({ root, apply: true });
9
+ const text = await fs.readFile(path.join(root, '.codex', 'config.toml'), 'utf8');
10
+ assertGate(report.ok === true, 'startup config blackbox must pass postcheck', report);
11
+ assertGate(!text.includes('message_role_prefix') && /config_file = "\//.test(text), 'startup config blackbox must rewrite to absolute config paths and remove unsupported fields', { text });
12
+ emitGate('doctor:startup-config-repair-blackbox');
13
+ //# sourceMappingURL=doctor-startup-config-repair-blackbox.js.map
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check-lib.js';
4
+ import { repairSupabaseMcp } from '../core/doctor/supabase-mcp-repair.js';
5
+ const root = await makeTempRoot('sks-supabase-mcp-blackbox-');
6
+ delete process.env.SUPABASE_ACCESS_TOKEN;
7
+ await writeText(path.join(root, '.codex', 'config.toml'), '[mcp_servers.supabase]\nurl = "https://supabase.example/mcp"\nSUPABASE_ACCESS_TOKEN = "env:SUPABASE_ACCESS_TOKEN"\n');
8
+ const unsafe = await repairSupabaseMcp({ root, apply: true });
9
+ assertGate(unsafe.ok === true && unsafe.manual_required === true && unsafe.read_only_migrated === true && unsafe.ready_blocking === false, 'Supabase unsafe write access must be migrated to read-only without passing silently as write-ready', unsafe);
10
+ await writeText(path.join(root, '.codex', 'config.toml'), '[mcp_servers.supabase_sauron]\ndisabled = true\n');
11
+ const optional = await repairSupabaseMcp({ root, apply: true });
12
+ assertGate(optional.ok === true && optional.disabled === true && optional.disabled_preserved === true, 'optional disabled Supabase config must be preserved', optional);
13
+ emitGate('doctor:supabase-mcp-repair-blackbox');
14
+ //# sourceMappingURL=doctor-supabase-mcp-repair-blackbox.js.map
@@ -49,7 +49,32 @@ const artifactEvidence = report.worker_lifecycle.every((row) => row.worker_artif
49
49
  && fs.existsSync(path.join(row.worker_artifact_dir, 'worker-heartbeat.jsonl'))
50
50
  && fs.existsSync(path.join(row.worker_artifact_dir, 'worker-result.json'))
51
51
  && fs.existsSync(path.join(row.worker_artifact_dir, 'codex-control', 'codex-control-proof.json')));
52
+ const timeoutReport = await runNarutoRealActivePool({
53
+ graph: buildNarutoWorkGraph({ requestedClones: 1, totalWorkItems: 1, writeCapable: false, maxActiveWorkers: 1 }),
54
+ governor: { ...target, safe_active_workers: 1, safe_zellij_visible_panes: 0 },
55
+ hardTimeoutMs: 1,
56
+ spawnWorker: async (item, placement) => ({
57
+ id: item.id,
58
+ item,
59
+ placement,
60
+ started_at: Date.now() - 1000,
61
+ pid: null,
62
+ worker_artifact_dir: path.join(tempRoot, 'timeout-worker'),
63
+ heartbeat_path: path.join(tempRoot, 'timeout-worker', 'worker-heartbeat.jsonl'),
64
+ exit: new Promise(() => undefined)
65
+ }),
66
+ collectWorker: async () => {
67
+ throw new Error('timed out worker should be force-collected before collectWorker');
68
+ },
69
+ enqueueVerification: async () => undefined,
70
+ updateDashboard: async () => undefined
71
+ });
72
+ const timeoutEvidence = timeoutReport.completed_count === 1
73
+ && timeoutReport.failed_count === 1
74
+ && timeoutReport.blockers.includes('naruto_worker_hard_timeout')
75
+ && timeoutReport.worker_lifecycle.some((row) => row.ok === false && row.completed_at !== null);
52
76
  const ok = report.ok && spawned === graph.total_work_items && collected === graph.total_work_items && report.max_observed_active_workers >= target.safe_active_workers && report.active_pool_utilization >= 0.8 && processEvidence && artifactEvidence;
53
77
  assertGate(ok, 'Naruto real active pool runtime must include actual child process, heartbeat, and result evidence', { report, spawned, collected, processEvidence, artifactEvidence, tempRoot });
54
- emitGate('naruto:real-active-pool-runtime', report);
78
+ assertGate(timeoutEvidence, 'Naruto real active pool must force-collect hung workers as timed-out results', { timeoutReport });
79
+ emitGate('naruto:real-active-pool-runtime', { ...report, timeout_force_collect_checked: true });
55
80
  //# sourceMappingURL=naruto-real-active-pool-runtime-check.js.map
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
+ import { assertGate, emitGate, makeTempRoot, writeManagedCoreSkill, writeUserSkill } from './sks-3-1-8-check-lib.js';
5
+ import { dedupeProjectSkills } from '../core/codex-native/project-skill-dedupe.js';
6
+ import { syncCodexSksSkills, withSkillSyncLock } from '../core/codex-app/codex-skill-sync.js';
7
+ import { buildSkillRegistryLedger } from '../core/codex-native/skill-registry-ledger.js';
8
+ const root = await makeTempRoot('sks-skill-dedupe-blackbox-');
9
+ process.env.CODEX_HOME = path.join(root, 'codex-home');
10
+ await writeManagedCoreSkill(root, '.agents/skills', 'loop');
11
+ await writeManagedCoreSkill(root, '.codex/skills', 'loop');
12
+ const scenarioA = await dedupeProjectSkills({ root, fix: true, yes: true });
13
+ await writeUserSkill(root, '.agents/skills', 'user-loop', 'loop');
14
+ await writeManagedCoreSkill(root, '.codex/skills', 'loop');
15
+ const scenarioB = await dedupeProjectSkills({ root, fix: true, yes: true });
16
+ await writeUserSkill(root, '.agents/skills', 'user-loop-a', 'Loop');
17
+ await writeUserSkill(root, '.codex/skills', 'user-loop-b', 'loop');
18
+ const scenarioC = await dedupeProjectSkills({ root, fix: true, yes: false });
19
+ const scenarioD = await dedupeProjectSkills({ root, fix: true, yes: true, quarantineUserDuplicates: true });
20
+ const staleRoot = await makeTempRoot('sks-skill-stale-lock-');
21
+ const lockPath = path.join(staleRoot, '.sneakoscope', 'locks', 'skill-sync.lock');
22
+ await fs.mkdir(lockPath, { recursive: true });
23
+ await fs.writeFile(path.join(lockPath, 'owner.json'), JSON.stringify({ schema: 'sks.skill-sync-lock.v1', pid: 999999, acquired_at: '1970-01-01T00:00:00.000Z', stale_after_ms: 1 }), 'utf8');
24
+ await withSkillSyncLock(staleRoot, async () => undefined);
25
+ const concurrentRoot = await makeTempRoot('sks-skill-concurrent-');
26
+ process.env.CODEX_HOME = path.join(concurrentRoot, 'codex-home');
27
+ const skillsRoot = path.join(process.env.CODEX_HOME, 'skills');
28
+ await Promise.all(Array.from({ length: 20 }, () => syncCodexSksSkills({ root: concurrentRoot, apply: true, skillsRoot })));
29
+ const concurrentLedger = await buildSkillRegistryLedger({ root: concurrentRoot });
30
+ assertGate(scenarioA.actions.some((action) => action.action === 'quarantined'), 'scenario A must quarantine SKS-managed duplicate', scenarioA);
31
+ assertGate(scenarioB.actions.some((action) => action.reason.includes('user-authored skill preserved')), 'scenario B must preserve user-authored skill', scenarioB);
32
+ assertGate(scenarioC.blockers.some((blocker) => blocker.includes('user_duplicate_requires_confirmation')), 'scenario C must not auto-delete user-authored duplicates', scenarioC);
33
+ assertGate(scenarioD.active_unique_by_canonical_name === true, 'scenario D must quarantine confirmed user-authored duplicate', scenarioD);
34
+ assertGate(concurrentLedger.active_unique_by_canonical_name === true, 'scenario F must keep exactly one active canonical name after concurrent sync', concurrentLedger);
35
+ emitGate('skill:dedupe-blackbox');
36
+ //# sourceMappingURL=project-skill-dedupe-blackbox.js.map
@@ -4,10 +4,6 @@ import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { assertGate, emitGate, readJson, root } from './sks-1-18-gate-lib.js';
6
6
  import { writeJsonAtomic } from '../core/fsx.js';
7
- import { REQUIRED_3112_REAL_CHECK_IDS, REQUIRED_3112_RELEASE_IDS } from './release-3112-required-gates.js';
8
- import { REQUIRED_3113_REAL_CHECK_IDS, REQUIRED_3113_RELEASE_IDS } from './release-3113-required-gates.js';
9
- import { REQUIRED_4000_REAL_CHECK_IDS, REQUIRED_4000_RELEASE_IDS } from './release-4000-required-gates.js';
10
- import { REQUIRED_4002_REAL_CHECK_IDS, REQUIRED_4002_RELEASE_IDS } from './release-4002-required-gates.js';
11
7
  export const REQUIRED_3110_RELEASE_IDS = [
12
8
  'core-skill:manifest',
13
9
  'core-skill:immutable-sync',
@@ -63,36 +59,52 @@ export function buildReleaseGateScriptParityReport() {
63
59
  const gates = Array.isArray(manifest.gates) ? manifest.gates : [];
64
60
  const gateById = new Map(gates.map((gate) => [gate.id, gate]));
65
61
  const releaseGateIds = gates.filter((gate) => gate.preset?.includes('release')).map((gate) => gate.id);
66
- const requiredReleaseIds = [...new Set([...REQUIRED_3110_RELEASE_IDS, ...REQUIRED_3112_RELEASE_IDS, ...REQUIRED_3113_RELEASE_IDS, ...REQUIRED_4000_RELEASE_IDS, ...REQUIRED_4002_RELEASE_IDS, 'codex:0140-real-probes'])];
67
- const requiredRealCheckIds = [...new Set([...REQUIRED_3112_REAL_CHECK_IDS, ...REQUIRED_3113_REAL_CHECK_IDS, ...REQUIRED_4000_REAL_CHECK_IDS, ...REQUIRED_4002_REAL_CHECK_IDS])];
62
+ const requiredReleaseIds = releaseGateIds;
63
+ const requiredRealCheckIds = gates.filter((gate) => gate.preset?.includes('real-check')).map((gate) => gate.id);
68
64
  const requiredAllIds = [...new Set([...requiredReleaseIds, ...requiredRealCheckIds])];
69
- const missingScripts = [...new Set([
70
- ...releaseGateIds.filter((id) => !scripts[id]),
71
- ...requiredAllIds.filter((id) => !scripts[id])
72
- ])].sort();
65
+ const requiredEntryScripts = [
66
+ 'build',
67
+ 'build:incremental',
68
+ 'typecheck',
69
+ 'release:check',
70
+ 'release:metadata',
71
+ 'release:check:affected',
72
+ 'release:check:fast',
73
+ 'release:check:confidence',
74
+ 'release:check:full',
75
+ 'prepublishOnly',
76
+ 'publish:prep-ignore-scripts',
77
+ 'publish:ignore-scripts',
78
+ 'gates:run',
79
+ 'policy:gate-audit'
80
+ ];
81
+ const missingEntryScripts = requiredEntryScripts.filter((id) => !scripts[id]).sort();
73
82
  const missingGates = requiredAllIds.filter((id) => !gateById.has(id)).sort();
74
83
  const missingReleasePreset = requiredReleaseIds.filter((id) => !gateById.get(id)?.preset?.includes('release')).sort();
75
84
  const missingRealCheckPreset = requiredRealCheckIds.filter((id) => !gateById.get(id)?.preset?.includes('real-check')).sort();
76
85
  const wrongCommands = requiredAllIds
77
- .map((id) => ({ id, expected: `npm run ${id} --silent`, actual: gateById.get(id)?.command || '' }))
78
- .filter((row) => row.actual !== row.expected);
86
+ .map((id) => {
87
+ const actual = gateById.get(id)?.command || '';
88
+ return { id, actual, reason: directManifestCommandIssue(actual) };
89
+ })
90
+ .filter((row) => row.reason);
79
91
  const missingSourceTargets = requiredAllIds
80
- .map((id) => ({ id, source: sourceTargetForScript(scripts[id]) }))
92
+ .map((id) => ({ id, source: sourceTargetForScript(gateById.get(id)?.command) }))
81
93
  .filter((row) => row.source && !fs.existsSync(path.join(root, row.source)))
82
94
  .map((row) => row.source)
83
95
  .sort();
84
96
  const missingDistTargets = requiredAllIds
85
- .map((id) => ({ id, dist: distTargetForScript(scripts[id]) }))
97
+ .map((id) => ({ id, dist: distTargetForScript(gateById.get(id)?.command) }))
86
98
  .filter((row) => row.dist && fs.existsSync(path.join(root, 'dist')) && !fs.existsSync(path.join(root, row.dist)))
87
99
  .map((row) => row.dist)
88
100
  .sort();
89
101
  return {
90
102
  schema: 'sks.release-gate-script-parity.v1',
91
- ok: missingScripts.length === 0 && missingGates.length === 0 && missingReleasePreset.length === 0 && missingRealCheckPreset.length === 0 && wrongCommands.length === 0 && missingSourceTargets.length === 0 && missingDistTargets.length === 0,
103
+ ok: missingEntryScripts.length === 0 && missingGates.length === 0 && missingReleasePreset.length === 0 && missingRealCheckPreset.length === 0 && wrongCommands.length === 0 && missingSourceTargets.length === 0 && missingDistTargets.length === 0,
92
104
  release_gate_count: releaseGateIds.length,
93
105
  package_script_count: Object.keys(scripts).length,
94
106
  checked_required_ids: requiredAllIds.length,
95
- missing_scripts: missingScripts,
107
+ missing_entry_scripts: missingEntryScripts,
96
108
  missing_gates: missingGates,
97
109
  missing_release_preset: missingReleasePreset,
98
110
  missing_real_check_preset: missingRealCheckPreset,
@@ -101,6 +113,22 @@ export function buildReleaseGateScriptParityReport() {
101
113
  missing_dist_targets: missingDistTargets
102
114
  };
103
115
  }
116
+ function directManifestCommandIssue(command) {
117
+ const text = String(command || '');
118
+ if (!text)
119
+ return 'missing_command';
120
+ if (/\bnpm\s+run\b/.test(text))
121
+ return 'gate_command_must_not_require_package_script_alias';
122
+ const segments = text.split(/\s+(?:&&|\|\|)\s+/).map((part) => part.trim()).filter(Boolean);
123
+ const invalid = segments.find((part) => !(/^node\s+\.\/dist\/scripts\/[^\s]+\.js\b/.test(part)
124
+ || /^node\s+\.\/dist\/bin\/sks\.js\b/.test(part)
125
+ || /^node\s+--test\b/.test(part)
126
+ || /^tsc\s+-p\s+tsconfig\.json\s+--noEmit$/.test(part)));
127
+ if (invalid) {
128
+ return 'gate_command_must_execute_dist_script_or_typecheck_directly';
129
+ }
130
+ return '';
131
+ }
104
132
  function distTargetForScript(script) {
105
133
  const match = String(script || '').match(/node\s+\.\/dist\/scripts\/([^\s]+\.js)/);
106
134
  return match ? `dist/scripts/${match[1]}` : null;
@@ -372,7 +372,7 @@ assertGate(releaseManifest?.schema === 'sks.release-gates.v2', 'release gate man
372
372
  assertGate(harnessManifest?.schema === 'sks.infra-harness-gates.v1', 'infra harness manifest schema mismatch', { schema: harnessManifest?.schema || null });
373
373
  assertGate(releaseGates.length > 0 && releaseGates.length <= 200, 'release v2 manifest must include 1..200 release gates', { release_gates: releaseGates.length });
374
374
  assertGate(harnessGates.length > 0, 'infra harness manifest must include harness gates', { harness_gates: harnessGates.length });
375
- const PACKAGE_SCRIPT_BUDGET = 150;
375
+ const PACKAGE_SCRIPT_BUDGET = 100;
376
376
  assertGate(Object.keys(pkg.scripts || {}).length <= PACKAGE_SCRIPT_BUDGET, 'package script budget exceeded', { script_count: Object.keys(pkg.scripts || {}).length, limit: PACKAGE_SCRIPT_BUDGET });
377
377
  for (const script of requiredPackageScripts)
378
378
  assertGate(Boolean(pkg.scripts?.[script]), `missing package script: ${script}`);