sneakoscope 6.0.2 → 6.1.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 (242) hide show
  1. package/README.md +5 -3
  2. package/config/codex-releases/{rust-v0.142.0.json → rust-v0.144.1.json} +6 -6
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/cli/install-helpers.js +255 -440
  6. package/dist/cli/router.js +53 -4
  7. package/dist/commands/codex-app.js +8 -3
  8. package/dist/commands/codex-lb.js +169 -40
  9. package/dist/commands/codex.js +6 -5
  10. package/dist/commands/doctor.js +60 -26
  11. package/dist/commands/versioning.js +7 -0
  12. package/dist/config/skills-manifest.json +57 -57
  13. package/dist/core/agents/agent-central-ledger.js +9 -0
  14. package/dist/core/agents/agent-cleanup-executor.js +334 -41
  15. package/dist/core/agents/agent-cleanup.js +20 -1
  16. package/dist/core/agents/agent-command-surface.js +2 -1
  17. package/dist/core/agents/agent-effort-policy.js +46 -33
  18. package/dist/core/agents/agent-janitor.js +9 -10
  19. package/dist/core/agents/agent-namespace-safety.js +56 -0
  20. package/dist/core/agents/agent-orchestrator.js +26 -4
  21. package/dist/core/agents/agent-output-validator.js +1 -1
  22. package/dist/core/agents/agent-proof-evidence.js +16 -2
  23. package/dist/core/agents/agent-role-config.js +3 -6
  24. package/dist/core/agents/agent-roster.js +2 -2
  25. package/dist/core/agents/agent-runner-codex-exec.js +2 -0
  26. package/dist/core/agents/agent-runner-process.js +1 -0
  27. package/dist/core/agents/fast-mode-policy.js +3 -5
  28. package/dist/core/agents/native-cli-session-swarm.js +18 -1
  29. package/dist/core/agents/native-worker-backend-router.js +180 -108
  30. package/dist/core/agents/parallel-write-fixture.js +15 -10
  31. package/dist/core/agents/runtime-proof-summary.js +21 -3
  32. package/dist/core/agents/work-partition/repo-inventory.js +68 -7
  33. package/dist/core/code-structure.js +5 -4
  34. package/dist/core/codex/codex-config-guard.js +31 -9
  35. package/dist/core/codex-app/codex-app-fast-ui-repair.js +51 -20
  36. package/dist/core/codex-app/codex-app-restart.js +38 -8
  37. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +11 -3
  38. package/dist/core/codex-app/sks-menubar.js +100 -40
  39. package/dist/core/codex-app.js +60 -17
  40. package/dist/core/codex-compat/codex-hook-warning-detector.js +10 -1
  41. package/dist/core/codex-compat/codex-release-manifest.js +6 -6
  42. package/dist/core/codex-control/codex-0139-doctor-real-probe.js +7 -1
  43. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +14 -8
  44. package/dist/core/codex-control/codex-0139-probe-runner.js +103 -28
  45. package/dist/core/codex-control/codex-0139-real-probe-summary.js +7 -3
  46. package/dist/core/codex-control/codex-0139-real-probes.js +21 -6
  47. package/dist/core/codex-control/codex-0139-sandbox-real-probe.js +39 -13
  48. package/dist/core/codex-control/codex-0139-web-search-probe.js +11 -8
  49. package/dist/core/codex-control/{codex-0142-capability.js → codex-0144-capability.js} +22 -22
  50. package/dist/core/codex-control/codex-0144-collab-agent-real-probe.js +63 -0
  51. package/dist/core/codex-control/codex-model-metadata.js +8 -4
  52. package/dist/core/codex-control/codex-multi-agent-event-normalizer.js +5 -1
  53. package/dist/core/codex-control/codex-reliability-shield.js +4 -1
  54. package/dist/core/codex-control/codex-sdk-adapter.js +53 -6
  55. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  56. package/dist/core/codex-control/codex-sdk-sandbox-policy.js +6 -3
  57. package/dist/core/codex-lb/codex-lb-env.js +107 -0
  58. package/dist/core/codex-lb/codex-lb-tool-catalog.js +478 -0
  59. package/dist/core/codex-native/codex-native-feature-broker.js +22 -22
  60. package/dist/core/codex-native/codex-native-feature-matrix.js +2 -2
  61. package/dist/core/codex-native/native-capability-postcheck.js +16 -8
  62. package/dist/core/codex-native/native-capability-repair-matrix.js +76 -27
  63. package/dist/core/codex-plugins/codex-plugin-json.js +67 -21
  64. package/dist/core/codex-plugins/codex-plugin-repair.js +118 -0
  65. package/dist/core/codex-runtime/codex-desktop-config-policy.js +306 -0
  66. package/dist/core/commands/agent-command.js +19 -0
  67. package/dist/core/commands/basic-cli.js +45 -24
  68. package/dist/core/commands/gc-command.js +27 -4
  69. package/dist/core/commands/image-ux-review-command.js +55 -24
  70. package/dist/core/commands/mad-sks-command.js +152 -132
  71. package/dist/core/commands/naruto-command.js +41 -10
  72. package/dist/core/commands/pipeline-command.js +1 -0
  73. package/dist/core/commands/wiki-command.js +179 -83
  74. package/dist/core/computer-use-live-evidence.js +166 -3
  75. package/dist/core/computer-use-status.js +5 -2
  76. package/dist/core/daemon/sksd-hook-daemon-entrypoint.js +2 -2
  77. package/dist/core/daemon/sksd-hook-daemon.js +171 -28
  78. package/dist/core/daemon/sksd-hook-dispatch.js +2 -2
  79. package/dist/core/dfix/verification-selector.js +6 -4
  80. package/dist/core/doctor/browser-use-repair.js +54 -13
  81. package/dist/core/doctor/codex-0139-doctor.js +3 -2
  82. package/dist/core/doctor/computer-use-repair.js +28 -39
  83. package/dist/core/doctor/doctor-codex-startup-repair.js +19 -9
  84. package/dist/core/doctor/doctor-context7-repair.js +1 -1
  85. package/dist/core/doctor/doctor-dirty-planner.js +1 -1
  86. package/dist/core/doctor/doctor-native-capability-repair.js +74 -3
  87. package/dist/core/doctor/imagegen-repair.js +47 -12
  88. package/dist/core/doctor/legacy-global-hook-cleanup.js +179 -0
  89. package/dist/core/feature-registry.js +25 -31
  90. package/dist/core/fsx.js +54 -6
  91. package/dist/core/harness-conflicts.js +1 -1
  92. package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +5 -4
  93. package/dist/core/hooks-runtime/hook-invocation-dedupe.js +147 -0
  94. package/dist/core/hooks-runtime/hook-io.js +6 -0
  95. package/dist/core/hooks-runtime.js +17 -2
  96. package/dist/core/image/image-artifact-path-contract.js +8 -6
  97. package/dist/core/image/image-artifact-registry.js +5 -1
  98. package/dist/core/image-ux-review/imagegen-adapter.js +5 -2
  99. package/dist/core/imagegen/imagegen-capability.js +10 -13
  100. package/dist/core/imagegen/require-imagegen.js +37 -11
  101. package/dist/core/managed-assets/managed-assets-manifest.js +1 -5
  102. package/dist/core/naruto/naruto-work-graph.js +70 -4
  103. package/dist/core/naruto/naruto-write-e2e.js +3 -4
  104. package/dist/core/ops/reporting.js +3 -0
  105. package/dist/core/pipeline-internals/runtime-core.js +12 -7
  106. package/dist/core/pipeline-internals/runtime-gates.js +17 -1
  107. package/dist/core/ppt/style-tokens.js +1 -1
  108. package/dist/core/ppt-review/index.js +1 -1
  109. package/dist/core/ppt-review/slide-exporter.js +23 -17
  110. package/dist/core/ppt-review/slide-imagegen-review.js +1 -1
  111. package/dist/core/ppt-review/slide-issue-extraction.js +4 -2
  112. package/dist/core/product-design-plugin.js +2 -2
  113. package/dist/core/proof/route-finalizer.js +1 -1
  114. package/dist/core/proof-field.js +3 -3
  115. package/dist/core/provider/model-router.js +68 -3
  116. package/dist/core/release/gate-manifest.js +18 -12
  117. package/dist/core/release/gate-pack-manifest.js +1 -1
  118. package/dist/core/release/package-size-budget.js +2 -0
  119. package/dist/core/release/release-gate-affected-selector.js +1 -1
  120. package/dist/core/release/release-gate-dag.js +43 -3
  121. package/dist/core/release/release-gate-hermetic-env.js +28 -17
  122. package/dist/core/release/release-real-contract.js +416 -0
  123. package/dist/core/retention/retention-budget.js +25 -6
  124. package/dist/core/retention.js +980 -101
  125. package/dist/core/routes/constants.js +1 -1
  126. package/dist/core/routes/evidence.js +4 -4
  127. package/dist/core/routes/model-mode-router.js +3 -0
  128. package/dist/core/routes/ppt-policy.js +1 -1
  129. package/dist/core/routes.js +11 -12
  130. package/dist/core/secret-redaction.js +4 -0
  131. package/dist/core/triwiki/code-pack.js +8 -2
  132. package/dist/core/triwiki/triwiki-cache-key.js +108 -29
  133. package/dist/core/triwiki-attention.js +13 -6
  134. package/dist/core/triwiki-provenance.js +274 -0
  135. package/dist/core/triwiki-runtime.js +21 -7
  136. package/dist/core/triwiki-wrongness/wrongness-ledger.js +178 -59
  137. package/dist/core/triwiki-wrongness/wrongness-retrieval.js +11 -2
  138. package/dist/core/triwiki-wrongness/wrongness-trust-policy.js +2 -0
  139. package/dist/core/update/update-migration-state.js +1 -1
  140. package/dist/core/update/update-notice.js +23 -0
  141. package/dist/core/update-check.js +69 -25
  142. package/dist/core/version.js +1 -1
  143. package/dist/core/wiki-coordinate.js +128 -12
  144. package/dist/core/work-order-ledger.js +168 -9
  145. package/dist/core/zellij/zellij-command.js +12 -1
  146. package/dist/core/zellij/zellij-dashboard-pane.js +3 -3
  147. package/dist/core/zellij/zellij-launcher.js +15 -14
  148. package/dist/core/zellij/zellij-pane-proof.js +32 -6
  149. package/dist/core/zellij/zellij-worker-pane-manager.js +5 -3
  150. package/dist/scripts/agent-fast-mode-default-check.js +48 -29
  151. package/dist/scripts/agent-role-config-repair-check.js +25 -4
  152. package/dist/scripts/agent-wiki-context-proof-check.js +16 -2
  153. package/dist/scripts/all-feature-deep-completion-check.js +14 -4
  154. package/dist/scripts/build-dist.js +17 -5
  155. package/dist/scripts/canonical-test-runner.js +188 -0
  156. package/dist/scripts/check-command-module-budget.js +38 -16
  157. package/dist/scripts/check-legacy-free.js +1 -1
  158. package/dist/scripts/codex-0139-interrupt-agent-check.js +5 -1
  159. package/dist/scripts/{codex-0142-app-server-v2-check.js → codex-0144-app-server-v2-check.js} +11 -10
  160. package/dist/scripts/{codex-0142-binary-identity-check.js → codex-0144-binary-identity-check.js} +4 -4
  161. package/dist/scripts/codex-0144-capability-check.js +21 -0
  162. package/dist/scripts/{codex-0139-real-probes-check.js → codex-0144-core-real-probes-check.js} +19 -5
  163. package/dist/scripts/{codex-0142-doctor-wiring-check.js → codex-0144-doctor-wiring-check.js} +6 -6
  164. package/dist/scripts/{codex-0142-manifest-check.js → codex-0144-manifest-check.js} +11 -3
  165. package/dist/scripts/{codex-0142-policy-check.js → codex-0144-policy-check.js} +2 -2
  166. package/dist/scripts/{codex-0142-thread-store-check.js → codex-0144-thread-store-check.js} +3 -3
  167. package/dist/scripts/codex-app-provider-model-ui-check.js +57 -8
  168. package/dist/scripts/codex-app-ui-preservation-check.js +35 -4
  169. package/dist/scripts/codex-control-side-effect-scope-check.js +2 -2
  170. package/dist/scripts/codex-effort-auto-discovery-check.js +1 -1
  171. package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -0
  172. package/dist/scripts/{codex-lb-gpt55-fast-profile-check.js → codex-lb-gpt56-fast-profile-check.js} +56 -5
  173. package/dist/scripts/codex-model-metadata-check.js +1 -1
  174. package/dist/scripts/codex-sdk-sandbox-policy-check.js +5 -1
  175. package/dist/scripts/codex-sdk-version-compat-check.js +2 -1
  176. package/dist/scripts/dfix-fixture-check.js +1 -1
  177. package/dist/scripts/docs-truthfulness-check.js +1 -1
  178. package/dist/scripts/doctor-codex-startup-repair-check.js +35 -10
  179. package/dist/scripts/doctor-context7-repair-check.js +2 -1
  180. package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
  181. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +34 -7
  182. package/dist/scripts/doctor-imagegen-repair-check.js +14 -4
  183. package/dist/scripts/doctor-startup-config-repair-blackbox.js +1 -1
  184. package/dist/scripts/doctor-startup-config-repair-check.js +1 -1
  185. package/dist/scripts/gate-policy-audit-check.js +2 -2
  186. package/dist/scripts/install-update-preserves-config-check.js +1 -1
  187. package/dist/scripts/legacy-gate-inventory-check.js +7 -5
  188. package/dist/scripts/legacy-strong-inventory-check.js +7 -5
  189. package/dist/scripts/legacy-upgrade-matrix-check.js +5 -5
  190. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +175 -14
  191. package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +166 -6
  192. package/dist/scripts/mutation-callsite-coverage-check.js +0 -11
  193. package/dist/scripts/naruto-shadow-clone-swarm-check.js +88 -50
  194. package/dist/scripts/npm-publish-performance-check.js +2 -1
  195. package/dist/scripts/package-published-contract-check.js +9 -0
  196. package/dist/scripts/packlist-performance-check.js +5 -2
  197. package/dist/scripts/postinstall-safe-side-effects-check.js +8 -3
  198. package/dist/scripts/ppt-no-mock-as-real-check.js +3 -1
  199. package/dist/scripts/product-design-plugin-routing-check.js +1 -1
  200. package/dist/scripts/release-check-stamp.js +182 -2
  201. package/dist/scripts/release-dag-full-coverage-check.js +7 -7
  202. package/dist/scripts/release-dynamic-presets-check.js +8 -0
  203. package/dist/scripts/release-gate-dag-runner-check.js +3 -2
  204. package/dist/scripts/release-gate-dag-runner.js +24 -11
  205. package/dist/scripts/release-gate-existence-audit.js +8 -9
  206. package/dist/scripts/release-gate-script-parity-check.js +1 -1
  207. package/dist/scripts/release-real-check.js +368 -157
  208. package/dist/scripts/release-runtime-truth-matrix-check.js +22 -0
  209. package/dist/scripts/route-proof-artifact-structure-check.js +1 -0
  210. package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
  211. package/dist/scripts/search-visibility-gate-lib.js +1 -1
  212. package/dist/scripts/seo-geo-route-identity-check.js +30 -4
  213. package/dist/scripts/sizecheck.js +7 -2
  214. package/dist/scripts/sks-1-11-gate-lib.js +53 -6
  215. package/dist/scripts/sks-menubar-install-check.js +7 -0
  216. package/dist/scripts/sks-uninstall-regression-check.js +1 -1
  217. package/dist/scripts/sksd-daemon-check.js +16 -6
  218. package/dist/scripts/update-default-command-check.js +4 -2
  219. package/dist/scripts/write-build-manifest.js +2 -1
  220. package/dist/scripts/zellij-layout-valid-check.js +31 -6
  221. package/dist/scripts/zellij-pane-proof-check.js +33 -2
  222. package/dist/scripts/zellij-real-session-cleanup-check.js +122 -2
  223. package/dist/scripts/zellij-real-session-launch-check.js +76 -5
  224. package/dist/scripts/zellij-slot-pane-renderer-check.js +4 -4
  225. package/package.json +27 -30
  226. package/schemas/codex/{app-server-0.142 → app-server-0.144}/codex_app_server_protocol.v2.schemas.json +1146 -781
  227. package/schemas/codex/codex-0139-real-probe-result.schema.json +39 -5
  228. package/schemas/codex/ppt-slide-issue-ledger.schema.json +2 -1
  229. package/dist/core/codex-control/codex-0139-multi-agent-real-probe.js +0 -107
  230. package/dist/scripts/agent-native-release-gate.js +0 -274
  231. package/dist/scripts/codex-0142-capability-check.js +0 -21
  232. package/dist/scripts/command-performance-scorecard-check.js +0 -204
  233. package/dist/scripts/config-managed-merge-callsite-coverage-check.js +0 -212
  234. package/dist/scripts/dollar-performance-scorecard-check.js +0 -257
  235. package/dist/scripts/gate-timing-check.js +0 -16
  236. package/dist/scripts/ops-maturity-scorecard-check.js +0 -12
  237. package/dist/scripts/performance-baseline-check.js +0 -12
  238. package/dist/scripts/performance-improvement-report-check.js +0 -58
  239. package/dist/scripts/release-parallel-check.js +0 -389
  240. package/dist/scripts/release-runner-efficiency-check.js +0 -15
  241. package/dist/scripts/retention-long-run-smoke-check.js +0 -299
  242. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check-lib.js';
