sneakoscope 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (534) hide show
  1. package/README.md +9 -1
  2. package/config/architecture-budgets.v1.json +1 -8
  3. package/crates/sks-core/Cargo.lock +225 -1
  4. package/crates/sks-core/Cargo.toml +8 -1
  5. package/crates/sks-core/src/main.rs +7 -1
  6. package/crates/sks-core/src/search/files.rs +114 -0
  7. package/crates/sks-core/src/search/mod.rs +274 -0
  8. package/crates/sks-core/src/search/text.rs +175 -0
  9. package/dist/bin/sks-dispatch.js +4 -3
  10. package/dist/cli/command-manifest-lite.js +177 -6
  11. package/dist/cli/command-registry.js +42 -8
  12. package/dist/cli/help.js +30 -0
  13. package/dist/cli/install-helpers-codex-lb-config.js +9 -6
  14. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +1 -1
  15. package/dist/cli/install-helpers-install-support.js +1 -1
  16. package/dist/cli/install-helpers.js +19 -9
  17. package/dist/cli/router.js +16 -3
  18. package/dist/commands/codex-app.js +40 -5
  19. package/dist/commands/doctor-profile.js +78 -0
  20. package/dist/commands/doctor.js +29 -69
  21. package/dist/commands/search.js +4 -0
  22. package/dist/config/skills-manifest.json +53 -60
  23. package/dist/core/agent-bridge/agent-manifest.js +22 -26
  24. package/dist/core/agent-bridge/host-capability-policy.js +109 -8
  25. package/dist/core/agent-bridge/host-capability-runtime.js +178 -50
  26. package/dist/core/agent-guidance/directive-registry.js +123 -0
  27. package/dist/core/agents/agent-effort-policy.js +45 -6
  28. package/dist/core/agents/agent-worker-pipeline.js +1 -3
  29. package/dist/core/agents/native-worker-backend-router.js +43 -19
  30. package/dist/core/auto-review.js +1 -3
  31. package/dist/core/code-structure.js +282 -19
  32. package/dist/core/codex/codex-config-guard.js +14 -4
  33. package/dist/core/codex/codex-config-toml.js +1 -3
  34. package/dist/core/codex-app/codex-agent-role-sync.js +1 -3
  35. package/dist/core/codex-app/codex-app-fast-ui-repair.js +14 -1
  36. package/dist/core/codex-app/codex-app-types.js +2 -3
  37. package/dist/core/codex-app/codex-desktop-routing-ownership.js +91 -0
  38. package/dist/core/codex-app/codex-hook-approval-probe.js +1 -3
  39. package/dist/core/codex-app/codex-hook-lifecycle.js +1 -3
  40. package/dist/core/codex-app/codex-init-deep.js +22 -10
  41. package/dist/core/codex-app/codex-model-catalog.js +384 -0
  42. package/dist/core/codex-app/codex-models-cache.js +160 -0
  43. package/dist/core/codex-app/desktop-routing-snapshot.js +233 -0
  44. package/dist/core/codex-app/glm-profile-installer.js +1 -3
  45. package/dist/core/codex-app/mcp-manager.js +1 -3
  46. package/dist/core/codex-app/menubar/constants.js +3 -0
  47. package/dist/core/codex-app/menubar/installer.js +11 -1
  48. package/dist/core/codex-app/multi-provider-router-support.js +202 -0
  49. package/dist/core/codex-app/multi-provider-router.js +379 -0
  50. package/dist/core/codex-app/openrouter-activate.js +189 -26
  51. package/dist/core/codex-app/openrouter-base-instructions.js +1 -0
  52. package/dist/core/codex-app/openrouter-model-catalog.js +217 -0
  53. package/dist/core/codex-app/role-model-catalog-priority.js +61 -0
  54. package/dist/core/codex-app.js +30 -6
  55. package/dist/core/codex-compat/codex-hook-semantic-validator.js +1 -3
  56. package/dist/core/codex-control/codex-app-server-v2-client.js +39 -2
  57. package/dist/core/codex-lb/codex-lb-env.js +1 -3
  58. package/dist/core/codex-lb/codex-lb-tool-catalog.js +6 -1
  59. package/dist/core/codex-native/codex-native-feature-broker.js +2 -6
  60. package/dist/core/codex-native/codex-native-interop-policy.js +1 -3
  61. package/dist/core/codex-native/codex-native-reference-cache.js +1 -3
  62. package/dist/core/codex-native/codex-native-reference-source.js +1 -3
  63. package/dist/core/codex-native/codex-native-repair-transaction.js +1 -3
  64. package/dist/core/codex-native/core-skill-manifest.js +3 -2
  65. package/dist/core/codex-native/native-capability-repair-matrix.js +1 -3
  66. package/dist/core/codex-plugins/codex-plugin-repair.js +1 -3
  67. package/dist/core/codex-runtime/codex-desktop-config-policy.js +4 -4
  68. package/dist/core/commands/agent-bridge-command.js +24 -3
  69. package/dist/core/commands/image-ux-review-command.js +5 -11
  70. package/dist/core/commands/menubar-command.js +1 -1
  71. package/dist/core/commands/naruto-command.js +40 -13
  72. package/dist/core/commands/pipeline-command.js +5 -2
  73. package/dist/core/commands/release-command.js +63 -1
  74. package/dist/core/commands/remote-command.js +18 -0
  75. package/dist/core/commands/search-command.js +151 -0
  76. package/dist/core/commands/telegram-command.js +251 -108
  77. package/dist/core/commands/uninstall-command.js +1 -1
  78. package/dist/core/config/secret-preservation.js +1 -3
  79. package/dist/core/context7-client.js +2 -1
  80. package/dist/core/db-review.js +704 -0
  81. package/dist/core/db-safety.js +17 -23
  82. package/dist/core/doctor/browser-use-repair.js +1 -3
  83. package/dist/core/doctor/codex-startup-config-repair.js +1 -3
  84. package/dist/core/doctor/computer-use-repair.js +1 -3
  85. package/dist/core/doctor/context7-mcp-repair.js +1 -3
  86. package/dist/core/doctor/current-project-guidance.js +1 -3
  87. package/dist/core/doctor/doctor-codex-startup-repair.js +1 -3
  88. package/dist/core/doctor/doctor-dirty-planner.js +2 -2
  89. package/dist/core/doctor/doctor-native-capability-repair.js +1 -3
  90. package/dist/core/doctor/doctor-transaction.js +1 -3
  91. package/dist/core/doctor/imagegen-repair.js +1 -3
  92. package/dist/core/doctor/legacy-global-hook-cleanup.js +1 -3
  93. package/dist/core/doctor/mcp-transport-collision-repair.js +1 -3
  94. package/dist/core/doctor/retired-managed-residue-private.js +1 -3
  95. package/dist/core/doctor/supabase-mcp-repair.js +1 -3
  96. package/dist/core/engineering-sanity-review.js +186 -0
  97. package/dist/core/errors/message.js +3 -0
  98. package/dist/core/evidence/evidence-schema.js +1 -3
  99. package/dist/core/feature-fixtures.js +1 -0
  100. package/dist/core/feature-registry.js +1 -3
  101. package/dist/core/fsx.js +13 -0
  102. package/dist/core/git-hygiene/shared-memory-publish.js +1 -3
  103. package/dist/core/git-hygiene/validators.js +1 -3
  104. package/dist/core/harness-guard.js +21 -3
  105. package/dist/core/hooks-runtime/hook-context.js +2 -2
  106. package/dist/core/image-ux-review/imagegen-adapter.js +52 -56
  107. package/dist/core/image-ux-review/real-callout-extractor.js +6 -1
  108. package/dist/core/image-ux-review.js +3 -5
  109. package/dist/core/imagegen/codex-lb-imagegen-target.js +70 -0
  110. package/dist/core/imagegen/imagegen-capability.js +11 -11
  111. package/dist/core/imagegen/imagegen-evidence.js +32 -0
  112. package/dist/core/init/skills.js +9 -11
  113. package/dist/core/init.js +4 -6
  114. package/dist/core/json/records.js +9 -0
  115. package/dist/core/json-schema-validator.js +1 -3
  116. package/dist/core/lean-engineering-policy.js +54 -0
  117. package/dist/core/loops/loop-continuation-enforcer.js +1 -3
  118. package/dist/core/mad-sks/sql-plane/mcp-executor.js +2 -1
  119. package/dist/core/mad-sks/sql-plane/policy.js +2 -1
  120. package/dist/core/managed-assets/managed-assets-manifest.js +4 -3
  121. package/dist/core/mcp-config/codex-cli-adapter.js +1 -3
  122. package/dist/core/mcp-config/config-reader.js +2 -3
  123. package/dist/core/mcp-config/guarded-patch.js +1 -1
  124. package/dist/core/mcp-config/health-check.js +2 -1
  125. package/dist/core/mcp-config/mutation.js +1 -1
  126. package/dist/core/mcp-config/restore.js +1 -1
  127. package/dist/core/mcp-config/secret-policy.js +1 -3
  128. package/dist/core/migration/migration-transaction-journal.js +3 -3
  129. package/dist/core/naruto/naruto-concurrency-governor.js +2 -2
  130. package/dist/core/perf/import-graph-budget.js +91 -1
  131. package/dist/core/pipeline/pipeline-plan-writer.js +1 -1
  132. package/dist/core/pipeline-internals/runtime-core.js +121 -23
  133. package/dist/core/pipeline-internals/runtime-gates.js +193 -4
  134. package/dist/core/pipeline.js +1 -1
  135. package/dist/core/ppt-review/slide-imagegen-review.js +7 -7
  136. package/dist/core/ppt.js +3 -3
  137. package/dist/core/product-design-app-server.js +2 -1
  138. package/dist/core/proof/fake-real-proof-policy.js +1 -3
  139. package/dist/core/proof/root-cause-policy.js +1 -3
  140. package/dist/core/proof/validation.js +1 -3
  141. package/dist/core/provider/model-router.js +4 -0
  142. package/dist/core/provider/provider-context.js +1 -3
  143. package/dist/core/providers/openrouter/openrouter-account.js +1 -3
  144. package/dist/core/quality/helper-dedup.js +0 -0
  145. package/dist/core/recallpulse.js +1 -1
  146. package/dist/core/release/file-ownership.js +26 -17
  147. package/dist/core/release/macos-menubar-proof.js +59 -1
  148. package/dist/core/release/main-push-guard.js +288 -24
  149. package/dist/core/release/main-push-receipt.js +10 -45
  150. package/dist/core/release/package-size-budget.js +1 -1
  151. package/dist/core/release/release-gate-contract.js +2 -0
  152. package/dist/core/release/release-pack-receipt.js +1 -1
  153. package/dist/core/release/release-proof-io.js +33 -0
  154. package/dist/core/release/release-upgrade-baseline.js +9 -0
  155. package/dist/core/release/runtime-script-pack-closure.js +153 -4
  156. package/dist/core/release/stage-publish.js +194 -0
  157. package/dist/core/remote/index.js +2 -0
  158. package/dist/core/remote/local-worker-client.js +35 -0
  159. package/dist/core/remote/machine-registry.js +18 -13
  160. package/dist/core/remote/owner-proof.js +1 -3
  161. package/dist/core/remote/protocol.js +1 -3
  162. package/dist/core/remote/readiness.js +1 -3
  163. package/dist/core/remote/session-binding.js +153 -0
  164. package/dist/core/remote/session-index.js +1 -3
  165. package/dist/core/remote/types.js +1 -0
  166. package/dist/core/remote/worker.js +228 -14
  167. package/dist/core/responses-stream.js +151 -0
  168. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  169. package/dist/core/routes.js +8 -8
  170. package/dist/core/rust-accelerator.js +12 -2
  171. package/dist/core/safety/ssot-guard.js +1 -3
  172. package/dist/core/search/benchmark.js +358 -0
  173. package/dist/core/search/cache.js +65 -0
  174. package/dist/core/search/context.js +144 -0
  175. package/dist/core/search/doctor.js +74 -0
  176. package/dist/core/search/files.js +192 -0
  177. package/dist/core/search/index.js +5 -0
  178. package/dist/core/search/ipc.js +64 -0
  179. package/dist/core/search/provider.js +142 -0
  180. package/dist/core/search/rust-bridge.js +24 -0
  181. package/dist/core/search/structure.js +230 -0
  182. package/dist/core/search/symbol.js +272 -0
  183. package/dist/core/search/text.js +146 -0
  184. package/dist/core/search/types.js +22 -0
  185. package/dist/core/search/typescript-ls.js +215 -0
  186. package/dist/core/search-visibility/discovery.js +1 -3
  187. package/dist/core/skill-forge.js +1 -1
  188. package/dist/core/stop-gate/gate-evaluator.js +3 -3
  189. package/dist/core/stop-gate/stop-gate-check.js +1 -3
  190. package/dist/core/structured-output-adapter.js +62 -2
  191. package/dist/core/subagents/agent-catalog.js +56 -43
  192. package/dist/core/subagents/model-policy.js +17 -7
  193. package/dist/core/subagents/naruto-proof-projection.js +2 -6
  194. package/dist/core/subagents/official-subagent-config.js +1 -3
  195. package/dist/core/subagents/official-subagent-preparation.js +73 -10
  196. package/dist/core/subagents/official-subagent-prompt.js +85 -18
  197. package/dist/core/subagents/official-subagent-runner.js +10 -5
  198. package/dist/core/subagents/role-model-preferences.js +202 -44
  199. package/dist/core/subagents/subagent-evidence.js +1 -3
  200. package/dist/core/subagents/thread-budget.js +1 -1
  201. package/dist/core/subagents/triwiki-attention.js +1 -5
  202. package/dist/core/telegram/config.js +29 -12
  203. package/dist/core/telegram/hub.js +8 -2
  204. package/dist/core/telegram/index.js +2 -0
  205. package/dist/core/telegram/messages.js +8 -2
  206. package/dist/core/telegram/runtime-projection.js +8 -5
  207. package/dist/core/telegram/runtime.js +19 -9
  208. package/dist/core/telegram/service.js +181 -0
  209. package/dist/core/telegram/setup.js +220 -0
  210. package/dist/core/text/regex.js +3 -0
  211. package/dist/core/text/strings.js +6 -0
  212. package/dist/core/triwiki-wrongness/image-wrongness.js +1 -3
  213. package/dist/core/triwiki-wrongness/wrongness-ledger.js +1 -3
  214. package/dist/core/triwiki-wrongness/wrongness-proof-linker.js +1 -3
  215. package/dist/core/triwiki-wrongness/wrongness-retrieval.js +1 -3
  216. package/dist/core/triwiki-wrongness/wrongness-schema.js +1 -3
  217. package/dist/core/triwiki-wrongness/wrongness-trust-policy.js +1 -3
  218. package/dist/core/trust-kernel/completion-contract.js +1 -3
  219. package/dist/core/trust-kernel/route-contract.js +1 -3
  220. package/dist/core/update/update-migration-state.js +1 -3
  221. package/dist/core/update-check.js +20 -6
  222. package/dist/core/verification/diff-quality.js +3 -4
  223. package/dist/core/verification/impact-scan.js +36 -89
  224. package/dist/core/verification/mistake-rule-compiler.js +17 -4
  225. package/dist/core/version-manager.js +3 -20
  226. package/dist/core/version.js +1 -1
  227. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +23 -3
  228. package/dist/native/sks-menubar/Sources/ControlKit.swift +102 -0
  229. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +23 -11
  230. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +7 -3
  231. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +17 -1
  232. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +22 -15
  233. package/dist/native/sks-menubar/Sources/ProcessClient.swift +15 -5
  234. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +324 -0
  235. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +154 -214
  236. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +252 -0
  237. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +77 -71
  238. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +167 -51
  239. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +14 -5
  240. package/dist/native/sks-menubar/Sources/SidebarItem.swift +15 -1
  241. package/dist/native/sks-menubar/Sources/StatusItemController.swift +2 -2
  242. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +16 -12
  243. package/dist/scripts/agent-guidance-dedup-check.js +23 -0
  244. package/dist/scripts/agent-message-bus-reader-check.js +1 -1
  245. package/dist/scripts/all-feature-deep-completion-check.js +1 -1
  246. package/dist/scripts/appshots-thread-attachment-discovery-check.js +1 -1
  247. package/dist/scripts/architecture-guard-check.js +1 -2
  248. package/dist/scripts/certificate-sla-check.js +1 -1
  249. package/dist/scripts/cli-output-consistency-check.js +1 -1
  250. package/dist/scripts/codex-0-134-runner-truth-check.js +1 -1
  251. package/dist/scripts/codex-0138-capability-artifact-check.js +1 -1
  252. package/dist/scripts/codex-0138-capability-check.js +1 -1
  253. package/dist/scripts/codex-0138-doctor-check.js +1 -1
  254. package/dist/scripts/codex-0138-feature-probes-check.js +1 -1
  255. package/dist/scripts/codex-0144-app-server-v2-check.js +1 -1
  256. package/dist/scripts/codex-0144-binary-identity-check.js +1 -1
  257. package/dist/scripts/codex-0144-capability-check.js +1 -1
  258. package/dist/scripts/codex-0144-core-real-probes-check.js +1 -1
  259. package/dist/scripts/codex-0144-manifest-check.js +1 -1
  260. package/dist/scripts/codex-0144-policy-check.js +1 -1
  261. package/dist/scripts/codex-0144-thread-store-check.js +1 -1
  262. package/dist/scripts/codex-agent-role-rich-content-check.js +2 -2
  263. package/dist/scripts/codex-agent-role-sync-check.js +2 -2
  264. package/dist/scripts/codex-agent-type-routing-check.js +2 -2
  265. package/dist/scripts/codex-app-execution-profile-check.js +2 -2
  266. package/dist/scripts/codex-app-handoff-check.js +1 -1
  267. package/dist/scripts/codex-app-handoff-launch-check.js +1 -1
  268. package/dist/scripts/codex-control-tool-output-continuity-audit-check.js +1 -1
  269. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -3
  270. package/dist/scripts/codex-native-feature-broker-blackbox.js +2 -2
  271. package/dist/scripts/codex-native-feature-broker-check.js +2 -2
  272. package/dist/scripts/{sks-3-1-6-directive-check-lib.js → codex-native-gate-lib.js} +5 -5
  273. package/dist/scripts/codex-native-harness-compat-check.js +2 -2
  274. package/dist/scripts/codex-native-hook-lifecycle-proof-check.js +2 -2
  275. package/dist/scripts/codex-plugin-app-template-policy-check.js +1 -1
  276. package/dist/scripts/codex-sdk-backend-router-check.js +1 -1
  277. package/dist/scripts/codex-sdk-capability-check.js +1 -1
  278. package/dist/scripts/context7-evidence-dedupe-check.js +1 -1
  279. package/dist/scripts/core-skill-card-schema-check.js +1 -1
  280. package/dist/scripts/core-skill-deployment-snapshot-check.js +1 -1
  281. package/dist/scripts/core-skill-heldout-validation-check.js +1 -1
  282. package/dist/scripts/core-skill-no-inference-optimizer-check.js +1 -1
  283. package/dist/scripts/core-skill-patch-check.js +1 -1
  284. package/dist/scripts/core-skill-route-runtime-integration-check.js +1 -1
  285. package/dist/scripts/current-command-surface-check.js +1 -1
  286. package/dist/scripts/current-surface-update-e2e-check.js +4 -1
  287. package/dist/scripts/current-upgrade-matrix-check.js +1 -1
  288. package/dist/scripts/dfix-fixture-check.js +1 -1
  289. package/dist/scripts/dfix-patch-handoff-check.js +1 -1
  290. package/dist/scripts/dfix-verification-check.js +1 -1
  291. package/dist/scripts/dfix-verification-recommendation-check.js +1 -1
  292. package/dist/scripts/docs-truthfulness-check.js +16 -3
  293. package/dist/scripts/doctor-codex-app-harness-check.js +2 -2
  294. package/dist/scripts/evidence-flagship-coverage-check.js +1 -1
  295. package/dist/scripts/{sks-1-18-gate-lib.js → gate-lib.js} +1 -1
  296. package/dist/scripts/gate-policy-audit-check.js +4 -1
  297. package/dist/scripts/git-worktree-checkpoint-check.js +1 -1
  298. package/dist/scripts/git-worktree-cross-rebase-check.js +1 -1
  299. package/dist/scripts/git-worktree-diff-envelope-check.js +1 -1
  300. package/dist/scripts/git-worktree-dirty-lock-check.js +1 -1
  301. package/dist/scripts/git-worktree-dirty-main-detection-check.js +1 -1
  302. package/dist/scripts/git-worktree-integration-primary-check.js +1 -1
  303. package/dist/scripts/git-worktree-manifest-append-check.js +1 -1
  304. package/dist/scripts/git-worktree-untracked-diff-check.js +1 -1
  305. package/dist/scripts/harness-benchmark-check.js +1 -1
  306. package/dist/scripts/helper-dedup-check.js +62 -0
  307. package/dist/scripts/hook-latency-budget-check.js +1 -1
  308. package/dist/scripts/image-artifact-path-contract-check.js +1 -1
  309. package/dist/scripts/imagegen-real-smoke-check.js +2 -5
  310. package/dist/scripts/installed-package-smoke-check.js +1 -1
  311. package/dist/scripts/legacy-gate-inventory-check.js +1 -1
  312. package/dist/scripts/legacy-gate-purge-check.js +1 -1
  313. package/dist/scripts/legacy-strong-inventory-check.js +1 -1
  314. package/dist/scripts/lib/codex-sdk-gate-lib.js +1 -1
  315. package/dist/scripts/mad-sks-launch-sql-plane-boundary-check.js +1 -1
  316. package/dist/scripts/mad-sks-sql-plane-capability-check.js +1 -1
  317. package/dist/scripts/mad-sks-sql-plane-lifecycle-hook-decision-check.js +1 -1
  318. package/dist/scripts/mad-sks-sql-plane-result-lifecycle-check.js +1 -1
  319. package/dist/scripts/managed-config-merge-check.js +1 -1
  320. package/dist/scripts/mcp-plugin-inventory-check.js +1 -1
  321. package/dist/scripts/model-call-concurrency-check.js +1 -1
  322. package/dist/scripts/mutation-callsite-coverage-check.js +1 -1
  323. package/dist/scripts/naruto-ssot-gate-aliases-check.js +1 -1
  324. package/dist/scripts/naruto-ssot-pipeline-default-check.js +1 -1
  325. package/dist/scripts/naruto-ssot-route-normalization-check.js +1 -1
  326. package/dist/scripts/naruto-ssot-routing-check.js +1 -1
  327. package/dist/scripts/naruto-worktree-coding-blackbox.js +1 -1
  328. package/dist/scripts/native-capability-repair-matrix-check.js +1 -1
  329. package/dist/scripts/native-image-generation-repair-check.js +1 -1
  330. package/dist/scripts/official-subagent-workflow-check.js +3 -3
  331. package/dist/scripts/package-published-contract-check.js +37 -4
  332. package/dist/scripts/packlist-performance-check.js +17 -1
  333. package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +1 -3
  334. package/dist/scripts/ppt-real-export-adapter-check.js +1 -1
  335. package/dist/scripts/ppt-real-imagegen-wiring-check.js +1 -1
  336. package/dist/scripts/ppt-reexport-rereview-check.js +1 -1
  337. package/dist/scripts/prepublish-fast-check.js +1 -1
  338. package/dist/scripts/probe-memoization-check.js +2 -2
  339. package/dist/scripts/prompt-placeholder-guard-check.js +1 -1
  340. package/dist/scripts/qa-loop-app-handoff-capability-check.js +1 -1
  341. package/dist/scripts/qa-loop-app-handoff-check.js +1 -1
  342. package/dist/scripts/qa-loop-app-handoff-cli-check.js +1 -1
  343. package/dist/scripts/qa-loop-app-handoff-confirmation-check.js +1 -1
  344. package/dist/scripts/qa-loop-app-handoff-gate-lifecycle-check.js +1 -1
  345. package/dist/scripts/qa-loop-app-handoff-launch-check.js +1 -1
  346. package/dist/scripts/qa-loop-app-handoff-status-lifecycle-check.js +1 -1
  347. package/dist/scripts/qa-loop-budget-policy-check.js +1 -1
  348. package/dist/scripts/qa-loop-effort-escalation-check.js +1 -1
  349. package/dist/scripts/qa-loop-execution-profile-routing-check.js +2 -2
  350. package/dist/scripts/qa-loop-image-path-exposure-check.js +1 -1
  351. package/dist/scripts/qa-loop-image-path-prompt-injection-check.js +1 -1
  352. package/dist/scripts/{sks-1-12-real-execution-check-lib.js → real-execution-check-lib.js} +1 -1
  353. package/dist/scripts/release-affected-selector-check.js +1 -1
  354. package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
  355. package/dist/scripts/release-cache-glob-hashing-check.js +1 -1
  356. package/dist/scripts/release-cache-input-classifier-check.js +1 -1
  357. package/dist/scripts/release-cache-neutralization-report-check.js +1 -1
  358. package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -1
  359. package/dist/scripts/release-dynamic-presets-check.js +2 -2
  360. package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
  361. package/dist/scripts/release-gate-batch-runner-check.js +1 -1
  362. package/dist/scripts/release-gate-budget-check.js +1 -1
  363. package/dist/scripts/release-gate-dag-runner-check.js +1 -1
  364. package/dist/scripts/release-latency-slo-check.js +1 -1
  365. package/dist/scripts/release-metadata-check.js +256 -2
  366. package/dist/scripts/release-parallel-speed-budget-check.js +1 -1
  367. package/dist/scripts/release-proof-truth-check.js +1 -1
  368. package/dist/scripts/release-provenance-check.js +4 -2
  369. package/dist/scripts/release-runtime-truth-matrix-check.js +1 -1
  370. package/dist/scripts/research-blueprint-densifier-check.js +1 -1
  371. package/dist/scripts/research-claim-builder-check.js +1 -1
  372. package/dist/scripts/research-complete-package-fixture-check.js +1 -1
  373. package/dist/scripts/research-execution-profile-routing-check.js +2 -2
  374. package/dist/scripts/research-final-reviewer-blackbox.js +1 -1
  375. package/dist/scripts/{sks-3-1-4-directive-check-lib.js → runtime-domain-gate-lib.js} +2 -2
  376. package/dist/scripts/runtime-proof-summary-check.js +1 -1
  377. package/dist/scripts/runtime-script-pack-closure-check.js +7 -1
  378. package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
  379. package/dist/scripts/scheduler-parallel-proof-consistency-check.js +1 -1
  380. package/dist/scripts/scheduler-utilization-integral-check.js +1 -1
  381. package/dist/scripts/search-visibility-gate-lib.js +1 -1
  382. package/dist/scripts/secret-preservation-check.js +1 -1
  383. package/dist/scripts/seo-geo-geo-disambiguation-check.js +1 -1
  384. package/dist/scripts/seo-geo-skill-rich-content-check.js +1 -1
  385. package/dist/scripts/side-effect-runtime-report-check.js +1 -1
  386. package/dist/scripts/{sks-3-1-8-check-lib.js → skill-fixture-check-lib.js} +1 -1
  387. package/dist/scripts/skill-name-canonicalizer-check.js +1 -1
  388. package/dist/scripts/skills-manifest-continuity-check.js +1 -1
  389. package/dist/scripts/sks-uninstall-regression-check.js +1 -1
  390. package/dist/scripts/sksd-daemon-check.js +1 -1
  391. package/dist/scripts/super-search-provider-interface-check.js +1 -1
  392. package/dist/scripts/triwiki-cache-key-check.js +1 -1
  393. package/dist/scripts/triwiki-proof-bank-blackbox.js +2 -2
  394. package/dist/scripts/triwiki-proof-bank-check.js +1 -1
  395. package/dist/scripts/triwiki-proof-card-check.js +1 -1
  396. package/dist/scripts/type-surface-codex-app-check.js +2 -2
  397. package/dist/scripts/{sks-3-1-5-directive-check-lib.js → typed-routing-gate-lib.js} +2 -2
  398. package/dist/scripts/ultra-router-auto-router-check.js +1 -1
  399. package/dist/scripts/ux-review-extract-wires-real-extractor-check.js +1 -1
  400. package/dist/scripts/ux-review-patch-diff-recheck-check.js +1 -1
  401. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -2
  402. package/package.json +5 -7
  403. package/release-gates.v2.json +66 -11
  404. package/runtime-required-scripts.json +138 -14
  405. package/config/bench-baseline.json +0 -7
  406. package/dist/core/codex-app/sks-menubar.js +0 -1
  407. package/dist/core/release/release-closure-contract.js +0 -30
  408. package/dist/core/release/release-closure-helpers.js +0 -293
  409. package/dist/core/release/release-closure-validation.js +0 -284
  410. package/dist/core/release/release-closure.js +0 -163
  411. package/dist/scripts/build-dist.js +0 -124
  412. package/dist/scripts/build-once-runner-check.js +0 -6
  413. package/dist/scripts/canonical-test-runner.js +0 -234
  414. package/dist/scripts/changelog-check.js +0 -45
  415. package/dist/scripts/check-architecture.js +0 -295
  416. package/dist/scripts/check-cli-entrypoint.js +0 -41
  417. package/dist/scripts/check-command-module-budget.js +0 -77
  418. package/dist/scripts/check-feature-quality.js +0 -51
  419. package/dist/scripts/check-legacy-free.js +0 -64
  420. package/dist/scripts/check-pipeline-budget.js +0 -49
  421. package/dist/scripts/check-pipeline-runtime.js +0 -16
  422. package/dist/scripts/check-publish-tag.js +0 -56
  423. package/dist/scripts/check-route-modularity.js +0 -72
  424. package/dist/scripts/codex-0140-capability-check.js +0 -14
  425. package/dist/scripts/codex-0140-feature-probes-check.js +0 -23
  426. package/dist/scripts/codex-permission-profiles-check.js +0 -34
  427. package/dist/scripts/codex-project-config-policy-splitter-check.js +0 -49
  428. package/dist/scripts/competitor-scorecard-check.js +0 -24
  429. package/dist/scripts/core-skill-no-drift-check.js +0 -23
  430. package/dist/scripts/db-route-materialization-check.js +0 -32
  431. package/dist/scripts/doctor-context7-mcp-repair-blackbox.js +0 -15
  432. package/dist/scripts/doctor-fastpath-check.js +0 -25
  433. package/dist/scripts/doctor-fix-production-blackbox.js +0 -35
  434. package/dist/scripts/doctor-idempotence-check.js +0 -6
  435. package/dist/scripts/doctor-ops-semantics-check.js +0 -83
  436. package/dist/scripts/doctor-startup-config-repair-blackbox.js +0 -18
  437. package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +0 -13
  438. package/dist/scripts/ensure-bin-executable.js +0 -16
  439. package/dist/scripts/fsx-hotpath-check.js +0 -9
  440. package/dist/scripts/high-risk-contracts-check.js +0 -183
  441. package/dist/scripts/hook-latency-quantum-check.js +0 -120
  442. package/dist/scripts/import-graph-budget-check.js +0 -9
  443. package/dist/scripts/mcp-tool-naming-parity-check.js +0 -14
  444. package/dist/scripts/naruto-codex-e2e-check.js +0 -105
  445. package/dist/scripts/naruto-write-e2e-check.js +0 -31
  446. package/dist/scripts/native-capability-postcheck-check.js +0 -16
  447. package/dist/scripts/ops-diagnostics-bundle-check.js +0 -11
  448. package/dist/scripts/parallel-production-smoke-check.js +0 -20
  449. package/dist/scripts/perf-budget-check.js +0 -12
  450. package/dist/scripts/project-skill-dedupe-blackbox.js +0 -62
  451. package/dist/scripts/release-dag-full-coverage-check.js +0 -98
  452. package/dist/scripts/release-dist-freshness-check.js +0 -6
  453. package/dist/scripts/release-file-ownership-check.js +0 -35
  454. package/dist/scripts/release-gate-script-parity-check.js +0 -148
  455. package/dist/scripts/release-macos-menubar-proof.js +0 -167
  456. package/dist/scripts/release-main-push-guard.js +0 -37
  457. package/dist/scripts/release-main-push-receipt.js +0 -43
  458. package/dist/scripts/release-metadata-1-19-check.js +0 -257
  459. package/dist/scripts/release-version-truth-check.js +0 -138
  460. package/dist/scripts/retention-apply-smoke-check.js +0 -19
  461. package/dist/scripts/retention-budget-check.js +0 -6
  462. package/dist/scripts/retention-dry-run-check.js +0 -8
  463. package/dist/scripts/rollback-recovery-smoke-check.js +0 -70
  464. package/dist/scripts/route-intent-regression-check.js +0 -42
  465. package/dist/scripts/runtime-current-terminal-check.js +0 -108
  466. package/dist/scripts/runtime-dist-parity-check.js +0 -76
  467. package/dist/scripts/runtime-no-src-mjs-check.js +0 -30
  468. package/dist/scripts/runtime-ts-rust-boundary-check.js +0 -58
  469. package/dist/scripts/runtime-ts-source-of-truth-check.js +0 -53
  470. package/dist/scripts/secret-preservation-guard-check.js +0 -36
  471. package/dist/scripts/seo-marketing-truthfulness-check.js +0 -98
  472. package/dist/scripts/seo-metadata-sync-check.js +0 -60
  473. package/dist/scripts/sizecheck.js +0 -148
  474. package/dist/scripts/sks-menubar-install-check.js +0 -333
  475. package/dist/scripts/supabase-secret-preservation-blackbox.js +0 -28
  476. package/dist/scripts/super-search-live-smoke-check.js +0 -115
  477. package/dist/scripts/super-search-local-http-smoke-check.js +0 -16
  478. package/dist/scripts/super-search-offline-contract-check.js +0 -43
  479. package/dist/scripts/update-fastpath-check.js +0 -28
  480. package/dist/scripts/update-preserves-supabase-keys-blackbox.js +0 -26
  481. package/dist/scripts/upgrade-migration-matrix-check.js +0 -351
  482. package/dist/scripts/write-build-manifest.js +0 -70
  483. package/dist/scripts/zellij-capability-check.js +0 -13
  484. package/dist/scripts/zellij-developer-controls-check.js +0 -16
  485. package/dist/scripts/zellij-doctor-readiness-check.js +0 -49
  486. package/dist/scripts/zellij-dynamic-pane-lifecycle-check.js +0 -19
  487. package/dist/scripts/zellij-fake-adapter-check.js +0 -31
  488. package/dist/scripts/zellij-first-slot-down-stack-check.js +0 -20
  489. package/dist/scripts/zellij-first-slot-down-stack-real-check.js +0 -348
  490. package/dist/scripts/zellij-homebrew-policy-check.js +0 -3
  491. package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -21
  492. package/dist/scripts/zellij-lane-renderer-check.js +0 -78
  493. package/dist/scripts/zellij-launch-command-truth-check.js +0 -62
  494. package/dist/scripts/zellij-layout-valid-check.js +0 -120
  495. package/dist/scripts/zellij-pane-creation-lock-metrics-check.js +0 -10
  496. package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -78
  497. package/dist/scripts/zellij-pane-lock-does-not-block-worker-check.js +0 -12
  498. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -134
  499. package/dist/scripts/zellij-qa-app-handoff-status-check.js +0 -7
  500. package/dist/scripts/zellij-right-column-geometry-proof.js +0 -158
  501. package/dist/scripts/zellij-right-column-headless-overflow-check.js +0 -20
  502. package/dist/scripts/zellij-right-column-manager-check.js +0 -24
  503. package/dist/scripts/zellij-self-heal-check.js +0 -3
  504. package/dist/scripts/zellij-self-heal-dry-run-check.js +0 -3
  505. package/dist/scripts/zellij-self-heal-status-contract-check.js +0 -3
  506. package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -3
  507. package/dist/scripts/zellij-slot-column-anchor-check.js +0 -65
  508. package/dist/scripts/zellij-slot-column-anchor-telemetry-check.js +0 -8
  509. package/dist/scripts/zellij-slot-only-ui-check.js +0 -29
  510. package/dist/scripts/zellij-slot-pane-renderer-check.js +0 -149
  511. package/dist/scripts/zellij-slot-pane-stale-detection-check.js +0 -65
  512. package/dist/scripts/zellij-slot-pane-telemetry-renderer-check.js +0 -11
  513. package/dist/scripts/zellij-slot-renderer-proof-semantics-check.js +0 -58
  514. package/dist/scripts/zellij-slot-telemetry-check.js +0 -38
  515. package/dist/scripts/zellij-slot-telemetry-incremental-check.js +0 -46
  516. package/dist/scripts/zellij-slot-telemetry-live-flush-check.js +0 -55
  517. package/dist/scripts/zellij-slot-telemetry-performance-check.js +0 -32
  518. package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -19
  519. package/dist/scripts/zellij-slot-telemetry-renderer-check.js +0 -11
  520. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +0 -23
  521. package/dist/scripts/zellij-spawn-on-demand-layout-check.js +0 -45
  522. package/dist/scripts/zellij-stacked-capability-routing-check.js +0 -9
  523. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -78
  524. package/dist/scripts/zellij-stacked-version-matrix-check.js +0 -29
  525. package/dist/scripts/zellij-stacked-version-parser-check.js +0 -19
  526. package/dist/scripts/zellij-ui-design-check.js +0 -92
  527. package/dist/scripts/zellij-update-missing-self-heal-check.js +0 -3
  528. package/dist/scripts/zellij-update-prompt-matrix-check.js +0 -16
  529. package/dist/scripts/zellij-update-prompt-mode-check.js +0 -18
  530. package/dist/scripts/zellij-update-prompt-safety-check.js +0 -8
  531. package/dist/scripts/zellij-worker-pane-manager-check.js +0 -108
  532. package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +0 -47
  533. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -199
  534. /package/dist/scripts/{sks-1-11-gate-lib.js → sks-cli-gate-lib.js} +0 -0
