sneakoscope 7.3.0 → 7.6.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 (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -2,7 +2,7 @@
2
2
  import fs from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { codexFastModeDesktopStatus, codexLbConfigPath, configureCodexLb, ensureGlobalCodexFastModeDuringInstall, releaseCodexLbAuthHold, repairCodexLbAuth } from '../cli/install-helpers.js';
5
+ import { codexFastModeDesktopStatus, codexLbConfigPath, configureCodexLb, configureCodexLbDesktopRouting, ensureGlobalCodexFastModeDuringInstall, repairCodexLbAuth } from '../cli/install-helpers.js';
6
6
  import { repairCodexConfigStructure, splitCodexProjectConfigPolicy } from '../core/codex/codex-project-config-policy.js';
7
7
  import { parseCodexConfigToml, validateCodexConfigRoundTrip } from '../core/codex/codex-config-toml.js';
8
8
  import { CODEX_LB_TOOL_OUTPUT_RECOVERY_MIN_VERSION } from '../core/codex-lb/codex-lb-tool-output-recovery.js';
@@ -14,30 +14,70 @@ const codexHome = path.join(home, '.codex');
14
14
  const configPath = codexLbConfigPath(home);
15
15
  const envPath = path.join(codexHome, 'sks-codex-lb.env');
16
16
  const authPath = path.join(codexHome, 'auth.json');
17
- const oauthBackupPath = path.join(codexHome, 'auth.chatgpt-backup.json');
18
17
  const projectConfig = path.join(root, '.codex', 'config.toml');
18
+ const remoteBaseUrl = 'https://lb.example.test/backend-api/codex';
19
+ const bridgeBaseUrl = 'http://127.0.0.1:54321/backend-api/codex';
19
20
  await fs.mkdir(path.dirname(projectConfig), { recursive: true });
20
21
  await fs.mkdir(codexHome, { recursive: true });
21
- await fs.writeFile(authPath, `${JSON.stringify({ auth_mode: 'chatgpt', tokens: { access_token: 'oauth-access' } }, null, 2)}\n`);
22
+ const oauth = `${JSON.stringify({
23
+ auth_mode: 'chatgpt',
24
+ account_id: 'acct-fast-profile',
25
+ tokens: {
26
+ access_token: 'oauth-access',
27
+ refresh_token: 'oauth-refresh'
28
+ }
29
+ }, null, 2)}\n`;
30
+ await fs.writeFile(authPath, oauth);
22
31
  const setup = await configureCodexLb({
23
32
  home,
24
33
  configPath,
25
34
  envPath,
26
- host: 'https://lb.example.test/backend-api/codex',
35
+ host: remoteBaseUrl,
27
36
  apiKey: 'fixture-codex-lb-fast-key',
28
37
  writeEnvFile: true,
29
- useDefaultProvider: true,
30
- forceFastMode: true,
31
- forceCodexLbApiKeyAuth: true,
32
- authMode: 'codex-lb',
33
38
  shellProfile: 'skip',
39
+ gatewayAuthTransport: 'x-codex-lb-api-key',
34
40
  toolOutputRecoveryFetch
35
41
  });
36
- const fastOn = await ensureGlobalCodexFastModeDuringInstall({ home, configPath, forceFastMode: true });
37
- const first = assertFastProfile(await fs.readFile(configPath, 'utf8'), 'setup_fast_on');
38
- const release = await releaseCodexLbAuthHold({ home, configPath, authPath, backupPath: oauthBackupPath });
39
- const afterOauth = assertFastProfile(await fs.readFile(configPath, 'utf8'), 'use_oauth_roundtrip');
40
- const repair = await repairCodexLbAuth({ home, configPath, envPath, forceCodexLbApiKeyAuth: true, forceFastMode: true, authMode: 'codex-lb', toolOutputRecoveryFetch });
42
+ const native = await configureCodexLbDesktopRouting({
43
+ mode: 'desktop-native-bridge',
44
+ home,
45
+ configPath,
46
+ authPath,
47
+ bridgeBaseUrl,
48
+ remoteBaseUrl,
49
+ gatewayAuthTransport: 'x-codex-lb-api-key'
50
+ });
51
+ const fastOn = await ensureGlobalCodexFastModeDuringInstall({
52
+ home,
53
+ configPath,
54
+ forceFastMode: true
55
+ });
56
+ const first = assertFastProfile(await fs.readFile(configPath, 'utf8'), 'native_fast_on');
57
+ const disabled = await configureCodexLbDesktopRouting({
58
+ mode: 'disabled',
59
+ home,
60
+ configPath,
61
+ authPath
62
+ });
63
+ const afterDisable = assertFastProfile(await fs.readFile(configPath, 'utf8'), 'routing_disabled', false);
64
+ const reenabled = await configureCodexLbDesktopRouting({
65
+ mode: 'desktop-native-bridge',
66
+ home,
67
+ configPath,
68
+ authPath,
69
+ bridgeBaseUrl,
70
+ remoteBaseUrl,
71
+ gatewayAuthTransport: 'x-codex-lb-api-key'
72
+ });
73
+ const repair = await repairCodexLbAuth({
74
+ home,
75
+ configPath,
76
+ envPath,
77
+ forceCodexLbApiKeyAuth: true,
78
+ forceFastMode: true,
79
+ authMode: 'codex-lb'
80
+ });
41
81
  await fs.writeFile(projectConfig, [
42
82
  '# SKS managed fixture',
43
83
  'default_profile = "sks-fast-high"',
@@ -52,37 +92,53 @@ await fs.writeFile(projectConfig, [
52
92
  'service_tier = "fast"',
53
93
  ''
54
94
  ].join('\n'));
55
- const split = await splitCodexProjectConfigPolicy(root, { apply: true, codexHome, configPath: projectConfig, writeReport: false });
95
+ const split = await splitCodexProjectConfigPolicy(root, {
96
+ apply: true,
97
+ codexHome,
98
+ configPath: projectConfig,
99
+ writeReport: false
100
+ });
56
101
  const structure = await repairCodexConfigStructure(configPath, { apply: true });
57
102
  const final = assertFastProfile(await fs.readFile(configPath, 'utf8'), 'after_rewriters');
58
103
  const toolCatalog = assertGpt56ToolCatalogContract();
59
- const ok = setup.ok !== false
104
+ const authPreserved = await fs.readFile(authPath, 'utf8') === oauth;
105
+ const ok = setup.ok === true
106
+ && native.ok === true
107
+ && disabled.ok === true
108
+ && reenabled.ok === true
60
109
  && !['failed', 'skipped_unsafe_rewrite', 'unparseable_config_preserved'].includes(String(fastOn.status))
61
- && release.status !== 'failed'
62
- && repair.ok !== false
110
+ && repair.ok === true
63
111
  && split.ok === true
64
112
  && structure.ok === true
113
+ && authPreserved
65
114
  && first.ok
66
- && afterOauth.ok
115
+ && afterDisable.ok
67
116
  && final.ok
68
117
  && toolCatalog.ok;
69
118
  const report = {
70
- schema: 'sks.codex-lb-gpt56-fast-profile-check.v1',
119
+ schema: 'sks.codex-lb-gpt56-fast-profile-check.v2',
71
120
  ok,
72
121
  setup_status: setup.status,
122
+ native_status: native.status,
73
123
  fast_on_status: fastOn.status,
74
- release_status: release.status,
124
+ disable_status: disabled.status,
125
+ reenable_status: reenabled.status,
75
126
  repair_status: repair.status,
76
127
  split_status: split.status || null,
77
128
  structure_status: structure.status,
78
- assertions: [first, afterOauth, final],
129
+ oauth_preserved: authPreserved,
130
+ assertions: [first, afterDisable, final],
79
131
  tool_catalog: toolCatalog,
80
- blockers: [...[first, afterOauth, final].flatMap((item) => item.blockers), ...toolCatalog.blockers]
132
+ blockers: [
133
+ ...(authPreserved ? [] : ['chatgpt_oauth_changed']),
134
+ ...[first, afterDisable, final].flatMap((item) => item.blockers),
135
+ ...toolCatalog.blockers
136
+ ]
81
137
  };
82
138
  console.log(JSON.stringify(report, null, 2));
83
139
  if (!report.ok)
84
140
  process.exitCode = 1;
85
- function assertFastProfile(text, label) {
141
+ function assertFastProfile(text, label, expectBridge = true) {
86
142
  const validation = validateCodexConfigRoundTrip(text);
87
143
  const parsed = validation.ok ? parseCodexConfigToml(text) : {};
88
144
  const provider = parsed.model_providers?.['codex-lb'] || {};
@@ -93,8 +149,14 @@ function assertFastProfile(text, label) {
93
149
  ...(parsed.user?.fast_mode === undefined ? [] : ['user_fast_mode_legacy_table_present']),
94
150
  ...(parsed.profiles?.['sks-fast-high'] === undefined ? [] : ['sks_fast_high_legacy_profile_present']),
95
151
  ...(parsed.model === undefined ? [] : ['codex_app_model_was_injected']),
96
- ...(provider.requires_openai_auth === true ? [] : ['codex_lb_requires_openai_auth_not_true']),
152
+ ...(parsed.model_provider === undefined ? [] : ['desktop_model_provider_was_selected']),
153
+ ...(provider.name === 'codex-lb' ? [] : ['codex_lb_cli_provider_name_mismatch']),
154
+ ...(provider.requires_openai_auth === false ? [] : ['codex_lb_cli_requires_openai_auth_not_false']),
155
+ ...(provider.env_key === 'CODEX_LB_API_KEY' ? [] : ['codex_lb_cli_env_key_missing']),
97
156
  ...(provider.wire_api === 'responses' ? [] : ['codex_lb_wire_api_not_responses']),
157
+ ...(expectBridge && parsed.openai_base_url === bridgeBaseUrl ? [] : expectBridge ? ['native_bridge_base_url_missing'] : []),
158
+ ...(!expectBridge && parsed.openai_base_url !== undefined ? ['disabled_routing_still_has_openai_base_url'] : []),
159
+ ...(parsed.model_catalog_json === undefined ? [] : ['native_local_catalog_must_not_be_bound']),
98
160
  ...(desktop.on ? [] : ['desktop_fast_status_off'])
99
161
  ];
100
162
  return {
@@ -102,8 +164,11 @@ function assertFastProfile(text, label) {
102
164
  ok: blockers.length === 0,
103
165
  default_profile: parsed.default_profile || null,
104
166
  model: parsed.model || null,
167
+ model_provider: parsed.model_provider || null,
168
+ openai_base_url: parsed.openai_base_url || null,
105
169
  legacy_keys: validation.legacy_keys,
106
170
  service_tier: parsed.service_tier || null,
171
+ provider_name: provider.name || null,
107
172
  provider_wire_api: provider.wire_api || null,
108
173
  provider_requires_openai_auth: provider.requires_openai_auth ?? null,
109
174
  blockers
@@ -111,36 +176,32 @@ function assertFastProfile(text, label) {
111
176
  }
112
177
  function assertGpt56ToolCatalogContract() {
113
178
  const normalized = normalizeCodexLbToolCatalog({
114
- models: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'].map(codex0144Model)
179
+ models: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'].map(codexModel)
115
180
  });
116
181
  const models = normalized.catalog.models.filter((model) => String(model.slug).startsWith('gpt-5.6-'));
117
182
  const blockers = [
118
183
  ...normalized.blockers,
119
184
  ...(normalized.schema === 'sks.codex-lb-tool-catalog.v1' ? [] : ['codex_lb_gpt56_catalog_schema_mismatch']),
185
+ ...(normalized.contract === 'codex-model-catalog-pass-through.v2' ? [] : ['codex_lb_catalog_contract_not_v2']),
120
186
  ...(normalized.tools_transport === 'full_responses' ? [] : ['codex_lb_gpt56_native_tool_transport_not_full_responses']),
121
187
  ...(normalized.patched_models.join(',') === 'gpt-5.6-luna,gpt-5.6-sol,gpt-5.6-terra' ? [] : ['codex_lb_gpt56_catalog_patch_set_incomplete']),
122
188
  ...(models.length === 3 ? [] : ['codex_lb_gpt56_catalog_model_set_incomplete']),
123
189
  ...(models.every((model) => model.use_responses_lite === false) ? [] : ['codex_lb_gpt56_responses_lite_not_disabled']),
124
- ...(models.every((model) => model.tool_mode === 'code_mode_only') ? [] : ['codex_lb_gpt56_native_tool_mode_not_preserved']),
125
- ...(models.every((model) => model.supports_parallel_tool_calls === true) ? [] : ['codex_lb_gpt56_parallel_tool_calls_not_preserved']),
126
- ...(models.every((model) => model.minimal_client_version === '0.144.5') ? [] : ['codex_lb_gpt56_codex_0144_contract_missing'])
190
+ ...(models.every((model) => model.future_unknown_field === 'preserved') ? [] : ['codex_lb_unknown_catalog_field_lost'])
127
191
  ];
128
192
  return {
129
- schema: normalized.schema,
130
193
  ok: blockers.length === 0,
131
- codex_cli_contract: '0.144.5',
132
- models: normalized.gpt56_models,
133
- patched_models: normalized.patched_models,
194
+ contract: normalized.contract,
134
195
  tools_transport: normalized.tools_transport,
135
- native_tool_mode: 'code_mode_only',
196
+ patched_models: normalized.patched_models,
136
197
  blockers
137
198
  };
138
199
  }
139
- function codex0144Model(slug) {
200
+ function codexModel(slug) {
140
201
  return {
141
202
  slug,
142
- display_name: slug.replace('gpt-', 'GPT-').replaceAll('-', ' '),
143
- supported_reasoning_levels: [{ effort: 'max', description: 'Maximum' }],
203
+ display_name: slug,
204
+ supported_reasoning_levels: [{ effort: 'medium', description: 'Balanced' }],
144
205
  shell_type: 'shell_command',
145
206
  visibility: 'list',
146
207
  supported_in_api: true,
@@ -153,7 +214,8 @@ function codex0144Model(slug) {
153
214
  experimental_supported_tools: [],
154
215
  tool_mode: 'code_mode_only',
155
216
  use_responses_lite: true,
156
- minimal_client_version: '0.144.5'
217
+ minimal_client_version: 'runtime',
218
+ future_unknown_field: 'preserved'
157
219
  };
158
220
  }
159
221
  async function toolOutputRecoveryFetch() {
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { assertGate, emitGate, importDist, root } from './gate-lib.js';
6
+ const modeIndex = process.argv.indexOf('--mode');
7
+ const mode = modeIndex >= 0 ? String(process.argv[modeIndex + 1] || '') : '';
8
+ const MODES = new Set(['contract', 'quality', 'performance', 'legacy-closure']);
9
+ assertGate(MODES.has(mode), 'context_graph_check_unknown_mode', { mode, modes: [...MODES] });
10
+ const GATE_ID = `context-graph:${mode}`;
11
+ function gitGrep(pattern, pathspecs) {
12
+ const result = spawnSync('git', ['grep', '-n', '-I', '-E', pattern, '--', ...pathspecs], {
13
+ cwd: root,
14
+ encoding: 'utf8'
15
+ });
16
+ if (result.status !== 0 && result.status !== 1) {
17
+ assertGate(false, 'context_graph_git_grep_failed', { pattern, stderr: (result.stderr || '').slice(0, 400) });
18
+ }
19
+ return String(result.stdout || '').split('\n').filter(Boolean);
20
+ }
21
+ async function runContract() {
22
+ const contracts = await importDist('core/triwiki/context-graph/contracts.js');
23
+ const ids = await importDist('core/triwiki/context-graph/ids.js');
24
+ const paths = await importDist('core/triwiki/context-graph/paths.js');
25
+ const profiles = await importDist('core/triwiki/context-graph/profiles.js');
26
+ const extractors = await importDist('core/triwiki/context-graph/extractors/index.js');
27
+ const schemaPath = path.join(root, 'schemas', 'triwiki', 'context-graph.schema.json');
28
+ assertGate(fs.existsSync(schemaPath), 'context_graph_schema_missing', { schemaPath: 'schemas/triwiki/context-graph.schema.json' });
29
+ const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
30
+ assertGate(schema.title === contracts.CONTEXT_GRAPH_SCHEMA, 'context_graph_schema_title_mismatch', { title: schema.title });
31
+ const schemaKinds = schema.definitions.node.properties.kind.enum;
32
+ const schemaEdges = schema.definitions.edge.properties.type.enum;
33
+ assertGate(JSON.stringify(schemaKinds) === JSON.stringify([...contracts.CONTEXT_GRAPH_NODE_KINDS]), 'context_graph_node_kind_drift', { schema: schemaKinds, code: [...contracts.CONTEXT_GRAPH_NODE_KINDS] });
34
+ assertGate(JSON.stringify(schemaEdges) === JSON.stringify([...contracts.CONTEXT_GRAPH_EDGE_TYPES]), 'context_graph_edge_type_drift', { schema: schemaEdges, code: [...contracts.CONTEXT_GRAPH_EDGE_TYPES] });
35
+ const first = ids.contextGraphNodeId({ kind: 'symbol', path: 'src/a.ts', symbolKind: 'function', name: 'run', startOffset: 7 });
36
+ const second = ids.contextGraphNodeId({ kind: 'symbol', path: 'src/a.ts', symbolKind: 'function', name: 'run', startOffset: 7 });
37
+ assertGate(first === second && first === 'symbol:src/a.ts#function:run@7', 'context_graph_unstable_node_id', { first, second });
38
+ const edgeA = ids.contextGraphEdgeId({ from: 'file:a.ts', to: 'file:b.ts', type: 'imports' });
39
+ const edgeB = ids.contextGraphEdgeId({ from: 'file:a.ts', to: 'file:b.ts', type: 'imports' });
40
+ assertGate(edgeA === edgeB, 'context_graph_unstable_edge_id', { edgeA, edgeB });
41
+ const comparatorSources = gitGrep('\\.localeCompare\\(', [
42
+ 'src/core/triwiki/context-graph/contracts.ts',
43
+ 'src/core/triwiki/context-graph/ids.ts',
44
+ 'src/core/triwiki/context-graph/compiler/serialize.ts'
45
+ ]);
46
+ assertGate(comparatorSources.length === 0, 'context_graph_locale_sensitive_ordering', { hits: comparatorSources });
47
+ for (const bad of ['/etc/passwd', '../outside.ts', '']) {
48
+ let threw = false;
49
+ try {
50
+ paths.normalizeGraphPath(root, bad);
51
+ }
52
+ catch {
53
+ threw = true;
54
+ }
55
+ assertGate(threw, 'context_graph_path_escape_accepted', { candidate: bad });
56
+ }
57
+ assertGate(paths.normalizeGraphPath(root, path.join(root, 'package.json')) === 'package.json', 'context_graph_path_normalization_broken');
58
+ const edgeTypes = new Set(contracts.CONTEXT_GRAPH_EDGE_TYPES);
59
+ const reachable = new Set();
60
+ for (const name of profiles.CONTEXT_GRAPH_QUERY_PROFILE_NAMES) {
61
+ const profile = profiles.contextGraphQueryProfile(name);
62
+ assertGate(profile.edges.length > 0, 'context_graph_empty_profile', { profile: name });
63
+ for (const edge of profile.edges) {
64
+ assertGate(edgeTypes.has(edge), 'context_graph_profile_unknown_edge', { profile: name, edge });
65
+ reachable.add(edge);
66
+ }
67
+ assertGate(profile.maxDepthHighRisk >= profile.maxDepth, 'context_graph_profile_depth_inverted', { profile: name });
68
+ }
69
+ const strayWeights = gitGrep('(exact_seed_bonus|edge_profile_weight|trust_bonus|stale_penalty|redundancy_penalty|token_cost_penalty)\\s*[:=]\\s*[0-9]', [
70
+ 'src/core/triwiki/context-graph',
71
+ ':!src/core/triwiki/context-graph/query/ranking-config.ts',
72
+ ':!src/core/triwiki/context-graph/**/__tests__/**'
73
+ ]);
74
+ assertGate(strayWeights.length === 0, 'context_graph_ranking_constants_duplicated', { hits: strayWeights.slice(0, 10) });
75
+ const registered = extractors.contextGraphExtractors();
76
+ assertGate(registered.length >= 3, 'context_graph_extractor_registry_incomplete', { count: registered.length });
77
+ const registeredIds = registered.map((extractor) => extractor.id).sort();
78
+ assertGate(new Set(registeredIds).size === registeredIds.length, 'context_graph_extractor_id_collision', { registeredIds });
79
+ emitGate(GATE_ID, {
80
+ mode,
81
+ node_kinds: schemaKinds.length,
82
+ edge_types: schemaEdges.length,
83
+ profiles: profiles.CONTEXT_GRAPH_QUERY_PROFILE_NAMES.length,
84
+ traversable_edge_types: reachable.size,
85
+ extractors: registeredIds
86
+ });
87
+ }
88
+ async function runBenchmark() {
89
+ const benchmark = await importDist('core/triwiki/context-graph/benchmark/index.js');
90
+ const baseline = await importDist('core/triwiki/context-graph/benchmark/adapters/baseline-lexical.js');
91
+ const candidate = await importDist('core/triwiki/context-graph/benchmark/adapters/candidate-graph.js');
92
+ return benchmark.runContextGraphBenchmark([baseline.createBaselineLexicalAdapter(), candidate.createCandidateGraphAdapter()], { root, writeReport: true });
93
+ }
94
+ function summaryOf(report, kind) {
95
+ return report.summaries.find((entry) => entry.adapterKind === kind) || null;
96
+ }
97
+ function failedFloors(report) {
98
+ return report.floors.results.filter((floor) => !floor.passed).map((floor) => ({
99
+ id: floor.id,
100
+ adapter: floor.adapterId,
101
+ observed: floor.observed,
102
+ limit: floor.limit,
103
+ detail: floor.detail.slice(0, 6)
104
+ }));
105
+ }
106
+ async function runQuality() {
107
+ const report = await runBenchmark();
108
+ assertGate(report.integrity.ok, 'context_graph_benchmark_corpus_tampered', { integrity: report.integrity });
109
+ assertGate(report.floors.ok, 'context_graph_hard_safety_floor_failed', { failed: failedFloors(report) });
110
+ const candidate = summaryOf(report, 'candidate');
111
+ assertGate(Boolean(candidate), 'context_graph_benchmark_missing_candidate');
112
+ assertGate(candidate.provenanceCoverage >= 1, 'context_graph_provenance_coverage_below_floor', { observed: candidate.provenanceCoverage });
113
+ assertGate(candidate.protectedGateRecall >= 1, 'context_graph_protected_gate_recall_below_floor', { observed: candidate.protectedGateRecall });
114
+ assertGate(candidate.conflictRecall >= 1, 'context_graph_conflict_recall_below_floor', { observed: candidate.conflictRecall });
115
+ assertGate(candidate.recallAtK >= 0.9, 'context_graph_recall_below_floor', { observed: candidate.recallAtK });
116
+ assertGate(candidate.precisionAtK >= 0.5, 'context_graph_precision_below_floor', { observed: candidate.precisionAtK });
117
+ assertGate(report.score !== null && report.score.passed, 'context_graph_composite_below_threshold', { score: report.score });
118
+ emitGate(GATE_ID, {
119
+ mode,
120
+ recall_at_k: candidate.recallAtK,
121
+ precision_at_k: candidate.precisionAtK,
122
+ protected_gate_recall: candidate.protectedGateRecall,
123
+ conflict_recall: candidate.conflictRecall,
124
+ provenance_coverage: candidate.provenanceCoverage,
125
+ composite_improvement: report.score.improvement,
126
+ floors_evaluated: report.floors.evaluated
127
+ });
128
+ }
129
+ async function runPerformance() {
130
+ const report = await runBenchmark();
131
+ assertGate(report.integrity.ok, 'context_graph_benchmark_corpus_tampered', { integrity: report.integrity });
132
+ assertGate(report.floors.ok, 'context_graph_hard_safety_floor_failed', { failed: failedFloors(report) });
133
+ const candidate = summaryOf(report, 'candidate');
134
+ const baseline = summaryOf(report, 'baseline');
135
+ assertGate(Boolean(candidate) && Boolean(baseline), 'context_graph_benchmark_missing_adapter');
136
+ const warmP95 = candidate.warmLatency.p95;
137
+ const baselineWarmP95 = baseline.warmLatency.p95;
138
+ const latencyOk = warmP95 <= 75 || (baselineWarmP95 > 0 && warmP95 <= baselineWarmP95 * 0.7);
139
+ assertGate(latencyOk, 'context_graph_warm_latency_regression', { warmP95, baselineWarmP95 });
140
+ const tokenOk = baseline.meanTokenCost === 0 || candidate.meanTokenCost <= baseline.meanTokenCost * 0.7;
141
+ assertGate(tokenOk, 'context_graph_token_cost_regression', { candidate: candidate.meanTokenCost, baseline: baseline.meanTokenCost });
142
+ assertGate(candidate.warmCacheHitRate >= 0.9, 'context_graph_cache_hit_below_floor', { observed: candidate.warmCacheHitRate });
143
+ emitGate(GATE_ID, {
144
+ mode,
145
+ warm_p95_ms: warmP95,
146
+ baseline_warm_p95_ms: baselineWarmP95,
147
+ mean_token_cost: candidate.meanTokenCost,
148
+ baseline_mean_token_cost: baseline.meanTokenCost,
149
+ warm_cache_hit_rate: candidate.warmCacheHitRate
150
+ });
151
+ }
152
+ function runLegacyClosure() {
153
+ const excluded = [':!CHANGELOG.md', ':!docs/work-orders', ':!.sneakoscope', ':!src/scripts/context-graph-check.ts'];
154
+ const checks = [
155
+ { id: 'triwiki_codepack_local', pattern: 'triwiki_codepack_local', pathspecs: ['.', ...excluded] },
156
+ { id: 'simple_counts_comment', pattern: 'simple counts avoid needing a real dependency graph', pathspecs: ['.', ...excluded] },
157
+ { id: 'scanCodebaseIndex', pattern: '\\bscanCodebaseIndex\\b', pathspecs: ['.', ...excluded] },
158
+ { id: 'code_index_scanner_module', pattern: 'code-index-scanner', pathspecs: ['src', 'package.json', 'release-gates.v2.json', 'infra-harness-gates.json', 'runtime-required-scripts.json', ':!src/scripts/context-graph-check.ts'] },
159
+ {
160
+ id: 'attentionRelevance',
161
+ pattern: '\\battentionRelevance\\b',
162
+ pathspecs: ['.', ...excluded, ':!src/core/subagents/__tests__']
163
+ },
164
+ {
165
+ id: 'lexical_fallback_in_context_mode',
166
+ pattern: '\\b(searchFilesJs|searchTextJs)\\b',
167
+ pathspecs: ['src/core/search/context.ts']
168
+ }
169
+ ];
170
+ const violations = [];
171
+ for (const check of checks) {
172
+ const hits = gitGrep(check.pattern, check.pathspecs);
173
+ if (hits.length)
174
+ violations.push({ id: check.id, hits: hits.slice(0, 6) });
175
+ }
176
+ assertGate(violations.length === 0, 'context_graph_legacy_path_survives', { violations });
177
+ for (const retired of ['src/core/triwiki/code-index-scanner.ts']) {
178
+ assertGate(!fs.existsSync(path.join(root, retired)), 'context_graph_retired_file_present', { path: retired });
179
+ }
180
+ const parked = gitGrep('code-index-scanner', ['src', ':!src/core/triwiki/context-graph', ':!src/scripts/context-graph-check.ts']);
181
+ assertGate(parked.length === 0, 'context_graph_retired_file_parked', { hits: parked.slice(0, 6) });
182
+ emitGate(GATE_ID, { mode, checks: checks.length, retired_modules: 1 });
183
+ }
184
+ if (mode === 'contract')
185
+ await runContract();
186
+ else if (mode === 'quality')
187
+ await runQuality();
188
+ else if (mode === 'performance')
189
+ await runPerformance();
190
+ else
191
+ runLegacyClosure();
@@ -12,7 +12,7 @@ const codexHome = path.join(root, 'home', '.codex');
12
12
  await fs.mkdir(path.join(root, '.codex'), { recursive: true });
13
13
  await fs.mkdir(codexHome, { recursive: true });
14
14
  await fs.writeFile(path.join(root, '.codex', 'config.toml'), 'model = "future-codex-model"\nmodel_reasoning_effort = "medium"\nmodel_provider = "codex-lb"\n');
15
- await fs.writeFile(path.join(codexHome, 'config.toml'), '# SKS forced fast UI during legacy install\nmodel = "legacy-sks-model"\nmodel_reasoning_effort = "xhigh"\nmodel_provider = "codex-lb"\nservice_tier = "fast"\n[features]\nfast_mode = false # user disabled, must remain untouched\n\n[profiles.sks-fast-high]\nmodel = "legacy-profile-model"\nservice_tier = "fast"\n\n[model_providers.codex-lb]\nname = "openai"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = true\n');
15
+ await fs.writeFile(path.join(codexHome, 'config.toml'), '# SKS forced fast UI during legacy install\nmodel = "legacy-sks-model"\nmodel_reasoning_effort = "xhigh"\nmodel_provider = "codex-lb"\nservice_tier = "fast"\n[features]\nfast_mode = false # user disabled, must remain untouched\n\n[profiles.sks-fast-high]\nmodel = "legacy-profile-model"\nservice_tier = "fast"\n\n[model_providers.codex-lb]\nname = "codex-lb"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = false\n');
16
16
  const fixtureEnv = {};
17
17
  const plan = await repairCodexAppFastUi(root, { codexHome, apply: false, env: fixtureEnv });
18
18
  const repaired = await repairCodexAppFastUi(root, { codexHome, apply: true, env: fixtureEnv });
@@ -44,7 +44,7 @@ if (releaseGates.length > 200)
44
44
  blockers.push(`release_preset_gate_budget_exceeded:${releaseGates.length}`);
45
45
  if (gates.length > 200)
46
46
  blockers.push(`release_manifest_gate_budget_exceeded:${gates.length}`);
47
- if (Object.keys(pkg.scripts || {}).length > 100)
47
+ if (Object.keys(pkg.scripts || {}).length > 101)
48
48
  blockers.push(`package_script_budget_exceeded:${Object.keys(pkg.scripts || {}).length}`);
49
49
  if (releaseGates.some((gate) => String(gate.id || '').startsWith('zellij:')))
50
50
  blockers.push('zellij_gate_in_release_preset');
@@ -9,9 +9,7 @@ const BASELINE = {
9
9
  emitGate: 10,
10
10
  sleep: 9,
11
11
  delay: 8,
12
- unique: 7,
13
12
  readJson: 6,
14
- errorCode: 5,
15
13
  restoreEnv: 5,
16
14
  asList: 4,
17
15
  escapeRegex: 4,
@@ -14,7 +14,10 @@ emitGate('runtime:installed-smoke', {
14
14
  tarball: report.tarball,
15
15
  tarball_sha256: report.tarball_sha256,
16
16
  exact_match: report.tarball_binding.exact_match,
17
- install_prefix: report.install_prefix
17
+ install_prefix: report.install_prefix,
18
+ postinstall_external_snapshot_match: report.postinstall_default.external_snapshot_match,
19
+ postinstall_launchctl_calls: report.postinstall_default.launchctl_calls.length,
20
+ postinstall_package_local_stamp_present: report.postinstall_default.package_local_stamp_present
18
21
  });
19
22
  function option(name) {
20
23
  const index = process.argv.indexOf(name);
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { assertGate, emitGate, importDist, root } from './gate-lib.js';
5
+ const { scanLatestVersionGuidance, latestVersionGuidanceReport } = await importDist('core/policy/latest-version-guidance.js');
6
+ const DOC_EXEMPT_RE = /(?:^|\/)(?:CHANGELOG|RECALLPULSE_[^/]*|known-gaps|release-proof-truth)\.md$|compat|codex-0|migration|legacy-upgrade|sks-4-migration|-tasks\.md$/i;
7
+ const SOURCE_EXEMPT = new Set([
8
+ 'src/core/codex-compat/codex-release-manifest.ts',
9
+ 'src/core/release/npm-stage-tarball-verifier-support.ts'
10
+ ]);
11
+ const SOURCE_EXEMPT_DIR_RE = /^src\/(?:scripts|vendor|generated)\//;
12
+ function walk(dir, accept, out = []) {
13
+ let entries = [];
14
+ try {
15
+ entries = fs.readdirSync(path.join(root, dir), { withFileTypes: true });
16
+ }
17
+ catch {
18
+ return out;
19
+ }
20
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
21
+ const rel = `${dir}/${entry.name}`;
22
+ if (entry.isDirectory()) {
23
+ if (['node_modules', 'dist', '.git', '__tests__', 'fixtures'].includes(entry.name))
24
+ continue;
25
+ walk(rel, accept, out);
26
+ }
27
+ else if (accept(rel)) {
28
+ out.push(rel);
29
+ }
30
+ }
31
+ return out;
32
+ }
33
+ const exemptions = [];
34
+ const findings = [];
35
+ let scannedFiles = 0;
36
+ function scan(rel, options) {
37
+ const absolute = path.join(root, rel);
38
+ if (!fs.existsSync(absolute))
39
+ return;
40
+ scannedFiles += 1;
41
+ findings.push(...scanLatestVersionGuidance(rel, fs.readFileSync(absolute, 'utf8'), options));
42
+ }
43
+ scan('README.md', {});
44
+ for (const rel of walk('docs', (candidate) => candidate.endsWith('.md') && path.dirname(candidate) === 'docs')) {
45
+ if (DOC_EXEMPT_RE.test(rel)) {
46
+ exemptions.push(rel);
47
+ continue;
48
+ }
49
+ scan(rel, {});
50
+ }
51
+ for (const rel of walk('src', (candidate) => candidate.endsWith('.ts'))) {
52
+ if (SOURCE_EXEMPT.has(rel) || SOURCE_EXEMPT_DIR_RE.test(rel)) {
53
+ exemptions.push(rel);
54
+ continue;
55
+ }
56
+ scan(rel, { stringLiteralsOnly: true });
57
+ }
58
+ for (const rel of walk('native', (candidate) => candidate.endsWith('.swift'))) {
59
+ scan(rel, { stringLiteralsOnly: true });
60
+ }
61
+ const report = latestVersionGuidanceReport(findings, scannedFiles, exemptions);
62
+ const outDir = path.join(root, '.sneakoscope', 'reports');
63
+ fs.mkdirSync(outDir, { recursive: true });
64
+ fs.writeFileSync(path.join(outDir, 'latest-version-guidance.json'), `${JSON.stringify(report, null, 2)}\n`);
65
+ assertGate(report.ok, 'user_facing_pinned_version_guidance', {
66
+ findings: report.findings.slice(0, 40),
67
+ total: report.findings.length,
68
+ policy: 'Recommend the official latest stable release and let capability probes decide support. See docs/architecture/latest-version-guidance-policy.md.'
69
+ });
70
+ emitGate('latest-version:guidance', {
71
+ scanned_files: report.scannedFiles,
72
+ exempt_files: report.exemptions.length,
73
+ findings: 0
74
+ });
@@ -111,7 +111,7 @@ assertGate(releaseManifest?.schema === 'sks.release-gates.v2', 'release gate man
111
111
  assertGate(harnessManifest?.schema === 'sks.infra-harness-gates.v1', 'infra harness manifest schema mismatch', { schema: harnessManifest?.schema || null });
112
112
  assertGate(releaseGates.length > 0 && releaseGates.length <= 200, 'release manifest must include 1..200 release gates', { release_gates: releaseGates.length });
113
113
  assertGate(harnessGates.length > 0, 'infra harness manifest must include harness gates', { harness_gates: harnessGates.length });
114
- assertGate(Object.keys(pkg.scripts || {}).length <= 100, 'package script budget exceeded', { script_count: Object.keys(pkg.scripts || {}).length, limit: 100 });
114
+ assertGate(Object.keys(pkg.scripts || {}).length <= 101, 'package script budget exceeded', { script_count: Object.keys(pkg.scripts || {}).length, limit: 101 });
115
115
  for (const script of requiredPackageScripts)
116
116
  assertGate(Boolean(pkg.scripts?.[script]), `missing package script: ${script}`);
117
117
  const fullReleaseScript = String(pkg.scripts?.['release:check:full'] || '');
@@ -149,15 +149,23 @@ assertGate(currentDollarManifest.includes("{ command: '$Naruto'") && currentDoll
149
149
  const stageWorkflow = text('.github/workflows/publish-npm.yml');
150
150
  const stageJob = stageWorkflow.match(/^ stage-publish:\n[\s\S]*$/m)?.[0] || '';
151
151
  const workflowPermissions = stageWorkflow.match(/^permissions:\n(?: [^\n]+\n)+/m)?.[0] || '';
152
+ const stageContract = text('src/core/release/npm-stage-contract.ts');
153
+ const stagePublishSource = text('src/core/release/stage-publish.ts');
152
154
  const stageVerifierSource = text('src/core/release/npm-stage-tarball-verifier.ts');
153
155
  const stageVerifierSupport = text('src/core/release/npm-stage-tarball-verifier-support.ts');
154
156
  const stageVerifierCli = text('src/scripts/npm-stage-tarball-verifier.ts');
155
157
  const releaseReadinessDoc = text('docs/release-readiness.md');
158
+ const workflowNpmStageVersion = stageWorkflow.match(/NPM_STAGE_CLI_VERSION:\s*([0-9]+\.[0-9]+\.[0-9]+)/)?.[1] || '';
159
+ const localNpmStageVersion = stageContract.match(/REQUIRED_NPM_STAGE_CLI_VERSION = '([0-9]+\.[0-9]+\.[0-9]+)'/)?.[1] || '';
156
160
  for (const job of ['linux-release-proof', 'macos-menubar-proof', 'pack-and-compare', 'stage-publish']) {
157
161
  assertGate(new RegExp(`^ ${job}:`, 'm').test(stageWorkflow), `stage workflow missing job: ${job}`);
158
162
  }
159
163
  assertGate(/npm install --global npm@\$\{NPM_STAGE_CLI_VERSION\}/.test(stageWorkflow), 'stage workflow must install the exact pinned npm CLI');
160
- assertGate(/NPM_STAGE_CLI_VERSION: 11\.15\.0/.test(stageWorkflow), 'stage workflow must pin npm 11.15.0');
164
+ assertGate(workflowNpmStageVersion === '11.15.0', 'stage workflow must pin npm 11.15.0', { version: workflowNpmStageVersion || null });
165
+ assertGate(localNpmStageVersion === workflowNpmStageVersion, 'local and workflow npm stage CLI versions must match', {
166
+ local_version: localNpmStageVersion || null,
167
+ workflow_version: workflowNpmStageVersion || null
168
+ });
161
169
  assertGate(/npm stage publish "\$TARBALL"/.test(stageWorkflow), 'stage workflow must stage the exact tarball path');
162
170
  assertGate((stageWorkflow.match(/npm stage publish "\$TARBALL"/g) || []).length === 1, 'stage workflow must contain exactly one registry mutation');
163
171
  assertGate(!/\bnpm\s+publish\b/.test(stageWorkflow), 'stage workflow must not call direct npm publish');
@@ -177,15 +185,24 @@ assertGate(/tarball_integrity: sha512Integrity/.test(stageJob), 'stage receipt m
177
185
  assertGate(/stage-receipt\/stage-output\.json/.test(stageJob) && /path: stage-receipt/.test(stageJob), 'stage receipt artifact must preserve digest-bound raw stage output');
178
186
  assertGate(/review_verifier_schema: 'sks\.npm-stage-review-receipt\.v1'/.test(stageJob), 'stage receipt must declare the maintainer verifier schema');
179
187
  assertGate(/localPackReceiptSha256 = crypto\.createHash\('sha256'\)\.update\(localPackReceiptBytes\)/.test(stageJob), 'stage receipt must bind immutable local pack receipt bytes');
188
+ assertGate(/smoke\.postinstall_default\?\.scripts_enabled !== true/.test(stageJob), 'stage job must require scripts-enabled default postinstall proof');
189
+ assertGate(/smoke\.postinstall_default\?\.external_snapshot_match !== true/.test(stageJob), 'stage job must require zero external postinstall mutation');
190
+ assertGate(/smoke\.postinstall_default\.launchctl_calls\.length !== 0/.test(stageJob), 'stage job must require zero default postinstall launchctl calls');
180
191
  assertGate(!/approve_command/.test(stageWorkflow), 'stage workflow must not serialize an approval command');
181
192
  assertGate(!/\bnpm[ \t]+(?:ci|pack|run|publish|login|logout|whoami)\b/.test(stageJob), 'stage job must not install dependencies, repack, run lifecycle scripts, or use session credentials');
182
193
  assertGate(!/NODE_AUTH_TOKEN|NPM_TOKEN|_authToken/.test(stageWorkflow), 'stage workflow must not inject npm tokens');
183
- assertGate(/REQUIRED_NPM_STAGE_CLI_VERSION = '11\.15\.0'/.test(stageVerifierSupport), 'maintainer stage verifier must require exact npm 11.15.0');
194
+ assertGate(/from '\.\/npm-stage-contract\.js'/.test(stageVerifierSupport), 'maintainer stage verifier support must use the shared npm stage contract');
195
+ assertGate(/exactNpmStageCliInvocation\(\)/.test(stageVerifierSource), 'maintainer stage verifier must resolve the exact pinned npm CLI');
184
196
  assertGate(/\['stage', 'view', stageId, '--json'/.test(stageVerifierSource), 'maintainer stage verifier must inspect the exact stage ID read-only');
185
197
  assertGate(/\['stage', 'download', stageId, '--json'/.test(stageVerifierSource), 'maintainer stage verifier must download the exact stage ID read-only');
186
198
  assertGate(/exact_bytes_match/.test(stageVerifierSource) && /sha256_match/.test(stageVerifierSource) && /sha512_match/.test(stageVerifierSource) && /integrity_match/.test(stageVerifierSource), 'maintainer stage verifier must compare bytes and digests');
187
- assertGate(/oidc_environment_not_allowed/.test(stageVerifierSupport), 'maintainer stage verifier must reject OIDC and GitHub Actions environments');
199
+ assertGate(/oidc_environment_not_allowed/.test(stageContract), 'maintainer stage verifier must reject OIDC and GitHub Actions environments');
200
+ assertGate(/local_review_verifier/.test(stagePublishSource), 'confirmed stage preflight must require the local review verifier');
201
+ assertGate(/localNpmStageReviewEnvironmentBlocker/.test(stagePublishSource), 'confirmed stage preflight must reject CI and OIDC review environments');
202
+ assertGate(/exactNpmStageCliInvocation\(\)/.test(stagePublishSource), 'confirmed stage preflight must resolve the pinned npm CLI before mutation');
203
+ assertGate(/if \(!preflight\.ok\) return finish\(\)/.test(stagePublishSource), 'stage preflight failures must stop before push or dispatch');
188
204
  assertGate(!/\['stage',\s*'(?:publish|approve|reject)'/.test(`${stageVerifierSource}\n${stageVerifierSupport}\n${stageVerifierCli}`), 'maintainer stage verifier must not contain mutating stage argv');
205
+ assertGate(/npx --yes npm@11\.15\.0/.test(releaseReadinessDoc), 'release readiness must document pinned local npm stage resolution');
189
206
  assertGate(/npm-stage-tarball-verifier\.js/.test(releaseReadinessDoc) && /--local-receipt/.test(releaseReadinessDoc) && /--local-tarball/.test(releaseReadinessDoc) && /--stage-receipt/.test(releaseReadinessDoc), 'release readiness must document the maintainer-local read-only verifier inputs');
190
207
  assertGate(pkg.scripts?.prepublishOnly === 'node ./dist/scripts/prepublish-release-check-or-fast.js', 'prepublishOnly must verify release proof during official npm publish');
191
208
  assertGate(pkg.scripts?.prepack === 'node ./dist/scripts/prepublish-release-check-or-fast.js --prepack-build', 'prepack must rebuild and reverify official npm publish output');
@@ -102,6 +102,13 @@ const tasks = [
102
102
  task('zellij:real-session-cleanup', 'direct', { command: nodeScript('zellij-real-session-cleanup-check.js'), group: 'real_ui', phase: 'aggregation', args: ['--mission', zellijMissionId, '--session', zellijSessionName, '--owned-socket-dir', zellijSocketDir], env: zellijOwnedEnv, deps: ['zellij:pane-proof', 'zellij:screen-proof'], alwaysRun: true, policy: requiredPolicy(['sks.zellij-real-session-cleanup-check.v1']) }),
103
103
  task('naruto:worktree-coding:blackbox', 'direct', { command: nodeScript('naruto-worktree-coding-blackbox.js'), group: 'real_smoke', phase: 'parallel_processing', args: ['--require-real'], env: { SKS_REQUIRE_GIT_WORKTREE: '1' }, policy: requiredPolicy(['sks.release-gate.v1']) }),
104
104
  task('codex-sdk:real-smoke', 'direct', { command: nodeScript('codex-sdk-real-smoke-check.js'), group: 'real_smoke', phase: 'parallel_processing', args: ['--require-real'], policy: requiredPolicy(['sks.release-gate.v1'], { statusRequired: true, passStatuses: ['proven'] }) }),
105
+ task('codex-lb:desktop-real-evidence', 'direct', {
106
+ command: nodeScript('codex-lb-desktop-real-evidence-check.js'),
107
+ group: 'real_ui',
108
+ phase: 'parallel_verification',
109
+ deps: ['codex:actual-config-load-probe'],
110
+ policy: liveOptionalPolicy(['sks.codex-lb-desktop-real-evidence-check.v1'], ['passed'])
111
+ }),
105
112
  task('imagegen:real-smoke', 'direct', { command: nodeScript('imagegen-real-smoke-check.js'), group: 'real_smoke', phase: 'parallel_processing', policy: liveOptionalPolicy(['sks.imagegen-real-smoke.v1'], ['passed']) }),
106
113
  task('ux-review:real-imagegen-smoke', 'direct', { command: nodeScript('ux-review-real-imagegen-smoke-check.js'), group: 'real_smoke', phase: 'parallel_processing', deps: ['imagegen:real-smoke'], policy: liveOptionalPolicy(['sks.ux-real-imagegen-smoke.v1'], ['passed']) }),
107
114
  task('ppt:real-imagegen-smoke', 'direct', { command: nodeScript('ppt-real-imagegen-smoke-check.js'), group: 'real_smoke', phase: 'parallel_processing', deps: ['imagegen:real-smoke'], policy: liveOptionalPolicy(['sks.ppt-real-imagegen-smoke.v1'], ['passed']) })