5
5
  import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
6
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');
7
+ await writeText(path.join(root, '.codex', 'config.toml'), '# SKS managed Codex config fixture\n[agents.analysis_scout]\nconfig_file = ".codex/agents/stale.toml"\nmessage_role_prefix = "legacy"\n');
8
8
  const report = await repairCodexStartupConfig({ root, apply: true });
9
9
  const text = await fs.readFile(path.join(root, '.codex', 'config.toml'), 'utf8');
10
10
  assertGate(report.ok === true, 'startup config blackbox must pass postcheck', report);
@@ -3,7 +3,7 @@ import { assertGate, emitGate, makeTempRoot, writeText } from './sks-3-1-8-check
3
3
  import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
4
4
  import path from 'node:path';
5
5
  const root = await makeTempRoot('sks-startup-config-');
6
- await writeText(path.join(root, '.codex', 'config.toml'), '[agents.analysis_scout]\nconfig_file = ".codex/agents/missing.toml"\nmessage_role_prefix = "legacy"\n');
6
+ await writeText(path.join(root, '.codex', 'config.toml'), '# SKS managed Codex config fixture\n[agents.analysis_scout]\nconfig_file = ".codex/agents/missing.toml"\nmessage_role_prefix = "legacy"\n');
7
7
  const report = await repairCodexStartupConfig({ root, apply: true });