@@ -1,8 +1,9 @@
1
1
  import path from 'node:path';
2
2
  import fsp from 'node:fs/promises';
3
3
  import { spawnSync } from 'node:child_process';
4
- import { nowIso, writeJsonAtomic } from './fsx.js';
5
- import { LEAN_CHANGE_EVIDENCE_SCHEMA, leanPolicyReference, parseLeanSimplificationMarkerLine } from './lean-engineering-policy.js';
4
+ import { nowIso, sha256, writeJsonAtomic } from './fsx.js';
5
+ import { LEAN_CHANGE_EVIDENCE_SCHEMA, assessTestVolume, leanPolicyReference, parseLeanSimplificationMarkerLine } from './lean-engineering-policy.js';
6
+ import { escapeRegExp } from './text/regex.js';
6
7
  export const CODE_STRUCTURE_THRESHOLDS = {
7
8
  warning: 1000,
8
9
  review: 2000,
@@ -15,8 +16,25 @@ const SOURCE_EXT_RE = /\.(js|ts|tsx|jsx|cjs|mjs|json|md|rs|toml)$/;
15
16
  const FALLBACK_RE = /\b(fallback|legacy|shim|compat|mock|catch-all|catch all|default provider)\b/i;
16
17
  const CONFIG_FLAG_RE = /\b(process\.env|SKS_[A-Z0-9_]+|CODEX_[A-Z0-9_]+|[A-Z][A-Z0-9_]{5,})\b/;
17
18
  const ABSTRACTION_RE = /\b(interface|abstract class|class|factory|provider|adapter|registry|orchestrator|manager)\b/;
19
+ const N_PLUS_ONE_RE = /(?:(?:for\s*(?:await\s*)?\([^)]*\)|for\s+(?:await\s+)?(?:const|let|var)\s+\w+\s+of\s+[^{]+|\.forEach\s*\(\s*async\b|\.map\s*\(\s*async\b))[\s\S]{0,1200}\bawait\s+[\w.$]*(?:query|execute|findMany|findUnique|findFirst|select|insert|update|delete|save)\s*\(/i;
20
+ const UNBOUNDED_LOOP_RE = /\bwhile\s*\(\s*true\s*\)|\bfor\s*\(\s*;\s*;\s*\)/i;
21
+ const RENDER_LOOP_RE = /\buseEffect\s*\(\s*\(\s*\)\s*=>\s*\{[\s\S]{0,1200}\bset[A-Z]\w*\s*\([^)]*\)[\s\S]{0,400}\}\s*\)\s*;?/i;
22
+ const VERIFICATION_BYPASS_RE = /\b(?:test|it|describe)\.(?:skip|only)\s*\(|\b(?:xit|xdescribe)\s*\(|@ts-ignore\b|eslint-disable(?:-next-line)?\b|\.catch\s*\(\s*\(\s*\)\s*=>\s*(?:undefined|\{\s*\})\s*\)|catch\s*\([^)]*\)\s*\{\s*\}/i;
23
+ const DB_POOL_CONSTRUCTOR_RE = /\bnew\s+(?:Pool|PrismaClient|DataSource|Sequelize)\s*\(|\bcreatePool\s*\(|\bknex\s*\(/i;
24
+ const SENSITIVE_DOMAIN_RE = /\b(payment|billing|invoice|charge|refund|ledger|balance|payout|settlement|wallet)\b|결제|청구|환불|원장|잔액|정산/i;
25
+ const DB_WRITE_CALL_RE = /\b(?:insert|update|delete|upsert|save|create|execute|query)\s*\(/gi;
26
+ const TRANSACTION_MARKER_RE = /\b(?:transaction|withTransaction|atomic|begin|commit|rollback|savepoint|\$transaction)\b/i;
27
+ const ENGINEERING_SANITY_TAGS = new Set([
28
+ 'solid',
29
+ 'n-plus-one',
30
+ 'unbounded-loop',
31
+ 'verification-bypass',
32
+ 'db-pool',
33
+ 'transaction'
34
+ ]);
18
35
  export async function scanCodeStructure(root, opts = {}) {
19
36
  const changedScope = await collectChangedScope(root, opts);
37
+ const addedHunkScopes = await collectAddedHunkScopes(root, changedScope);
20
38
  const files = await resolveScanFiles(root, opts, changedScope);
21
39
  const touched = new Set((opts.touchedFiles || []).map((file) => normalizeRel(root, file)));
22
40
  const changedSet = new Set((changedScope.source_files || []).map((file) => normalizeSlashes(file)));
@@ -30,7 +48,12 @@ export async function scanCodeStructure(root, opts = {}) {
30
48
  const changedByDiff = changedSet.has(rel);
31
49
  if (status === 'ok' && !opts.includeOk && !changedByDiff)
32
50
  continue;
33
- const signals = analyzeTextSignals(rel, text, changedByDiff);
51
+ const addedScope = addedHunkScopes.get(rel);
52
+ const signals = analyzeTextSignals(rel, text, changedByDiff, addedScope || {
53
+ scope: changedByDiff ? 'changed_file_without_added_hunks' : 'full_file_advisory',
54
+ text: changedByDiff ? '' : text,
55
+ line_ranges: []
56
+ });
34
57
  intentionalSimplifications.push(...signals.lean_markers);
35
58
  entries.push({
36
59
  path: rel,
@@ -115,18 +138,23 @@ async function resolveScanFiles(root, opts, changedScope) {
115
138
  const changedSourceFiles = (changedScope.source_files || [])
116
139
  .filter((file) => DEFAULT_INCLUDE.has(path.extname(file)))
117
140
  .map((file) => path.resolve(root, file));
118
- if ((opts.changed || opts.changedSince || opts.changedFiles?.length) && changedSourceFiles.length)
141
+ if (opts.changed || opts.changedSince || opts.changedFiles?.length)
119
142
  return changedSourceFiles;
120
143
  return listSourceFiles(root);
121
144
  }
122
145
  async function collectChangedScope(root, opts) {
123
146
  if (opts.changedFiles?.length) {
124
147
  const changedFiles = Array.from(new Set(opts.changedFiles.map((file) => normalizeRel(root, file))));
148
+ const entries = [];
149
+ for (const file of changedFiles) {
150
+ const text = await fsp.readFile(path.join(root, file), 'utf8').catch(() => '');
151
+ entries.push({ path: file, status: 'M', lines_added: 0, lines_deleted: 0, source_sha256: isSourceLike(file) ? sha256(text) : null });
152
+ }
125
153
  return {
126
154
  ...emptyChangedScope('explicit', opts.changedSince || 'HEAD'),
127
155
  changed_files: changedFiles,
128
156
  source_files: changedFiles.filter(isSourceLike),
129
- entries: changedFiles.map((file) => ({ path: file, status: 'M', lines_added: 0, lines_deleted: 0 }))
157
+ entries
130
158
  };
131
159
  }
132
160
  const shouldCollect = opts.changed || opts.changedSince;
@@ -169,11 +197,18 @@ async function collectChangedScope(root, opts) {
169
197
  entriesByPath.set(rel, {
170
198
  path: rel,
171
199
  status: 'A',
172
- lines_added: text ? text.split(/\n/).length : 0,
173
- lines_deleted: 0
200
+ lines_added: addedFileLines(text).length,
201
+ lines_deleted: 0,
202
+ source_sha256: isSourceLike(rel) ? sha256(text) : null
174
203
  });
175
204
  }
176
- const entries = [...entriesByPath.values()].filter((entry) => entry.path);
205
+ const entries = [...entriesByPath.values()].filter((entry) => entry.path && isChangedScopePath(entry.path));
206
+ for (const entry of entries) {
207
+ if (!isSourceLike(entry.path) || entry.source_sha256)
208
+ continue;
209
+ const text = await fsp.readFile(path.join(root, entry.path), 'utf8').catch(() => '');
210
+ entry.source_sha256 = sha256(text);
211
+ }
177
212
  const changedFiles = entries.map((entry) => entry.path);
178
213
  const sourceFiles = changedFiles.filter(isSourceLike);
179
214
  const linesAdded = entries.reduce((sum, entry) => sum + Number(entry.lines_added || 0), 0);
@@ -258,8 +293,70 @@ async function collectAddedFallbackSites(root, changedScope) {
258
293
  }
259
294
  return sites;
260
295
  }
261
- function analyzeTextSignals(rel, text, changedByDiff) {
296
+ async function collectAddedHunkScopes(root, changedScope) {
297
+ const scopes = new Map();
298
+ if (!changedScope?.source_files?.length)
299
+ return scopes;
300
+ if (changedScope.mode === 'explicit') {
301
+ for (const rel of changedScope.source_files) {
302
+ const text = await fsp.readFile(path.join(root, rel), 'utf8').catch(() => '');
303
+ const lineCount = text ? text.split(/\r?\n/).length : 0;
304
+ scopes.set(normalizeSlashes(rel), {
305
+ scope: 'explicit_changed_file',
306
+ text,
307
+ line_ranges: lineCount ? [{ start: 1, end: lineCount }] : []
308
+ });
309
+ }
310
+ return scopes;
311
+ }
312
+ if (changedScope.mode !== 'git-diff')
313
+ return scopes;
314
+ const diff = gitText(root, ['diff', '--unified=0', changedScope.base || 'HEAD', '--', ...changedScope.source_files]);
315
+ let currentFile = '';
316
+ let currentLine = 0;
317
+ const linesByFile = new Map();
318
+ for (const line of diff.split(/\r?\n/)) {
319
+ if (line.startsWith('+++ b/')) {
320
+ currentFile = normalizeSlashes(line.slice('+++ b/'.length));
321
+ continue;
322
+ }
323
+ const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
324
+ if (hunk) {
325
+ currentLine = Number(hunk[1]) || 0;
326
+ continue;
327
+ }
328
+ if (line.startsWith('+') && !line.startsWith('+++')) {
329
+ if (currentFile) {
330
+ const rows = linesByFile.get(currentFile) || [];
331
+ rows.push({ line: currentLine, text: line.slice(1) });
332
+ linesByFile.set(currentFile, rows);
333
+ }
334
+ currentLine += 1;
335
+ continue;
336
+ }
337
+ if (!line.startsWith('-'))
338
+ currentLine += 1;
339
+ }
340
+ for (const rel of changedScope.source_files) {
341
+ const normalized = normalizeSlashes(rel);
342
+ let rows = linesByFile.get(normalized) || [];
343
+ const entry = changedScope.entries?.find((candidate) => normalizeSlashes(candidate.path) === normalized);
344
+ if (!rows.length && String(entry?.status || '').startsWith('A')) {
345
+ const text = await fsp.readFile(path.join(root, normalized), 'utf8').catch(() => '');
346
+ rows = addedFileLines(text).map((value, index) => ({ line: index + 1, text: value }));
347
+ }
348
+ scopes.set(normalized, {
349
+ scope: 'added_hunks',
350
+ text: joinAddedHunkRows(rows),
351
+ line_ranges: contiguousLineRanges(rows.map((row) => row.line))
352
+ });
353
+ }
354
+ return scopes;
355
+ }
356
+ function analyzeTextSignals(rel, text, changedByDiff, sanityScope) {
262
357
  const lines = text.split(/\r?\n/);
358
+ const scopedText = String(sanityScope.text || '');
359
+ const scopedCode = stripNonCodeForSanity(scopedText);
263
360
  const imports = lines.filter((line) => /^\s*import\s/.test(line));
264
361
  const externalImports = imports
265
362
  .map((line) => /from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/.exec(line)?.[1] || /from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/.exec(line)?.[2] || '')
@@ -268,21 +365,64 @@ function analyzeTextSignals(rel, text, changedByDiff) {
268
365
  .map((line, index) => parseLeanSimplificationMarkerLine(line, rel, index + 1))
269
366
  .filter((marker) => Boolean(marker));
270
367
  const effectiveLines = lines.map((line) => line.trim()).filter(Boolean);
271
- const forwardingOnly = effectiveLines.length > 0
368
+ const scopeCoversWholeFile = sanityScope.line_ranges.length === 1
369
+ && sanityScope.line_ranges[0]?.start === 1
370
+ && sanityScope.line_ranges[0]?.end >= lines.length;
371
+ const forwardingOnly = scopeCoversWholeFile
372
+ && effectiveLines.length > 0
272
373
  && effectiveLines.length <= 10
273
374
  && effectiveLines.every((line) => line.startsWith('import ') || line.startsWith('export ') || line.startsWith('//') || line.startsWith('/*') || line.startsWith('*'));
274
375
  return {
275
376
  import_count: imports.length,
276
377
  external_dependency_imports: Array.from(new Set(externalImports)).sort(),
277
- ts_nocheck: /^\s*\/\/\s*@ts-nocheck\b/m.test(text),
378
+ ts_nocheck: /^\s*\/\/\s*@ts-nocheck\b/m.test(scopedText),
278
379
  changed_by_diff: changedByDiff,
279
380
  forwarding_only: forwardingOnly,
280
- fallback_markers: countMatches(text, FALLBACK_RE),
281
- config_flag_markers: countMatches(text, CONFIG_FLAG_RE),
282
- abstraction_markers: countMatches(text, ABSTRACTION_RE),
381
+ fallback_markers: countMatches(scopedCode, FALLBACK_RE),
382
+ config_flag_markers: countMatches(scopedCode, CONFIG_FLAG_RE),
383
+ abstraction_markers: countMatches(scopedCode, ABSTRACTION_RE),
384
+ engineering_sanity: engineeringSanitySignals(scopedText, sanityScope),
283
385
  lean_markers: leanMarkers
284
386
  };
285
387
  }
388
+ function engineeringSanitySignals(text, scope) {
389
+ const code = stripNonCodeForSanity(text);
390
+ const writeCalls = code.match(DB_WRITE_CALL_RE)?.length || 0;
391
+ return {
392
+ source_scope: scope.scope,
393
+ added_hunk_line_ranges: scope.line_ranges,
394
+ candidate_detection_only: true,
395
+ n_plus_one_candidates: countMatches(code, N_PLUS_ONE_RE),
396
+ unbounded_loop_candidates: countMatches(code, UNBOUNDED_LOOP_RE),
397
+ render_loop_candidates: countMatches(code, RENDER_LOOP_RE),
398
+ direct_recursion_candidates: countDirectRecursionCandidates(code),
399
+ verification_bypass_markers: countMatches(code, VERIFICATION_BYPASS_RE),
400
+ db_pool_constructor_markers: countMatches(code, DB_POOL_CONSTRUCTOR_RE),
401
+ sensitive_transaction_candidate: SENSITIVE_DOMAIN_RE.test(code)
402
+ && writeCalls >= 2
403
+ && !TRANSACTION_MARKER_RE.test(code),
404
+ db_write_call_markers: writeCalls
405
+ };
406
+ }
407
+ function stripNonCodeForSanity(text) {
408
+ return String(text || '')
409
+ .replace(/^\s*const\s+[A-Z0-9_]+_RE\s*=\s*\/.*\/[dgimsuvy]*;?\s*$/gm, '')
410
+ .replace(/\/\*[\s\S]*?\*\//g, (value) => value.replace(/[^\n]/g, ' '))
411
+ .replace(/(^|[^:\\])\/\/.*$/gm, '$1')
412
+ .replace(/(['"`])(?:\\.|(?!\1)[\s\S])*?\1/g, (value) => value.replace(/[^\n]/g, ' '));
413
+ }
414
+ function countDirectRecursionCandidates(text) {
415
+ let count = 0;
416
+ const functions = /(?:function\s+([A-Za-z_$][\w$]*)\s*\([^)]*\)|(?:const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>)\s*\{([\s\S]{0,3000}?)\n?\}/g;
417
+ let match;
418
+ while ((match = functions.exec(text))) {
419
+ const name = match[1] || match[2];
420
+ const body = match[3] || '';
421
+ if (name && new RegExp(`\\b${escapeRegExp(name)}\\s*\\(`).test(body))
422
+ count += 1;
423
+ }
424
+ return count;
425
+ }
286
426
  function collectRunnableChecks(changedScope) {
287
427
  return (changedScope.source_files || [])
288
428
  .filter((file) => /\b(test|tests|fixture|fixtures|spec|check|__tests__)\b/i.test(file))
@@ -290,6 +430,14 @@ function collectRunnableChecks(changedScope) {
290
430
  }
291
431
  function buildSemanticReview(input) {
292
432
  const findings = [];
433
+ const testVolume = assessTestVolume(input.changedScope?.entries || []);
434
+ if (testVolume.over_budget) {
435
+ findings.push({
436
+ tag: 'yagni',
437
+ severity: 'review',
438
+ summary: `change adds ${testVolume.added_test_lines} test lines for ${testVolume.added_source_lines} source lines (${testVolume.ratio?.toFixed(1)}x); keep the cases a real defect would produce and drop the rest — exhaustive cases are only owed on money-handling paths`
439
+ });
440
+ }
293
441
  for (const dep of input.dependencyDelta.added || []) {
294
442
  findings.push({
295
443
  tag: 'reuse',
@@ -340,12 +488,65 @@ function buildSemanticReview(input) {
340
488
  summary: 'changed file is forwarding-only; confirm it replaces an older path instead of duplicating an SSOT'
341
489
  });
342
490
  }
491
+ const sanity = entry.lean_signals.engineering_sanity || {};
343
492
  if (entry.lean_signals.config_flag_markers > 6 || entry.lean_signals.abstraction_markers > 12) {
344
493
  findings.push({
345
- tag: 'yagni',
494
+ tag: 'solid',
495
+ severity: 'review',
496
+ file: entry.path,
497
+ source_scope: sanity.source_scope,
498
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
499
+ summary: 'changed file has dense config/abstraction markers; review single responsibility, dependency direction, and unrequested layers'
500
+ });
501
+ }
502
+ if (sanity.n_plus_one_candidates > 0) {
503
+ findings.push({
504
+ tag: 'n-plus-one',
505
+ severity: 'review',
506
+ file: entry.path,
507
+ source_scope: sanity.source_scope,
508
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
509
+ summary: `${sanity.n_plus_one_candidates} loop-bound database or I/O call candidate(s) require batching, prefetching, or bounded-query review`
510
+ });
511
+ }
512
+ if (sanity.unbounded_loop_candidates > 0 || sanity.render_loop_candidates > 0 || sanity.direct_recursion_candidates > 0) {
513
+ findings.push({
514
+ tag: 'unbounded-loop',
515
+ severity: 'review',
516
+ file: entry.path,
517
+ source_scope: sanity.source_scope,
518
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
519
+ summary: `control-flow review required: unbounded=${sanity.unbounded_loop_candidates || 0}, render=${sanity.render_loop_candidates || 0}, recursion=${sanity.direct_recursion_candidates || 0}`
520
+ });
521
+ }
522
+ if (sanity.verification_bypass_markers > 0) {
523
+ findings.push({
524
+ tag: 'verification-bypass',
346
525
  severity: 'review',
347
526
  file: entry.path,
348
- summary: 'changed file has dense config/abstraction markers; review for unrequested knobs or layers'
527
+ source_scope: sanity.source_scope,
528
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
529
+ summary: `${sanity.verification_bypass_markers} disabled-check or swallowed-failure marker(s) require explicit justification and equivalent verification`
530
+ });
531
+ }
532
+ if (sanity.db_pool_constructor_markers > 0) {
533
+ findings.push({
534
+ tag: 'db-pool',
535
+ severity: 'review',
536
+ file: entry.path,
537
+ source_scope: sanity.source_scope,
538
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
539
+ summary: `${sanity.db_pool_constructor_markers} database client/pool construction marker(s) require canonical ownership, bounded acquire/release, shutdown, and exhaustion review`
540
+ });
541
+ }
542
+ if (sanity.sensitive_transaction_candidate) {
543
+ findings.push({
544
+ tag: 'transaction',
545
+ severity: 'review',
546
+ file: entry.path,
547
+ source_scope: sanity.source_scope,
548
+ added_hunk_line_ranges: sanity.added_hunk_line_ranges,
549
+ summary: 'sensitive or financial multi-write flow lacks an obvious transaction marker; verify atomic rollback, error propagation, idempotency, and post-commit invariants'
349
550
  });
350
551
  }
351
552
  }
@@ -360,12 +561,16 @@ function buildSemanticReview(input) {
360
561
  summary: `lean simplification marker is incomplete: ${marker.status}`
361
562
  });
362
563
  }
363
- const status = findings.some((finding) => finding.severity === 'blocker')
564
+ const finalizedFindings = findings.map((finding) => ({
565
+ ...finding,
566
+ id: `eng-${sha256(`${finding.tag}:${finding.file || ''}:${finding.line || 0}:${finding.summary}`).slice(0, 16)}`
567
+ }));
568
+ const status = finalizedFindings.some((finding) => finding.severity === 'blocker')
364
569
  ? 'blocked'
365
- : findings.some((finding) => finding.severity === 'review')
570
+ : finalizedFindings.some((finding) => finding.severity === 'review')
366
571
  ? 'needs-review'
367
572
  : 'pass';
368
- return { status, findings };
573
+ return { status, findings: finalizedFindings };
369
574
  }
370
575
  function isLeanOwnedTypeSafetyPath(file) {
371
576
  return [
@@ -401,6 +606,21 @@ function buildLeanChangeEvidence(input) {
401
606
  fallback_sites_added: input.fallbackSites || [],
402
607
  intentional_simplifications: input.intentionalSimplifications || [],
403
608
  runnable_checks: input.runnableChecks || [],
609
+ engineering_sanity: {
610
+ status: (changedScope.source_files || []).length ? 'manual-review-required' : 'not-applicable',
611
+ automated_candidate_status: (input.semanticReview?.findings || []).some((finding) => ENGINEERING_SANITY_TAGS.has(finding.tag))
612
+ ? 'candidates-found'
613
+ : 'no-candidates-found',
614
+ required_checks: [
615
+ 'solid_boundaries',
616
+ 'n_plus_one_and_repeated_io',
617
+ 'bounded_render_recursion_event_retry_polling',
618
+ 'verification_bypass_absent',
619
+ 'canonical_db_pool_lifecycle_when_applicable',
620
+ 'transaction_integrity_when_sensitive_or_multi_step'
621
+ ],
622
+ findings: (input.semanticReview?.findings || []).filter((finding) => ENGINEERING_SANITY_TAGS.has(finding.tag))
623
+ },
404
624
  semantic_review: input.semanticReview || { status: 'needs-review', findings: [] }
405
625
  };
406
626
  }
@@ -444,6 +664,13 @@ function isSourceLike(file) {
444
664
  const rel = normalizeSlashes(file);
445
665
  return SOURCE_DIR_RE.test(rel) && SOURCE_EXT_RE.test(rel) && !isGeneratedOrVendor(rel);
446
666
  }
667
+ function isChangedScopePath(file) {
668
+ const rel = normalizeSlashes(file);
669
+ const first = rel.split('/')[0] || '';
670
+ if (first.startsWith('.') && first !== '.agents')
671
+ return false;
672
+ return !isGeneratedOrVendor(rel);
673
+ }
447
674
  function parseNumstat(value) {
448
675
  const parsed = Number(value);
449
676
  return Number.isFinite(parsed) ? parsed : 0;
@@ -456,6 +683,18 @@ function safeJson(text) {
456
683
  return {};
457
684
  }
458
685
  }
686
+ function addedFileLines(text) {
687
+ if (!text)
688
+ return [];
689
+ const lines = text.split(/\r?\n/);
690
+ if (lines[lines.length - 1] === '')
691
+ lines.pop();
692
+ return lines;
693
+ }
694
+ export function resolveChangedScopeBase(root, fallback = 'HEAD') {
695
+ const sha = gitText(root, ['rev-parse', 'HEAD']).trim();
696
+ return /^[0-9a-f]{40}$/i.test(sha) ? sha : fallback;
697
+ }
459
698
  function gitLines(root, args) {
460
699
  const result = spawnSync('git', args, { cwd: root, encoding: 'utf8' });
461
700
  if (result.status !== 0)
@@ -488,3 +727,27 @@ function normalizeRel(root, file) {
488
727
  function normalizeSlashes(file) {
489
728
  return String(file || '').replace(/\\/g, '/');
490
729
  }
730
+ function contiguousLineRanges(lines) {
731
+ const sorted = [...new Set(lines.filter((line) => Number.isInteger(line) && line > 0))].sort((a, b) => a - b);
732
+ const ranges = [];
733
+ for (const line of sorted) {
734
+ const last = ranges.at(-1);
735
+ if (last && line === last.end + 1)
736
+ last.end = line;
737
+ else
738
+ ranges.push({ start: line, end: line });
739
+ }
740
+ return ranges;
741
+ }
742
+ function joinAddedHunkRows(rows) {
743
+ const out = [];
744
+ let previousLine = 0;
745
+ for (const row of rows) {
746
+ if (previousLine > 0 && row.line > previousLine + 1) {
747
+ out.push(' '.repeat(1301));
748
+ }
749
+ out.push(row.text);
750
+ previousLine = row.line;
751
+ }
752
+ return out.join('\n');
753
+ }
@@ -4,7 +4,20 @@ import { randomBytes } from 'node:crypto';
4
4
  import { appendJsonl, ensureDir, nowIso, readText, sha256, writeTextAtomic } from '../fsx.js';
5
5
  import { diffCodexAppUiSnapshots, snapshotCodexAppUiState } from '../codex-app/codex-app-ui-state-snapshot.js';
6
6
  import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from './codex-config-toml.js';
7
- const FAST_FEATURE_KEYS = ['fast_mode'];
7
+ import { escapeRegExp } from '../text/regex.js';
8
+ const FAST_FEATURE_KEYS = [
9
+ 'fast_mode',
10
+ 'hooks',
11
+ 'apps',
12
+ 'computer_use',
13
+ 'browser_use',
14
+ 'browser_use_external',
15
+ 'image_generation',
16
+ 'in_app_browser',
17
+ 'guardian_approval',
18
+ 'tool_suggest',
19
+ 'plugins'
20
+ ];
8
21
  export async function writeCodexConfigGuarded(input) {
9
22
  const configPath = path.resolve(input.configPath);
10
23
  const root = path.resolve(input.root || process.cwd());
@@ -513,6 +526,3 @@ async function recordCodexConfigGuard(root, reportPath, record) {
513
526
  }).catch(() => undefined);
514
527
  return file;
515
528
  }
516
- function escapeRegExp(value) {
517
- return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
518
- }
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { parse } from 'smol-toml';
4
+ import { messageOf } from '../errors/message.js';
4
5
  export function parseCodexConfigToml(text = '') {
5
6
  return parse(String(text || ''));
6
7
  }
@@ -115,6 +116,3 @@ function ownedBackupTag(base, file) {
115
116
  return 'codex-app-ui-repair';
116
117
  return null;
117
118
  }
118
- function messageOf(err) {
119
- return err instanceof Error ? err.message : String(err);
120
- }
@@ -4,6 +4,7 @@ import { nowIso, writeJsonAtomic } from '../fsx.js';
4
4
  import { repairAgentRoleConfigs } from '../agents/agent-role-config.js';
5
5
  import { MANAGED_OFFICIAL_SUBAGENT_ROLES } from '../managed-assets/managed-assets-manifest.js';
6
6
  import { agentRolePayloadFor, probeCodexAgentTypeSupport } from './codex-agent-type-probe.js';
7
+ import { messageOf } from '../errors/message.js';
7
8
  const OFFICIAL_ROLES = MANAGED_OFFICIAL_SUBAGENT_ROLES.map((role) => role.codex_name);
8
9
  export async function syncCodexAgentRoles(input) {
9
10
  const root = path.resolve(input.root);
@@ -81,6 +82,3 @@ function stringList(value, key) {
81
82
  ? value[key].map(String).filter(Boolean)
82
83
  : [];
83
84
  }
84
- function messageOf(err) {
85
- return err instanceof Error ? err.message : String(err);
86
- }
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
3
+ import { assertTestHomeWriteAllowed, ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
4
  import { codexHome, isSksOwnedGlobalUiLock, scanProjectLocalForbiddenKeys, snapshotCodexAppUiState } from './codex-app-ui-state-snapshot.js';
5
5
  import { assertCodexAppUiMutationAllowed } from './codex-app-ui-clobber-guard.js';
6
6
  import { codexProviderModelUiStatus } from '../codex-app.js';
@@ -11,6 +11,8 @@ const FAST_UI_LEGACY_TABLES = new Set(['user.fast_mode', 'profiles.sks-fast-high
11
11
  export async function repairCodexAppFastUi(root = process.cwd(), input = {}) {
12
12
  const resolvedRoot = path.resolve(root);
13
13
  const home = codexHome(input.codexHome === undefined ? {} : { codexHome: input.codexHome });
14
+ if (input.apply === true)
15
+ assertTestHomeWriteAllowed(path.join(home, 'config.toml'));
14
16
  const before = await snapshotCodexAppUiState(resolvedRoot, { codexHome: home });
15
17
  const candidates = [
16
18
  { scope: 'project', file: path.join(resolvedRoot, '.codex', 'config.toml'), mode: 'project_forbidden_keys' },
@@ -167,16 +169,27 @@ function stripProjectLocalForbiddenKeys(text) {
167
169
  function stripSksCausedHostOwnedLines(text) {
168
170
  const sourceLines = String(text || '').split(/\r?\n/);
169
171
  const preserveCodexLbSelection = hasActiveCodexLbProviderSelection(text);
172
+ const preserveOpenRouterSelection = hasActiveOpenRouterProviderSelection(text);
170
173
  const stripped = stripMatchingLines(text, (line, table, _previous, _next, index) => {
171
174
  const isLegacyFastTable = table ? FAST_UI_LEGACY_TABLES.has(table) : false;
172
175
  if (isLegacyFastTable)
173
176
  return true;
174
177
  if (!table && preserveCodexLbSelection && /^\s*model_provider\s*=\s*"codex-lb"\s*(?:#.*)?$/.test(line))
175
178
  return false;
179
+ if (!table && preserveOpenRouterSelection && /^\s*(?:model_provider\s*=\s*"openrouter"|model\s*=)/.test(line))
180
+ return false;
176
181
  return !table && FAST_UI_TOP_LEVEL_RE.test(line) && isSksOwnedGlobalUiLock(sourceLines, index);
177
182
  });
178
183
  return stripped;
179
184
  }
185
+ function hasActiveOpenRouterProviderSelection(text) {
186
+ const source = String(text || '');
187
+ if (!/(?:^|\n)\s*model_provider\s*=\s*"openrouter"\s*(?:#.*)?(?=\n|$)/.test(source))
188
+ return false;
189
+ const body = source.match(/(?:^|\n)\[model_providers\.openrouter\]([^\n]*(?:\n(?!\s*\[)[^\n]*)*)/)?.[1] || '';
190
+ return /(?:^|\n)\s*base_url\s*=\s*"https:\/\/openrouter\.ai\/api\/v1"\s*(?:#.*)?(?=\n|$)/.test(body)
191
+ && /(?:^|\n)\s*wire_api\s*=\s*"responses"\s*(?:#.*)?(?=\n|$)/.test(body);
192
+ }
180
193
  function hasActiveCodexLbProviderSelection(text) {
181
194
  const source = String(text || '');
182
195
  if (!/(?:^|\n)\s*model_provider\s*=\s*"codex-lb"\s*(?:#.*)?(?=\n|$)/.test(source))
@@ -1,6 +1,5 @@
1
- export function isRecord(value) {
2
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
- }
1
+ import { isRecord } from '../json/records.js';
2
+ export { isRecord };
4
3
  export function stringList(value) {
5
4
  return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
6
5
  }
@@ -0,0 +1,91 @@
1
+ import path from 'node:path';
2
+ import { isLoopbackHostname, resolveCatalogPath } from './multi-provider-router-support.js';
3
+ import { defaultOpenCodexCatalogPath, readTopLevelTomlString } from './codex-model-catalog.js';
4
+ const SKS_ROUTER_PROVIDER_ID = 'sks-router';
5
+ const OPENROUTER_PROVIDER_ID = 'openrouter';
6
+ export const OPENCODEX_INJECT_MARKER = 'Auto-injected by opencodex';
7
+ export const OPENCODEX_DESIGN_B_FORCE_HINT = 'OpenCodex Design B currently owns openai_base_url loopback routing. Stop OpenCodex inject or re-run with --force-routing-override only if replacing that owner is intentional.';
8
+ export function classifyCodexDesktopRouting(configText, input = {}) {
9
+ const text = String(configText || '');
10
+ const selectedProvider = readTopLevelTomlString(text, 'model_provider');
11
+ const selectedModel = readTopLevelTomlString(text, 'model');
12
+ const openaiBaseUrl = readTopLevelTomlString(text, 'openai_base_url');
13
+ const catalogPathRaw = readTopLevelTomlString(text, 'model_catalog_json');
14
+ const catalogResolved = catalogPathRaw
15
+ ? resolveCatalogPath(catalogPathRaw, input)
16
+ : null;
17
+ const defaultOpenCodexCatalog = path.resolve(defaultOpenCodexCatalogPath(input));
18
+ const catalogIsOpenCodexDefault = Boolean(catalogResolved && catalogResolved === defaultOpenCodexCatalog);
19
+ const markerPresent = text.includes(OPENCODEX_INJECT_MARKER);
20
+ const loopbackBase = openaiBaseUrl ? normalizeLoopbackOpenaiBaseUrl(openaiBaseUrl) : null;
21
+ const openaiBaseUrlLoopback = Boolean(loopbackBase);
22
+ const designB = Boolean(openaiBaseUrlLoopback
23
+ && (markerPresent || catalogIsOpenCodexDefault)
24
+ && (!selectedProvider || selectedProvider === 'openai'));
25
+ const blockers = [];
26
+ const warnings = [];
27
+ let classification = 'unconfigured';
28
+ if (selectedProvider === SKS_ROUTER_PROVIDER_ID)
29
+ classification = 'sks_router';
30
+ else if (selectedProvider === OPENROUTER_PROVIDER_ID)
31
+ classification = 'openrouter';
32
+ else if (selectedProvider === 'codex-lb')
33
+ classification = 'codex_lb';
34
+ else if (designB)
35
+ classification = 'opencodex_design_b';
36
+ else if (!selectedProvider || selectedProvider === 'openai')
37
+ classification = 'native_openai';
38
+ else
39
+ classification = 'external_conflict';
40
+ if (designB && selectedProvider && selectedProvider !== 'openai') {
41
+ classification = 'external_conflict';
42
+ blockers.push('opencodex_design_b_provider_conflict');
43
+ }
44
+ if (openaiBaseUrl && !openaiBaseUrlLoopback && selectedProvider === SKS_ROUTER_PROVIDER_ID) {
45
+ warnings.push('openai_base_url_present_with_sks_router');
46
+ }
47
+ if (designB) {
48
+ warnings.push('opencodex_design_b_routing_owner');
49
+ }
50
+ const sksRouterMayActivate = classification !== 'opencodex_design_b'
51
+ && classification !== 'external_conflict';
52
+ return {
53
+ schema: 'sks.codex-desktop-routing-ownership.v1',
54
+ classification,
55
+ selected_provider: selectedProvider,
56
+ selected_model: selectedModel,
57
+ openai_base_url: openaiBaseUrl,
58
+ openai_base_url_loopback: openaiBaseUrlLoopback,
59
+ opencodex_design_b: designB,
60
+ opencodex_marker_present: markerPresent,
61
+ catalog_path: catalogResolved,
62
+ catalog_is_opencodex_default: catalogIsOpenCodexDefault,
63
+ sks_router_may_activate: sksRouterMayActivate,
64
+ blockers,
65
+ warnings
66
+ };
67
+ }
68
+ export function opencodexDesignBBlocksRouterActivation(ownership, opts = {}) {
69
+ if (opts.forceRoutingOverride === true)
70
+ return null;
71
+ if (ownership.classification === 'opencodex_design_b' || ownership.opencodex_design_b) {
72
+ return 'opencodex_design_b_routing_owner';
73
+ }
74
+ return null;
75
+ }
76
+ function normalizeLoopbackOpenaiBaseUrl(value) {
77
+ try {
78
+ const parsed = new URL(String(value || '').trim());
79
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
80
+ return null;
81
+ if (!isLoopbackHostname(parsed.hostname))
82
+ return null;
83
+ const normalizedPath = parsed.pathname.replace(/\/+$/, '') || '';
84
+ if (normalizedPath && normalizedPath !== '/v1')
85
+ return null;
86
+ return parsed.toString().replace(/\/$/, '');
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
@@ -3,6 +3,7 @@ import { findCodexBinary } from '../codex-adapter.js';
3
3
  import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
4
4
  import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
5
  import { isRecord } from './codex-app-types.js';
6
+ import { messageOf } from '../errors/message.js';
6
7
  export async function probeCodexHookApprovalState(root, input = {}) {
7
8
  const env = input.env || process.env;
8
9
  const sources = [];
@@ -182,6 +183,3 @@ async function persist(root, report, writeReport) {
182
183
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-approval-probe.json'), report).catch(() => undefined);
183
184
  return report;
184
185
  }
185
- function messageOf(err) {
186
- return err instanceof Error ? err.message : String(err);
187
- }
@@ -3,6 +3,7 @@ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
3
  import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
4
4
  import { installManagedCodexHooks } from '../codex-hooks/codex-hook-managed-install.js';
5
5
  import { probeCodexHookApprovalState } from './codex-hook-approval-probe.js';
6
+ import { messageOf } from '../errors/message.js';
6
7
  export async function buildCodexHookLifecycle(input = {}) {
7
8
  const root = path.resolve(input.root || process.cwd());
8
9
  const install = input.apply === true
@@ -58,6 +59,3 @@ export async function buildCodexHookLifecycle(input = {}) {
58
59
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-lifecycle.json'), report).catch(() => undefined);
59
60
  return report;
60
61
  }
61
- function messageOf(err) {
62
- return err instanceof Error ? err.message : String(err);
63
- }