8
8
  assertGate(report.ok === true, 'startup config repair must repair stale config_file paths and unsupported fields', report);
9
9
  emitGate('doctor:startup-config-repair');
@@ -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}`);
@@ -68,7 +68,7 @@ const results = [];
68
68
  const codexDir = path.join(dir, '.codex');
69
69
  await fs.mkdir(codexDir, { recursive: true });
70
70
  const cfg = path.join(codexDir, 'config.toml');
71
- const broken = 'model = "gpt-5.5"\n[features\nhooks = true\n'; // malformed table header
71
+ const broken = 'model = "gpt-5.6-terra"\n[features\nhooks = true\n'; // malformed table header
72
72
  await fs.writeFile(cfg, broken);
73
73
  const res = await helpers.ensureGlobalCodexFastModeDuringInstall({ home: dir, configPath: cfg });
74
74
  const after = await fs.readFile(cfg, 'utf8');
@@ -1,7 +1,9 @@
1
1
  import { assertGate, emitGate, readJson, readText } from './sks-1-18-gate-lib.js';
2
2
  const gates = (readJson('release-gates.v2.json').gates || []);
3
3
  const migration = readText('docs/sks-4-migration.md');
4
- const allowlist = parseAllowlist(migration);
4
+ const currentVersion = String(readJson('package.json').version || '');
5
+ assertGate(/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(currentVersion), 'package version must be valid before evaluating legacy allowlist expiry', { currentVersion });
6
+ const allowlist = parseAllowlist(migration, currentVersion);
5
7
  const legacy = gates
6
8
  .filter((gate) => gate.preset?.includes('release'))
7
9
  .filter((gate) => removableLegacy(gate.id, gate.command))
@@ -19,7 +21,7 @@ function removableLegacy(id, command) {
19
21
  || /codex[:.-]?0139/.test(id)
20
22
  || /codex-0139/.test(command);
21
23
  }
22
- function parseAllowlist(text) {
24
+ function parseAllowlist(text, currentVersion) {
23
25
  return [...text.matchAll(/<!--\s*sks-legacy-allowlist\s*([\s\S]*?)-->/g)].map((match) => {
24
26
  const body = match[1] || '';
25
27
  return {
@@ -28,7 +30,7 @@ function parseAllowlist(text) {
28
30
  owner: field(body, 'owner'),
29
31
  expires: field(body, 'expires')
30
32
  };
31
- }).filter((row) => row.id && row.reason && row.owner && compareVersion(row.expires, '4.0.2') > 0);
33
+ }).filter((row) => row.id && row.reason && row.owner && compareVersion(row.expires, currentVersion) > 0);
32
34
  }
33
35
  function isAllowed(id, allowlist) {
34
36
  return allowlist.some((row) => row.id.endsWith('*') ? id.startsWith(row.id.slice(0, -1)) : row.id === id);
@@ -37,8 +39,8 @@ function field(body, name) {
37
39
  return body.match(new RegExp(`^${name}:\\s*(.+)$`, 'm'))?.[1]?.trim() || '';
38
40
  }
39
41
  function compareVersion(a, b) {
40
- const left = a.split('.').map(Number);
41
- const right = b.split('.').map(Number);
42
+ const left = (a.split(/[+-]/, 1)[0] || '').split('.').map(Number);
43
+ const right = (b.split(/[+-]/, 1)[0] || '').split('.').map(Number);
42
44
  for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
43
45
  const delta = (left[i] || 0) - (right[i] || 0);
44
46
  if (delta !== 0)
@@ -2,7 +2,9 @@ import { assertGate, emitGate, importDist, readJson, readText, root } from './sk
2
2
  const impactMod = await importDist('core/triwiki/triwiki-gate-impact-map.js');
3
3
  const map = impactMod.buildTriWikiGateImpactMap(root);
4
4
  const migration = readText('docs/sks-4-migration.md');
5
- const allowlist = parseAllowlist(migration);
5
+ const currentVersion = String(readJson('package.json').version || '');
6
+ assertGate(/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(currentVersion), 'package version must be valid before evaluating legacy allowlist expiry', { currentVersion });
7
+ const allowlist = parseAllowlist(migration, currentVersion);
6
8
  const gates = (readJson('release-gates.v2.json').gates || []);
7
9
  const legacyOrphans = map.impacts.filter((impact) => impact.orphan && removableLegacy(impact.gate_id, ''));
8
10
  const legacyPresent = gates.filter((gate) => gate.preset?.includes('release') && removableLegacy(gate.id, gate.command) && !isAllowed(gate.id, allowlist));
@@ -18,11 +20,11 @@ function removableLegacy(id, command) {
18
20
  || /codex[:.-]?0139/.test(id)
19
21
  || /codex-0139/.test(command);
20
22
  }
21
- function parseAllowlist(text) {
23
+ function parseAllowlist(text, currentVersion) {
22
24
  return [...text.matchAll(/<!--\s*sks-legacy-allowlist\s*([\s\S]*?)-->/g)].map((match) => {
23
25
  const body = match[1] || '';
24
26
  return { id: field(body, 'id'), reason: field(body, 'reason'), owner: field(body, 'owner'), expires: field(body, 'expires') };
25
- }).filter((row) => row.id && row.reason && row.owner && compareVersion(row.expires, '4.0.2') > 0);
27
+ }).filter((row) => row.id && row.reason && row.owner && compareVersion(row.expires, currentVersion) > 0);
26
28
  }
27
29
  function isAllowed(id, allowlist) {
28
30
  return allowlist.some((row) => row.id.endsWith('*') ? id.startsWith(row.id.slice(0, -1)) : row.id === id);
@@ -31,8 +33,8 @@ function field(body, name) {
31
33
  return body.match(new RegExp(`^${name}:\\s*(.+)$`, 'm'))?.[1]?.trim() || '';
32
34
  }
33
35
  function compareVersion(a, b) {
34
- const left = a.split('.').map(Number);
35
- const right = b.split('.').map(Number);
36
+ const left = (a.split(/[+-]/, 1)[0] || '').split('.').map(Number);
37
+ const right = (b.split(/[+-]/, 1)[0] || '').split('.').map(Number);
36
38
  for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
37
39
  const delta = (left[i] || 0) - (right[i] || 0);
38
40
  if (delta !== 0)
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // @ts-nocheck
3
- // Gate: legacy:upgrade-zero-break
3
+ // Gate: migration:upgrade-safety
4
4
  // Proves a 1.18.x -> 1.19 upgrade never breaks user Codex config across legacy
5
5
  // states. Operates entirely on temp dirs (os.tmpdir + fs.mkdtemp); never touches
6
6
  // the real ~/.codex. Always restores process.env.CODEX_HOME / HOME in finally.
@@ -41,7 +41,7 @@ try {
41
41
  const dir = mkTmp('sks-legacy-corrupt-');
42
42
  tmpDirs.push(dir);
43
43
  const configPath = path.join(dir, 'config.toml');
44
- const corrupted = 'model = "gpt-5.5"\nservice_tier = "fast"\n\n[mcp_servers.xai-grok.env]\nXAI_API_KEY = "x"\nmodel_provider = "codex-lb"\nnotify = ["a","b"]\n';
44
+ const corrupted = 'model = "gpt-5.6-terra"\nservice_tier = "fast"\n\n[mcp_servers.xai-grok.env]\nXAI_API_KEY = "x"\nmodel_provider = "codex-lb"\nnotify = ["a","b"]\n';
45
45
  fs.writeFileSync(configPath, corrupted, 'utf8');
46
46
  const result = await policy.repairCodexConfigStructure(configPath, { apply: true });
47
47
  const okStatuses = ['structure_repaired', 'structure_repair_available', 'structure_ok'];
@@ -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,
@@ -214,7 +214,7 @@ try {
214
214
  const dir = mkTmp('sks-legacy-noop-');
215
215
  tmpDirs.push(dir);
216
216
  const configPath = path.join(dir, 'config.toml');
217
- const clean = 'model = "gpt-5.5"\nservice_tier = "fast"\n\n[features]\nhooks = true\n';
217
+ const clean = 'model = "gpt-5.6-terra"\nservice_tier = "fast"\n\n[features]\nhooks = true\n';
218
218
  fs.writeFileSync(configPath, clean, 'utf8');
219
219
  const before = fs.readFileSync(configPath, 'utf8');
220
220
  const result = await policy.repairCodexConfigStructure(configPath, { apply: true });
@@ -270,7 +270,7 @@ try {
270
270
  fs.mkdirSync(reportDir, { recursive: true });
271
271
  const reportPath = path.join(reportDir, 'legacy-upgrade-matrix.json');
272
272
  fs.writeFileSync(reportPath, `${JSON.stringify({ schema: 'sks.legacy-upgrade-matrix.v1', ok: true, ...summary }, null, 2)}\n`, 'utf8');
273
- emitGate('legacy:upgrade-zero-break', {
273
+ emitGate('migration:upgrade-safety', {
274
274
  report_path: reportPath,
275
275
  states_checked: [
276
276
  'corrupted_config',
@@ -1,8 +1,15 @@
1
1
  // @ts-nocheck
2
2
  import fs from 'node:fs';
3
+ import os from 'node:os';
3
4
  import path from 'node:path';
5
+ import crypto from 'node:crypto';
4
6
  import { execFileSync } from 'node:child_process';
5
7
  import { assertGate, root } from '../sks-1-18-gate-lib.js';
8
+ const MAX_EMPTY_OUTPUT_ATTEMPTS = 2;
9
+ const CLI_COMPLETION_TIMEOUT_MS = 180_000;
10
+ const CLI_STABILITY_WAIT_MS = 500;
11
+ const CLI_STABILITY_SAMPLE_MS = 25;
12
+ const stabilityWaitCell = new Int32Array(new SharedArrayBuffer(4));
6
13
  export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName, backend = 'fake', extraArgs = [], expectedFastMode = null }) {
7
14
  const distCli = path.join(root, 'dist', 'bin', 'sks.js');
8
15
  assertGate(fs.existsSync(distCli), 'dist CLI missing for native CLI swarm check', { distCli });
@@ -25,18 +32,37 @@ export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName,
25
32
  '--json',
26
33
  ...extraArgs
27
34
  ];
28
- const stdout = execFileSync(process.execPath, args, {
29
- cwd: root,
30
- encoding: 'utf8',
31
- env: {
32
- ...process.env,
33
- SKS_TEST_ISOLATION: '1',
34
- SKS_RELEASE_FIXTURE_ACTIVE_ROUTE_BYPASS: '1',
35
- ...(backend === 'codex-sdk' ? { SKS_CODEX_SDK_FAKE: '1', NODE_ENV: 'test' } : {})
36
- },
37
- maxBuffer: 96 * 1024 * 1024
35
+ const attempts = [];
36
+ let inspected = null;
37
+ while (attempts.length < MAX_EMPTY_OUTPUT_ATTEMPTS) {
38
+ const attempt = runCliAttempt({ distCli, args, backend, attemptNumber: attempts.length + 1 });
39
+ attempts.push(attempt);
40
+ inspected = inspectNativeCliSwarmOutput(attempt.stdout);
41
+ if (inspected.ok || inspected.reason !== 'empty_stdout')
42
+ break;
43
+ if (attempts.length >= MAX_EMPTY_OUTPUT_ATTEMPTS)
44
+ break;
45
+ const stable = waitForStableNonemptyCli(distCli);
46
+ assertGate(stable.ok, 'native CLI session swarm emitted empty JSON and the CLI did not settle for a bounded retry', {
47
+ dist_cli: distCli,
48
+ attempt_count: attempts.length,
49
+ completion_timeout_ms: CLI_COMPLETION_TIMEOUT_MS,
50
+ stability_wait_ms: CLI_STABILITY_WAIT_MS,
51
+ before: attempt.cli_before,
52
+ after: attempt.cli_after,
53
+ stable
54
+ });
55
+ }
56
+ assertGate(inspected?.ok === true, inspected?.reason === 'empty_stdout'
57
+ ? 'native CLI session swarm completed without JSON output after one bounded retry'
58
+ : 'native CLI session swarm emitted incomplete or invalid JSON', {
59
+ dist_cli: distCli,
60
+ attempt_count: attempts.length,
61
+ completion_timeout_ms: CLI_COMPLETION_TIMEOUT_MS,
62
+ attempts: attempts.map(attemptDiagnostic),
63
+ output: inspected
38
64
  });
39
- const result = JSON.parse(stdout);
65
+ const result = inspected.value;
40
66
  const proof = result.native_cli_session_proof || {};
41
67
  const noSubagent = result.no_subagent_scaling_policy || {};
42
68
  const officialHelper = result.official_subagent_helper_policy || {};
@@ -55,7 +81,16 @@ export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName,
55
81
  no_subagent_scaling_policy: noSubagent,
56
82
  official_subagent_helper_policy: officialHelper,
57
83
  fast_mode_propagation: fast,
58
- proof_status: result.proof?.status || null
84
+ proof_status: result.proof?.status || null,
85
+ completion_protocol: {
86
+ schema: 'sks.native-cli-check-completion.v1',
87
+ attempt_count: attempts.length,
88
+ max_attempts: MAX_EMPTY_OUTPUT_ATTEMPTS,
89
+ bounded_timeout_ms: CLI_COMPLETION_TIMEOUT_MS,
90
+ atomic_output_read: true,
91
+ transient_empty_output_recovered: attempts.length > 1,
92
+ output_bytes: attempts.at(-1)?.output_bytes || 0
93
+ }
59
94
  };
60
95
  const out = path.join(root, '.sneakoscope', 'reports', reportName);
61
96
  fs.mkdirSync(path.dirname(out), { recursive: true });
@@ -63,7 +98,8 @@ export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName,
63
98
  assertGate(result.ok === true, 'native CLI session swarm run must pass', report);
64
99
  assertGate(proof.spawned_worker_process_count >= agents, 'spawned native worker process count below requested agents', report);
65
100
  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);
101
+ const expectedSlots = Math.min(agents, Number(proof.target_active_slots || agents));
102
+ assertGate(proof.unique_slot_count >= expectedSlots, 'unique slot count below effective concurrency target', report);
67
103
  assertGate(Array.isArray(proof.process_ids) && proof.process_ids.length >= agents, 'process ids missing from native CLI proof', report);
68
104
  assertGate(proof.close_report_count >= agents, 'worker close report count below requested agents', report);
69
105
  assertGate(noSubagent.ok === true && noSubagent.subagent_events_counted_as_worker_sessions === false, 'no-subagent scaling policy must pass', report);
@@ -72,10 +108,135 @@ export function runNativeCliSwarmCheck({ agents, workItems = agents, reportName,
72
108
  assertGate(noSubagent.official_codex_subagent_helper_lane_allowed === true && noSubagent.official_helper_lane_worker_capacity_credit === 0, 'no-subagent policy must allow helper lane with zero capacity credit', report);
73
109
  assertGate(fast.ok === true, 'fast mode propagation proof must pass', report);
74
110
  assertGate(fast.fast_mode === expectedFast && fast.service_tier === expectedTier, 'worker service tier must match the selected fast-mode policy', { ...report, expected_fast_mode: expectedFast, expected_service_tier: expectedTier });
75
- if (expectedFast) {
111
+ if (expectedFast && expectedFastMode === true) {
76
112
  assertGate(policy.explicit_fast === true || policy.preference_mode === 'fast' || policy.explicit_service_tier === 'fast', 'fast-mode propagation gate must use explicit fast opt-in', report);
77
113
  }
78
114
  assertGate((proof.worker_command_lines || []).every((line) => line.includes('--agent') && line.includes('worker')), 'worker command lines must use --agent worker', report);
79
115
  return report;
80
116
  }
117
+ export function inspectNativeCliSwarmOutput(stdout) {
118
+ const text = typeof stdout === 'string' ? stdout : String(stdout || '');
119
+ if (text.trim().length === 0)
120
+ return { ok: false, reason: 'empty_stdout', output_bytes: Buffer.byteLength(text) };
121
+ try {
122
+ const value = JSON.parse(text);
123
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
124
+ return { ok: false, reason: 'non_object_json', output_bytes: Buffer.byteLength(text) };
125
+ }
126
+ return { ok: true, value, output_bytes: Buffer.byteLength(text) };
127
+ }
128
+ catch (error) {
129
+ return {
130
+ ok: false,
131
+ reason: 'invalid_json',
132
+ output_bytes: Buffer.byteLength(text),
133
+ parse_error: error instanceof Error ? error.message : String(error),
134
+ output_sha256: crypto.createHash('sha256').update(text).digest('hex')
135
+ };
136
+ }
137
+ }
138
+ function runCliAttempt({ distCli, args, backend, attemptNumber }) {
139
+ const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-native-cli-swarm-fixture-'));
140
+ const fixtureHome = path.join(fixtureRoot, 'home');
141
+ const outputDraft = path.join(fixtureRoot, `.native-cli-result-${attemptNumber}.json.partial`);
142
+ const outputComplete = path.join(fixtureRoot, `.native-cli-result-${attemptNumber}.json`);
143
+ let outputFd = fs.openSync(outputDraft, 'wx', 0o600);
144
+ const cleanupFixture = () => fs.rmSync(fixtureRoot, { recursive: true, force: true });
145
+ process.once('exit', cleanupFixture);
146
+ const cliBefore = cliSnapshot(distCli);
147
+ try {
148
+ execFileSync(process.execPath, args, {
149
+ cwd: fixtureRoot,
150
+ env: {
151
+ ...process.env,
152
+ HOME: fixtureHome,
153
+ CODEX_HOME: path.join(fixtureHome, '.codex'),
154
+ SKS_GLOBAL_ROOT: path.join(fixtureHome, '.sneakoscope-global'),
155
+ TMPDIR: fixtureRoot,
156
+ TMP: fixtureRoot,
157
+ TEMP: fixtureRoot,
158
+ PWD: fixtureRoot,
159
+ SKS_TEST_ISOLATION: '1',
160
+ SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
161
+ NODE_ENV: 'test',
162
+ CI: 'true',
163
+ ...(backend === 'codex-sdk' ? { SKS_CODEX_SDK_FAKE: '1' } : {})
164
+ },
165
+ timeout: CLI_COMPLETION_TIMEOUT_MS,
166
+ killSignal: 'SIGTERM',
167
+ stdio: ['ignore', outputFd, 'pipe'],
168
+ maxBuffer: 96 * 1024 * 1024
169
+ });
170
+ fs.fsyncSync(outputFd);
171
+ fs.closeSync(outputFd);
172
+ outputFd = null;
173
+ fs.renameSync(outputDraft, outputComplete);
174
+ const stdout = fs.readFileSync(outputComplete, 'utf8');
175
+ const cliAfter = cliSnapshot(distCli);
176
+ return {
177
+ attempt: attemptNumber,
178
+ stdout,
179
+ output_bytes: Buffer.byteLength(stdout),
180
+ cli_before: cliBefore,
181
+ cli_after: cliAfter
182
+ };
183
+ }
184
+ finally {
185
+ if (outputFd !== null) {
186
+ try {
187
+ fs.closeSync(outputFd);
188
+ }
189
+ catch { }
190
+ }
191
+ process.removeListener('exit', cleanupFixture);
192
+ cleanupFixture();
193
+ }
194
+ }
195
+ function waitForStableNonemptyCli(distCli) {
196
+ const startedAt = Date.now();
197
+ let previous = cliSnapshot(distCli);
198
+ while (Date.now() - startedAt < CLI_STABILITY_WAIT_MS) {
199
+ Atomics.wait(stabilityWaitCell, 0, 0, CLI_STABILITY_SAMPLE_MS);
200
+ const current = cliSnapshot(distCli);
201
+ if (current.ok && current.size > 0 && sameCliSnapshot(previous, current)) {
202
+ return { ok: true, waited_ms: Date.now() - startedAt, snapshot: current };
203
+ }
204
+ previous = current;
205
+ }
206
+ return { ok: false, waited_ms: Date.now() - startedAt, snapshot: previous };
207
+ }
208
+ function cliSnapshot(file) {
209
+ try {
210
+ const stat = fs.statSync(file);
211
+ const bytes = fs.readFileSync(file);
212
+ return {
213
+ ok: stat.isFile(),
214
+ size: stat.size,
215
+ mtime_ms: stat.mtimeMs,
216
+ dev: String(stat.dev),
217
+ ino: String(stat.ino),
218
+ sha256: crypto.createHash('sha256').update(bytes).digest('hex')
219
+ };
220
+ }
221
+ catch (error) {
222
+ return { ok: false, size: 0, error: error instanceof Error ? error.message : String(error) };
223
+ }
224
+ }
225
+ function sameCliSnapshot(left, right) {
226
+ return left?.ok === true
227
+ && right?.ok === true
228
+ && left.size === right.size
229
+ && left.mtime_ms === right.mtime_ms
230
+ && left.dev === right.dev
231
+ && left.ino === right.ino
232
+ && left.sha256 === right.sha256;
233
+ }
234
+ function attemptDiagnostic(attempt) {
235
+ return {
236
+ attempt: attempt.attempt,
237
+ output_bytes: attempt.output_bytes,
238
+ cli_before: attempt.cli_before,
239
+ cli_after: attempt.cli_after
240
+ };
241
+ }
81
242
  //# sourceMappingURL=native-cli-session-swarm-check-lib.js.map
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import assert from 'node:assert/strict';
3
+ import { randomBytes } from 'node:crypto';
3
4
  import fs from 'node:fs/promises';
4
5
  import os from 'node:os';
5
6
  import path from 'node:path';
6
7
  import { madHighCommand } from '../core/commands/mad-sks-command.js';
7
- import { sha256 } from '../core/fsx.js';
8
+ import { runProcess, sha256 } from '../core/fsx.js';
9
+ import { runZellij } from '../core/zellij/zellij-command.js';
8
10
  const original = {
9
11
  cwd: process.cwd(),
10
12
  home: process.env.HOME,
11
13
  codexHome: process.env.CODEX_HOME,
12
14
  noAttach: process.env.SKS_NO_ZELLIJ_ATTACH,
13
15
  requireZellij: process.env.SKS_REQUIRE_ZELLIJ,
16
+ zellijSocketDir: process.env.ZELLIJ_SOCKET_DIR,
14
17
  madSwarm: process.env.SKS_MAD_NATIVE_SWARM,
15
18
  skipNpm: process.env.SKS_SKIP_NPM_FRESHNESS_CHECK
16
19
  };
@@ -18,6 +21,12 @@ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-mad-ui-no-mutation-'));
18
21
  const home = path.join(tmp, 'home');
19
22
  const codexHome = path.join(home, '.codex');
20
23
  const configPath = path.join(codexHome, 'config.toml');
24
+ const runId = `${process.pid}-${Date.now().toString(36)}-${randomBytes(3).toString('hex')}`;
25
+ const sessionName = `sks-mad-ui-check-${runId}`;
26
+ const socketDir = path.join('/tmp', `sks-zj-madui-${runId}`);
27
+ let report;
28
+ let cleanup = { ok: false, blockers: ['cleanup_not_run'] };
29
+ let fixtureRemoval = { ok: false, blockers: ['fixture_removal_not_run'] };
21
30
  try {
22
31
  await fs.mkdir(codexHome, { recursive: true });
23
32
  await fs.mkdir(path.join(tmp, '.sneakoscope'), { recursive: true });
@@ -35,10 +44,11 @@ try {
35
44
  process.env.CODEX_HOME = codexHome;
36
45
  process.env.SKS_NO_ZELLIJ_ATTACH = '1';
37
46
  process.env.SKS_REQUIRE_ZELLIJ = '0';
47
+ process.env.ZELLIJ_SOCKET_DIR = socketDir;
38
48
  process.env.SKS_MAD_NATIVE_SWARM = '0';
39
49
  process.env.SKS_SKIP_NPM_FRESHNESS_CHECK = '1';
40
50
  process.exitCode = 0;
41
- await madHighCommand(['--no-attach', '--no-swarm'], {
51
+ const launch = await madHighCommand(['--no-attach', '--no-swarm', '--session', sessionName], {
42
52
  maybePromptSksUpdateForLaunch: async () => ({ status: 'skipped' }),
43
53
  maybePromptCodexUpdateForLaunch: async () => ({ status: 'skipped' }),
44
54
  ensureMadLaunchDependencies: async () => ({ ready: true, actions: [] }),
@@ -51,33 +61,54 @@ try {
51
61
  assert.equal(/\[profiles\.sks-mad-high\]/.test(after), false);
52
62
  assert.equal(/enabled\s*=\s*false/.test(after), true);
53
63
  process.exitCode = 0;
54
- emit({
64
+ report = {
55
65
  schema: 'sks.mad-sks-app-ui-no-mutation-check.v1',
56
66
  ok: true,
57
67
  before_hash: sha256(before),
58
68
  after_hash: sha256(after),
59
69
  profile_files_written: entries.filter((entry) => /sks-mad-high/.test(entry)),
60
70
  plugin_disabled_preserved: true,
71
+ session_name: launch?.session_name || null,
72
+ isolated_socket_dir: socketDir,
61
73
  blockers: []
62
- });
74
+ };
63
75
  }
64
76
  catch (err) {
65
- emit({
77
+ report = {
66
78
  schema: 'sks.mad-sks-app-ui-no-mutation-check.v1',
67
79
  ok: false,
68
80
  error: err?.message || String(err),
69
81
  blockers: ['mad_sks_app_ui_no_mutation_failed']
70
- });
82
+ };
71
83
  }
72
84
  finally {
73
85
  process.chdir(original.cwd);
86
+ cleanup = await cleanupOwnedSession(sessionName, socketDir, original.cwd);
87
+ if (!cleanup.ok) {
88
+ report.ok = false;
89
+ report.blockers = [...new Set([...(report.blockers || []), ...cleanup.blockers])];
90
+ }
74
91
  restoreEnv('HOME', original.home);
75
92
  restoreEnv('CODEX_HOME', original.codexHome);
76
93
  restoreEnv('SKS_NO_ZELLIJ_ATTACH', original.noAttach);
77
94
  restoreEnv('SKS_REQUIRE_ZELLIJ', original.requireZellij);
95
+ restoreEnv('ZELLIJ_SOCKET_DIR', original.zellijSocketDir);
78
96
  restoreEnv('SKS_MAD_NATIVE_SWARM', original.madSwarm);
79
97
  restoreEnv('SKS_SKIP_NPM_FRESHNESS_CHECK', original.skipNpm);
98
+ if (cleanup.ok) {
99
+ fixtureRemoval = await removeFixtureRootAfterQuiescence(tmp);
100
+ if (!fixtureRemoval.ok) {
101
+ cleanup.ok = false;
102
+ cleanup.blockers = [...new Set([...(cleanup.blockers || []), ...fixtureRemoval.blockers])];
103
+ report.ok = false;
104
+ report.blockers = [...new Set([...(report.blockers || []), ...fixtureRemoval.blockers])];
105
+ }
106
+ }
80
107
  }
108
+ report.cleanup = cleanup;
109
+ report.fixture_removal = fixtureRemoval;
110
+ report.temporary_root_removed = fixtureRemoval.ok && !(await exists(tmp));
111
+ emit(report);
81
112
  function restoreEnv(key, value) {
82
113
  if (value == null)
83
114
  delete process.env[key];
@@ -89,4 +120,133 @@ function emit(report) {
89
120
  if (!report.ok)
90
121
  process.exitCode = 1;
91
122
  }
123
+ async function cleanupOwnedSession(sessionName, socketDir, cwd) {
124
+ const ownedProcessTree = await readOwnedZellijProcessTree(sessionName, socketDir, cwd);
125
+ let kill = null;
126
+ let remaining = [];
127
+ for (let attempt = 1; attempt <= 2; attempt += 1) {
128
+ kill = await runZellij(['kill-session', sessionName], {
129
+ cwd,
130
+ env: { ZELLIJ_SOCKET_DIR: socketDir },
131
+ timeoutMs: 5000,
132
+ optional: true
133
+ });
134
+ remaining = await waitForSocketEntriesToClear(socketDir, 1500);
135
+ if (!remaining.includes(sessionName))
136
+ break;
137
+ }
138
+ const processExit = await waitForOwnedProcessesToExit(ownedProcessTree.processes, 5000);
139
+ const sessionRemoved = !remaining.includes(sessionName);
140
+ const socketExclusive = remaining.length === 0;
141
+ if (sessionRemoved && socketExclusive && processExit.ok)
142
+ await fs.rm(socketDir, { recursive: true, force: true });
143
+ const socketDirRemoved = !(await exists(socketDir));
144
+ const blockers = [
145
+ ...(!sessionRemoved ? ['mad_ui_check_zellij_session_still_present'] : []),
146
+ ...(!socketExclusive ? ['mad_ui_check_zellij_socket_not_exclusive'] : []),
147
+ ...(!ownedProcessTree.server_found ? ['mad_ui_check_zellij_server_not_observed'] : []),
148
+ ...(!processExit.ok ? ['mad_ui_check_owned_processes_still_alive'] : []),
149
+ ...(!socketDirRemoved ? ['mad_ui_check_zellij_socket_dir_not_removed'] : [])
150
+ ];
151
+ return {
152
+ ok: blockers.length === 0,
153
+ session_removed: sessionRemoved,
154
+ socket_dir_removed: socketDirRemoved,
155
+ remaining_socket_entries: remaining,
156
+ owned_process_tree: ownedProcessTree,
157
+ owned_process_exit: processExit,
158
+ kill,
159
+ blockers
160
+ };
161
+ }
162
+ async function readOwnedZellijProcessTree(sessionName, socketDir, cwd) {
163
+ const serverSocket = path.join(socketDir, 'contract_version_1', sessionName);
164
+ const deadline = Date.now() + 1500;
165
+ let rows = [];
166
+ let server = null;
167
+ do {
168
+ const result = await runProcess('ps', ['-axo', 'pid=,ppid=,command='], {
169
+ cwd,
170
+ timeoutMs: 3000,
171
+ maxOutputBytes: 1024 * 1024
172
+ });
173
+ rows = parseProcessRows(result.stdout);
174
+ server = rows.find((row) => {
175
+ const executable = row.command.trim().split(/\s+/, 1)[0] || '';
176
+ return path.basename(executable) === 'zellij' && row.command.includes(` --server ${serverSocket}`);
177
+ }) || null;
178
+ if (!server && Date.now() < deadline)
179
+ await delay(50);
180
+ } while (!server && Date.now() < deadline);
181
+ if (!server)
182
+ return { server_found: false, server_socket: serverSocket, processes: [] };
183
+ const byParent = new Map();
184
+ for (const row of rows)
185
+ byParent.set(row.ppid, [...(byParent.get(row.ppid) || []), row]);
186
+ const processes = [];
187
+ const visit = (pid) => {
188
+ const row = rows.find((candidate) => candidate.pid === pid);
189
+ if (row)
190
+ processes.push(row);
191
+ for (const child of byParent.get(pid) || [])
192
+ visit(child.pid);
193
+ };
194
+ visit(server.pid);
195
+ return { server_found: true, server_socket: serverSocket, processes };
196
+ }
197
+ function parseProcessRows(text) {
198
+ return text.split(/\r?\n/).map((line) => {
199
+ const match = line.match(/^\s*(\d+)\s+(\d+)\s+(.+)$/);
200
+ return match ? { pid: Number(match[1]), ppid: Number(match[2]), command: match[3] || '' } : null;
201
+ }).filter(Boolean);
202
+ }
203
+ async function waitForOwnedProcessesToExit(processes, timeoutMs) {
204
+ const pids = [...new Set(processes.map((row) => row.pid))];
205
+ const deadline = Date.now() + timeoutMs;
206
+ while (Date.now() < deadline && pids.some(processAlive))
207
+ await delay(50);
208
+ const alive = pids.filter(processAlive);
209
+ return { ok: alive.length === 0, observed_pids: pids, verified_exited_pids: pids.filter((pid) => !alive.includes(pid)), alive_pids: alive };
210
+ }
211
+ function processAlive(pid) {
212
+ try {
213
+ process.kill(pid, 0);
214
+ return true;
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ }
220
+ async function removeFixtureRootAfterQuiescence(root) {
221
+ try {
222
+ await fs.rm(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
223
+ }
224
+ catch (err) {
225
+ return { ok: false, removed: false, recreated: false, quiet_period_ms: 300, error: err?.message || String(err), blockers: ['mad_ui_check_fixture_removal_failed'] };
226
+ }
227
+ await delay(300);
228
+ const recreated = await exists(root);
229
+ return {
230
+ ok: !recreated,
231
+ removed: !recreated,
232
+ recreated,
233
+ quiet_period_ms: 300,
234
+ blockers: recreated ? ['mad_ui_check_fixture_recreated_after_return'] : []
235
+ };
236
+ }
237
+ async function waitForSocketEntriesToClear(socketDir, timeoutMs) {
238
+ const deadline = Date.now() + timeoutMs;
239
+ for (;;) {
240
+ const entries = await fs.readdir(path.join(socketDir, 'contract_version_1')).catch(() => []);
241
+ if (entries.length === 0 || Date.now() >= deadline)
242
+ return entries;
243
+ await delay(50);
244
+ }
245
+ }
246
+ function delay(ms) {
247
+ return new Promise((resolve) => setTimeout(resolve, ms));
248
+ }
249
+ async function exists(value) {
250
+ return fs.access(value).then(() => true).catch(() => false);
251
+ }
92
252
  //# sourceMappingURL=mad-sks-app-ui-no-mutation-check.js.map
@@ -117,20 +117,9 @@ function listScanFiles() {
117
117
  const relative = rel(file);
118
118
  if (isTestSource(relative))
119
119
  return;
120
- if (relative.startsWith('src/scripts/'))
121
- return;
122
120
  if (file.endsWith('.ts'))
123
121
  files.push(relative);
124
122
  });
125
- walk(path.join(root, 'src', 'scripts'), (file) => {
126
- if (!file.endsWith('.ts'))
127
- return;
128
- if (isTestSource(rel(file)))
129
- return;
130
- const base = path.basename(file);
131
- if (/(install|publish|release|doctor|codex|zellij|migration)/i.test(base))
132
- files.push(rel(file));
133
- });
134
123
  return files.sort();
135
124
  }
136
125
  function isTestSource(relative) {