sneakoscope 8.1.2 → 8.2.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 (261) hide show
  1. package/README.md +96 -58
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -51
  5. package/dist/cli/command-manifest-lite.js +2 -1
  6. package/dist/cli/command-registry.js +2 -1
  7. package/dist/cli/global-mode-router.js +1 -1
  8. package/dist/cli/install-helpers-codex-lb-config.js +112 -914
  9. package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
  10. package/dist/cli/install-helpers-install-support.js +4 -20
  11. package/dist/cli/install-helpers.js +36 -2716
  12. package/dist/cli/router.js +9 -9
  13. package/dist/commands/bridge-command-output.js +111 -0
  14. package/dist/commands/bridge.js +320 -0
  15. package/dist/commands/codex-app.js +39 -123
  16. package/dist/commands/doctor-helpers.js +380 -0
  17. package/dist/commands/doctor-profile.js +3 -0
  18. package/dist/commands/doctor.js +114 -873
  19. package/dist/commands/mad-sks.js +0 -4
  20. package/dist/config/skills-manifest.json +1 -1
  21. package/dist/core/agents/agent-effort-policy.js +11 -21
  22. package/dist/core/agents/agent-recursion-guard.js +26 -0
  23. package/dist/core/agents/agent-runner-codex-exec.js +7 -9
  24. package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
  25. package/dist/core/architecture-hardening/state/state-service.js +49 -13
  26. package/dist/core/auto-review.js +15 -9
  27. package/dist/core/codex/codex-config-guard.js +8 -0
  28. package/dist/core/codex-adapter.js +8 -10
  29. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
  30. package/dist/core/codex-app/codex-app-launcher.js +10 -12
  31. package/dist/core/codex-app/codex-app-restart.js +10 -12
  32. package/dist/core/codex-app/menubar/constants.js +2 -5
  33. package/dist/core/codex-app/menubar/global-install.js +3 -2
  34. package/dist/core/codex-app/menubar/installer/result.js +23 -0
  35. package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
  36. package/dist/core/codex-app/menubar/installer.js +5 -96
  37. package/dist/core/codex-app/menubar/migration.js +1 -42
  38. package/dist/core/codex-app/openrouter-provider.js +0 -78
  39. package/dist/core/codex-app.js +97 -360
  40. package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
  41. package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
  42. package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
  43. package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
  44. package/dist/core/codex-control/codex-task-runner.js +16 -60
  45. package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
  46. package/dist/core/codex-exec-output-schema.js +11 -13
  47. package/dist/core/codex-lb/bridge-contracts.js +2 -0
  48. package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
  49. package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
  50. package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
  51. package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
  52. package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
  53. package/dist/core/codex-lb/capability-runner.js +505 -201
  54. package/dist/core/codex-lb/codex-lb-env.js +179 -12
  55. package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
  56. package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
  57. package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
  58. package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
  59. package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
  60. package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
  61. package/dist/core/codex-lb/combined-catalog.js +12 -0
  62. package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
  63. package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
  64. package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
  65. package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
  66. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
  67. package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
  68. package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
  69. package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
  70. package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
  71. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
  72. package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
  73. package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
  74. package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
  75. package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
  76. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
  77. package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
  78. package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
  79. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
  80. package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
  81. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
  82. package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
  83. package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
  84. package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
  85. package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
  86. package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
  87. package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
  88. package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
  89. package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
  90. package/dist/core/codex-lb/desktop-controller.js +2 -1730
  91. package/dist/core/codex-lb/desktop-service.js +462 -627
  92. package/dist/core/codex-lb/migration-receipt.js +560 -118
  93. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
  94. package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
  95. package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
  96. package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
  97. package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
  98. package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
  99. package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
  100. package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
  101. package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
  102. package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
  103. package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
  104. package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
  105. package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
  106. package/dist/core/codex-lb/provider-credentials/types.js +1 -0
  107. package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
  108. package/dist/core/codex-lb/provider-credentials.js +5 -0
  109. package/dist/core/codex-lb/provider-registry.js +323 -0
  110. package/dist/core/codex-lb/provider-route-policy.js +118 -0
  111. package/dist/core/codex-lb/request-route-resolver.js +172 -0
  112. package/dist/core/codex-lb/route-index.js +148 -0
  113. package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
  114. package/dist/core/commands/basic-cli.js +1 -1
  115. package/dist/core/commands/check-command.js +3 -10
  116. package/dist/core/commands/gate-result-contract.js +10 -14
  117. package/dist/core/commands/intent-normalization/normalizer.js +1 -1
  118. package/dist/core/commands/mad-sks-command.js +2 -6
  119. package/dist/core/commands/release-command.js +11 -1
  120. package/dist/core/commands/telegram-command.js +46 -6
  121. package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
  122. package/dist/core/doctor/skill-legacy-surface.js +5 -5
  123. package/dist/core/evidence/v2/evidence-key.js +30 -3
  124. package/dist/core/feature-fixtures.js +8 -1
  125. package/dist/core/feature-registry.js +3 -2
  126. package/dist/core/hooks-runtime/stop-finalization.js +11 -1
  127. package/dist/core/hooks-runtime/subagent-context.js +15 -1
  128. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
  129. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  130. package/dist/core/hooks-runtime.js +73 -87
  131. package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
  132. package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
  133. package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
  134. package/dist/core/imagegen/imagegen-capability.js +43 -151
  135. package/dist/core/init/skills/filesystem.js +89 -0
  136. package/dist/core/init/skills/inventory.js +97 -0
  137. package/dist/core/init/skills/manifest.js +151 -0
  138. package/dist/core/init/skills.js +5 -325
  139. package/dist/core/init.js +0 -63
  140. package/dist/core/install/installed-package-smoke.js +1 -1
  141. package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
  142. package/dist/core/policy/latest-version-guidance.js +15 -2
  143. package/dist/core/preflight/parallel-preflight-engine.js +27 -51
  144. package/dist/core/provider/provider-badge.js +9 -4
  145. package/dist/core/provider/provider-context.js +57 -107
  146. package/dist/core/providers/openrouter/openrouter-account.js +3 -1
  147. package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
  148. package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
  149. package/dist/core/release/gate-manifest.js +0 -1
  150. package/dist/core/release/main-push-guard.js +13 -0
  151. package/dist/core/release/main-push-receipt.js +11 -0
  152. package/dist/core/release/physical-release-gates.js +664 -61
  153. package/dist/core/release/publish-preflight.js +3 -3
  154. package/dist/core/release/release-gate-batch-runner.js +19 -6
  155. package/dist/core/release/release-gate-dag.js +33 -6
  156. package/dist/core/release/release-gate-node.js +1 -3
  157. package/dist/core/release/release-gate-output-contract.js +55 -0
  158. package/dist/core/release/release-pack-contract.js +1 -1
  159. package/dist/core/release/release-pack-receipt.js +209 -8
  160. package/dist/core/release/release-real-contract.js +1 -1
  161. package/dist/core/release/stage-publish.js +106 -28
  162. package/dist/core/retention/temp-lease.js +204 -0
  163. package/dist/core/retention.js +18 -195
  164. package/dist/core/routes.js +1 -2
  165. package/dist/core/safety/intent-contract/intent-contract.js +3 -3
  166. package/dist/core/subagents/naruto-host-credentials.js +6 -1
  167. package/dist/core/subagents/official-subagent-config.js +27 -9
  168. package/dist/core/subagents/official-subagent-runner.js +14 -31
  169. package/dist/core/subagents/wave-lifecycle.js +36 -8
  170. package/dist/core/subagents/wave-parent-guidance.js +2 -1
  171. package/dist/core/telegram/doctor.js +2 -2
  172. package/dist/core/telegram/liveness.js +10 -4
  173. package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
  174. package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
  175. package/dist/core/update/update-migration-state/session-state-split.js +247 -0
  176. package/dist/core/update/update-migration-state/simple-stages.js +46 -0
  177. package/dist/core/update/update-migration-state.js +4 -481
  178. package/dist/core/version.js +1 -1
  179. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
  180. package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
  181. package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
  182. package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
  183. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
  184. package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
  185. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
  186. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
  187. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
  188. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
  189. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  190. package/dist/scripts/canonical-test-runner.js +7 -1
  191. package/dist/scripts/check-runtime-schemas.js +7 -2
  192. package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
  193. package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
  194. package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
  195. package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
  196. package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
  197. package/dist/scripts/desktop-bridge-transport-client.js +116 -0
  198. package/dist/scripts/desktop-bridge-unification-check.js +276 -0
  199. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
  200. package/dist/scripts/gate-policy-audit-check.js +52 -2
  201. package/dist/scripts/provider-badge-context-check.js +55 -12
  202. package/dist/scripts/publish-preflight.js +1 -2
  203. package/dist/scripts/release-affected-selector-check.js +1 -2
  204. package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
  205. package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
  206. package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
  207. package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
  208. package/dist/scripts/release-gate-batch-runner-check.js +9 -0
  209. package/dist/scripts/release-gate-dag-runner-check.js +6 -0
  210. package/dist/scripts/release-gate-dag-runner.js +4 -2
  211. package/dist/scripts/release-metadata-check.js +16 -3
  212. package/dist/scripts/release-pack-receipt.js +29 -6
  213. package/dist/scripts/release-physical-gates-check.js +39 -0
  214. package/dist/scripts/release-real-check.js +7 -5
  215. package/package.json +6 -8
  216. package/release-gates.v2.json +156 -157
  217. package/runtime-required-scripts.json +11 -11
  218. package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
  219. package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
  220. package/schemas/release/release-gate-node.schema.json +2 -3
  221. package/dist/cli/codex-lb-setup-warning-output.js +0 -17
  222. package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
  223. package/dist/commands/codex-lb.js +0 -1232
  224. package/dist/core/architecture-hardening/migration/migration.js +0 -111
  225. package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
  226. package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
  227. package/dist/core/codex-app/child-policy/child-policy.js +0 -35
  228. package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
  229. package/dist/core/codex-app/glm-model-profile.js +0 -1
  230. package/dist/core/codex-app/glm-profile-installer.js +0 -191
  231. package/dist/core/codex-app/glm-profile-schema.js +0 -42
  232. package/dist/core/codex-app/multi-provider-router.js +0 -391
  233. package/dist/core/codex-app/openrouter-activate.js +0 -405
  234. package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
  235. package/dist/core/codex-app/provider-child-policy.js +0 -64
  236. package/dist/core/codex-app/provider-mode.js +0 -91
  237. package/dist/core/codex-app/provider-session-policy.js +0 -85
  238. package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
  239. package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
  240. package/dist/core/codex-lb/codex-lb-setup.js +0 -293
  241. package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
  242. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
  243. package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
  244. package/dist/core/codex-lb/desktop-mode.js +0 -32
  245. package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
  246. package/dist/core/codex-lb/legacy-migration.js +0 -576
  247. package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
  248. package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
  249. package/dist/core/codex-lb/routing-truth.js +0 -327
  250. package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
  251. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
  252. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
  253. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
  254. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
  255. package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
  256. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
  257. package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
  258. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
  259. package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
  260. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
  261. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { COMMAND_ALIASES_LITE, COMMAND_MANIFEST_BY_NAME } from '../cli/command-manifest-lite.js';
7
+ import { normalizeCommand } from '../cli/router.js';
8
+ import { inspectHistoricalDesktopBridgeIntent, migrateDesktopBridgeConfig } from '../core/codex-lb/desktop-bridge-migration.js';
9
+ import { rollbackDesktopBridgeUnificationReceipt } from '../core/codex-lb/migration-receipt.js';
10
+ import { configureProviderCredential, resolveAllProviderCredentials } from '../core/codex-lb/provider-credentials.js';
11
+ import { buildStoredBridgeProviderRegistry, resolveBridgeProviderRegistry, serializeStoredBridgeProviderRegistry } from '../core/codex-lb/provider-registry.js';
12
+ import { buildBridgeRoutingPolicy, readBridgeRoutingPolicy } from '../core/codex-lb/provider-route-policy.js';
13
+ import { buildBridgeRouteIndex } from '../core/codex-lb/route-index.js';
14
+ import { openRouterSecretPaths } from '../core/providers/openrouter/openrouter-secret-store.js';
15
+ import { emitGate } from './gate-lib.js';
16
+ const clientCapability = 'A'.repeat(43);
17
+ const bridgeBaseUrl = `http://127.0.0.1:47821/__sks/client/${clientCapability}/backend-api/codex`;
18
+ const codexLbEndpoint = 'https://lb.example.test/backend-api/codex';
19
+ const initialCodexLbKey = 'fixture-codex-lb-key-initial-123456789';
20
+ const initialOpenRouterKey = 'fixture-openrouter-key-initial-987654321';
21
+ const rotatedCodexLbKey = 'fixture-codex-lb-key-rotated-234567891';
22
+ const rotatedOpenRouterKey = 'fixture-openrouter-key-rotated-876543219';
23
+ const initialOauthAccess = 'fixture-oauth-access-initial';
24
+ const initialOauthRefresh = 'fixture-oauth-refresh-initial';
25
+ const rotatedOauthAccess = 'fixture-oauth-access-rotated';
26
+ const rotatedOauthRefresh = 'fixture-oauth-refresh-rotated';
27
+ const allFixtureSecrets = [
28
+ initialCodexLbKey,
29
+ initialOpenRouterKey,
30
+ rotatedCodexLbKey,
31
+ rotatedOpenRouterKey,
32
+ initialOauthAccess,
33
+ initialOauthRefresh,
34
+ rotatedOauthAccess,
35
+ rotatedOauthRefresh
36
+ ];
37
+ const temporaryRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-desktop-bridge-unification-gate-'));
38
+ let failed = null;
39
+ try {
40
+ const home = path.join(temporaryRoot, 'home');
41
+ const codexHome = path.join(home, '.codex');
42
+ const configPath = path.join(codexHome, 'config.toml');
43
+ const authPath = path.join(codexHome, 'auth.json');
44
+ const codexLbEnvPath = path.join(codexHome, 'sks-codex-lb.env');
45
+ const openRouterPaths = openRouterSecretPaths({ HOME: home });
46
+ const routePolicyPath = path.join(codexHome, 'sks', 'sks-bridge-route-policy.json');
47
+ const providerRegistryPath = path.join(codexHome, 'sks', 'sks-bridge-provider-registry.json');
48
+ const combinedCatalogPath = path.join(codexHome, 'sks', 'sks-bridge-catalog.json');
49
+ const historicalConfig = [
50
+ '# sks-codex-lb-managed-provider-selection',
51
+ 'model_provider = "codex-lb"',
52
+ '',
53
+ '[model_providers.codex-lb]',
54
+ 'name = "codex-lb"',
55
+ `base_url = "${codexLbEndpoint}"`,
56
+ 'env_key = "CODEX_LB_API_KEY"',
57
+ 'requires_openai_auth = false',
58
+ '',
59
+ '[model_providers.openrouter]',
60
+ 'name = "OpenRouter"',
61
+ 'base_url = "https://openrouter.ai/api/v1"',
62
+ 'requires_openai_auth = false',
63
+ ''
64
+ ].join('\n');
65
+ const initialAuth = authJson(initialOauthAccess, initialOauthRefresh);
66
+ await fs.mkdir(codexHome, { recursive: true, mode: 0o700 });
67
+ await fs.writeFile(configPath, historicalConfig, { mode: 0o600 });
68
+ await fs.writeFile(authPath, initialAuth, { mode: 0o600 });
69
+ await configureProviderCredential({
70
+ provider_id: 'codex-lb',
71
+ api_key: initialCodexLbKey,
72
+ host: codexLbEndpoint,
73
+ home,
74
+ processEnv: {}
75
+ });
76
+ await configureProviderCredential({
77
+ provider_id: 'openrouter',
78
+ api_key: initialOpenRouterKey,
79
+ home,
80
+ processEnv: { HOME: home },
81
+ openRouterPaths
82
+ });
83
+ const initialCredentials = await resolveAllProviderCredentials({
84
+ codexLb: {
85
+ home,
86
+ processEnv: {},
87
+ validation: { state: 'ready', checked_at: '2026-08-06T00:00:00.000Z' }
88
+ },
89
+ openrouter: {
90
+ processEnv: { HOME: home },
91
+ openRouterPaths,
92
+ validation: { state: 'ready', checked_at: '2026-08-06T00:00:00.000Z' }
93
+ }
94
+ });
95
+ assert.equal(initialCredentials['codex-lb'].secret, initialCodexLbKey);
96
+ assert.equal(initialCredentials.openrouter.secret, initialOpenRouterKey);
97
+ assert.equal(initialCredentials['codex-lb'].state, 'ready');
98
+ assert.equal(initialCredentials.openrouter.state, 'ready');
99
+ const intent = inspectHistoricalDesktopBridgeIntent(historicalConfig);
100
+ assert.deepEqual(intent.blockers, []);
101
+ assert.equal(intent.default_provider_id, 'codex-lb');
102
+ assert.equal(intent.providers['codex-lb'].enabled, true);
103
+ assert.equal(intent.providers.openrouter.enabled, true);
104
+ const storedRegistry = buildStoredBridgeProviderRegistry({
105
+ credentials: initialCredentials,
106
+ overrides: intent.providers
107
+ });
108
+ const registryText = serializeStoredBridgeProviderRegistry(storedRegistry);
109
+ const registry = await resolveBridgeProviderRegistry({
110
+ home,
111
+ registryPath: providerRegistryPath,
112
+ credentials: initialCredentials,
113
+ storedRegistry
114
+ });
115
+ assert.equal(registry.profiles['codex-lb'].state, 'ready');
116
+ assert.equal(registry.profiles.openrouter.state, 'ready');
117
+ assertSecretFree(JSON.stringify({ credentials: initialCredentials, registry }), allFixtureSecrets);
118
+ assertSecretFree(registryText, allFixtureSecrets);
119
+ const routeIndex = buildBridgeRouteIndex({
120
+ models: [],
121
+ providers: {
122
+ 'codex-lb': {
123
+ catalog_generation: 'fixture-codex-lb-catalog',
124
+ credential_fingerprint: initialCredentials['codex-lb'].fingerprint,
125
+ state: initialCredentials['codex-lb'].state
126
+ },
127
+ openrouter: {
128
+ catalog_generation: 'fixture-openrouter-catalog',
129
+ credential_fingerprint: initialCredentials.openrouter.fingerprint,
130
+ state: initialCredentials.openrouter.state
131
+ }
132
+ },
133
+ created_at: '2026-08-06T00:00:00.000Z'
134
+ }).route_index;
135
+ const routePolicy = buildBridgeRoutingPolicy({
136
+ route_index: routeIndex,
137
+ catalog_generation: 'fixture-combined-catalog',
138
+ default_provider_id: intent.default_provider_id,
139
+ changed_at: '2026-08-06T00:00:00.000Z'
140
+ });
141
+ const routePolicyText = `${JSON.stringify(routePolicy, null, 2)}\n`;
142
+ assert.equal(routePolicy.fallback, 'none');
143
+ assert.deepEqual(routePolicy.model_routes, {});
144
+ const initialCodexLbBytes = await fs.readFile(codexLbEnvPath);
145
+ const initialOpenRouterBytes = await fs.readFile(openRouterPaths.keyPath);
146
+ const first = await migrateDesktopBridgeConfig({
147
+ home,
148
+ bridgeBaseUrl,
149
+ combinedCatalogPath,
150
+ metadataUpdates: [
151
+ { kind: 'provider_registry', path: providerRegistryPath, text: registryText },
152
+ { kind: 'route_policy', path: routePolicyPath, text: routePolicyText }
153
+ ],
154
+ now: new Date('2026-08-06T00:01:00.000Z')
155
+ });
156
+ assert.equal(first.ok, true);
157
+ assert.equal(first.status, 'migrated');
158
+ assert.ok(first.receipt_path);
159
+ assert.deepEqual(await fs.readFile(authPath), Buffer.from(initialAuth));
160
+ assert.deepEqual(await fs.readFile(codexLbEnvPath), initialCodexLbBytes);
161
+ assert.deepEqual(await fs.readFile(openRouterPaths.keyPath), initialOpenRouterBytes);
162
+ const loadedPolicy = await readBridgeRoutingPolicy(routePolicyPath);
163
+ assert.deepEqual(loadedPolicy.blockers, []);
164
+ assert.equal(loadedPolicy.policy?.default_provider_id, 'codex-lb');
165
+ assert.equal(loadedPolicy.policy?.fallback, 'none');
166
+ assert.deepEqual(loadedPolicy.policy?.model_routes, {});
167
+ const migratedConfig = await fs.readFile(configPath, 'utf8');
168
+ const receiptText = await fs.readFile(first.receipt_path, 'utf8');
169
+ const receiptEntries = (await fs.readdir(path.dirname(first.receipt_path))).sort();
170
+ assertSecretFree(migratedConfig, allFixtureSecrets);
171
+ assertSecretFree(await fs.readFile(routePolicyPath, 'utf8'), allFixtureSecrets);
172
+ assertSecretFree(await fs.readFile(providerRegistryPath, 'utf8'), allFixtureSecrets);
173
+ assertSecretFree(receiptText, allFixtureSecrets);
174
+ assert.equal(receiptText.includes(clientCapability), false);
175
+ const second = await migrateDesktopBridgeConfig({
176
+ home,
177
+ bridgeBaseUrl,
178
+ combinedCatalogPath,
179
+ metadataUpdates: [
180
+ { kind: 'provider_registry', path: providerRegistryPath, text: registryText },
181
+ { kind: 'route_policy', path: routePolicyPath, text: routePolicyText }
182
+ ],
183
+ now: new Date('2026-08-06T00:02:00.000Z')
184
+ });
185
+ assert.equal(second.ok, true);
186
+ assert.equal(second.status, 'already_migrated');
187
+ assert.equal(second.receipt_path, null);
188
+ assert.equal(await fs.readFile(configPath, 'utf8'), migratedConfig);
189
+ assert.deepEqual((await fs.readdir(path.dirname(first.receipt_path))).sort(), receiptEntries);
190
+ const rotatedAuth = authJson(rotatedOauthAccess, rotatedOauthRefresh);
191
+ await fs.writeFile(authPath, rotatedAuth, { mode: 0o600 });
192
+ await configureProviderCredential({
193
+ provider_id: 'codex-lb',
194
+ api_key: rotatedCodexLbKey,
195
+ host: codexLbEndpoint,
196
+ home,
197
+ processEnv: {}
198
+ });
199
+ await configureProviderCredential({
200
+ provider_id: 'openrouter',
201
+ api_key: rotatedOpenRouterKey,
202
+ home,
203
+ processEnv: { HOME: home },
204
+ openRouterPaths
205
+ });
206
+ const rollback = await rollbackDesktopBridgeUnificationReceipt({ receiptPath: first.receipt_path });
207
+ assert.equal(rollback.ok, true);
208
+ assert.equal(rollback.status, 'rolled_back');
209
+ assert.equal(rollback.credentials_overwritten, false);
210
+ assert.equal(rollback.auth_overwritten, false);
211
+ assert.equal(await fs.readFile(configPath, 'utf8'), historicalConfig);
212
+ await assert.rejects(fs.access(routePolicyPath));
213
+ await assert.rejects(fs.access(providerRegistryPath));
214
+ assert.equal(await fs.readFile(authPath, 'utf8'), rotatedAuth);
215
+ const rotatedCredentials = await resolveAllProviderCredentials({
216
+ codexLb: { home, processEnv: {} },
217
+ openrouter: { processEnv: { HOME: home }, openRouterPaths }
218
+ });
219
+ assert.equal(rotatedCredentials['codex-lb'].secret, rotatedCodexLbKey);
220
+ assert.equal(rotatedCredentials.openrouter.secret, rotatedOpenRouterKey);
221
+ assertSecretFree(await fs.readFile(first.receipt_path, 'utf8'), allFixtureSecrets);
222
+ const retiredCommand = normalizeCommand(['codex-lb', 'status']);
223
+ assert.equal(retiredCommand.command, null);
224
+ assert.equal(retiredCommand.aliasTarget, null);
225
+ assert.equal(Object.hasOwn(COMMAND_ALIASES_LITE, 'codex-lb'), false);
226
+ assert.equal(Object.hasOwn(COMMAND_MANIFEST_BY_NAME, 'codex-lb'), false);
227
+ emitGate('desktop-bridge:unification', {
228
+ evidence_kind: 'hermetic_fixture',
229
+ fixture: true,
230
+ real_execution: false,
231
+ real_evidence_claimed: false,
232
+ checks: {
233
+ dual_credentials_coexist: true,
234
+ serialized_metadata_secret_free: true,
235
+ historical_config_migration_idempotent: true,
236
+ rollback_preserves_rotated_credentials_and_oauth: true,
237
+ retired_codex_lb_command_unknown_without_alias: true,
238
+ fallback_route_absent: true
239
+ },
240
+ limitations: [
241
+ 'Temporary-home fixture evidence only; this does not prove a live Codex Desktop or provider execution.'
242
+ ]
243
+ });
244
+ }
245
+ catch (error) {
246
+ failed = error;
247
+ }
248
+ finally {
249
+ await fs.rm(temporaryRoot, { recursive: true, force: true });
250
+ }
251
+ if (failed) {
252
+ const message = failed instanceof Error ? failed.message : String(failed);
253
+ console.error(JSON.stringify({
254
+ schema: 'sks.release-gate.v1',
255
+ ok: false,
256
+ gate: 'desktop-bridge:unification',
257
+ evidence_kind: 'hermetic_fixture',
258
+ fixture: true,
259
+ real_execution: false,
260
+ real_evidence_claimed: false,
261
+ blocker: message
262
+ }, null, 2));
263
+ process.exitCode = 1;
264
+ }
265
+ function authJson(accessToken, refreshToken) {
266
+ return `${JSON.stringify({
267
+ auth_mode: 'chatgpt',
268
+ account_id: 'fixture-desktop-account',
269
+ tokens: { access_token: accessToken, refresh_token: refreshToken }
270
+ }, null, 2)}\n`;
271
+ }
272
+ function assertSecretFree(text, secrets) {
273
+ assert.equal(text.includes('"secret"'), false);
274
+ for (const secret of secrets)
275
+ assert.equal(text.includes(secret), false);
276
+ }
@@ -30,23 +30,27 @@ const unsafePlan = await repairCodexAppFastUi(unsafeRoot, { codexHome: unsafeCod
30
30
  const unsafeAfter = await fs.readFile(path.join(unsafeCodexHome, 'config.toml'), 'utf8');
31
31
  const ok = plan.fast_selector === 'manual_action_required'
32
32
  && plan.provider_selector === 'manual_action_required'
33
- && plan.selected_provider_blockers.includes('codex_lb_api_key_missing')
34
- && plan.selected_provider_blockers.includes('codex_lb_base_url_missing')
35
- && plan.provider_actions.includes('sks codex-app set-openrouter-key --api-key-stdin')
36
- && plan.provider_actions.includes('sks codex-lb setup --host <domain> --api-key-stdin --yes')
33
+ && plan.selected_provider_blockers.includes('bridge_active_route_not_selected')
34
+ && plan.selected_provider_blockers.includes('combined_catalog_active_generation_missing')
35
+ && plan.selected_provider_blockers.includes('bridge_route_policy_missing')
36
+ && plan.provider_actions.includes('sks bridge provider configure')
37
+ && plan.provider_actions.includes('sks bridge provider validate')
38
+ && plan.provider_actions.includes('sks bridge provider enable')
39
+ && plan.provider_actions.includes('sks bridge catalog sync')
40
+ && plan.provider_actions.includes('sks bridge route set-default')
37
41
  && plan.safe_auto_apply === true
38
42
  && repaired.fast_selector === 'repaired'
39
43
  && repaired.provider_selector === 'manual_action_required'
40
- && repaired.selected_provider_blockers.includes('codex_lb_api_key_missing')
41
- && repaired.selected_provider_blockers.includes('codex_lb_base_url_missing')
42
- && repaired.selected_provider_blockers.includes('codex_lb_model_catalog_json_unselected')
44
+ && repaired.selected_provider_blockers.includes('bridge_active_route_not_selected')
45
+ && repaired.selected_provider_blockers.includes('combined_catalog_active_generation_missing')
46
+ && repaired.selected_provider_blockers.includes('bridge_route_policy_missing')
43
47
  && repaired.safe_auto_apply === true
44
48
  && backups.length >= 1
45
49
  && /^model\s*=\s*"future-codex-model"$/m.test(projectAfter.split(/\n\s*\[/)[0] || '')
46
50
  && /^model_reasoning_effort\s*=\s*"medium"$/m.test(projectAfter.split(/\n\s*\[/)[0] || '')
47
51
  && !/^model\s*=/m.test(homeTopLevel)
48
52
  && !/^model_reasoning_effort\s*=/m.test(homeTopLevel)
49
- && /^model_provider\s*=\s*"codex-lb"$/m.test(homeTopLevel)
53
+ && !/^model_provider\s*=/m.test(homeTopLevel)
50
54
  && /model_provider\s*=\s*"codex-lb"/.test(projectAfter)
51
55
  && /^service_tier\s*=\s*"fast"$/m.test(homeTopLevel)
52
56
  && /fast_mode = false/.test(homeAfter)
@@ -18,8 +18,6 @@ const splitReviewFiles = [
18
18
  'src/core/recallpulse.ts'
19
19
  ];
20
20
  const policyCallsiteAllowlist = new Map([
21
- ['codex_config_write|src/cli/install-helpers-codex-lb-selftest-chain.ts|runCodexLbLaunchChainSelftest|writeTextAtomic', 'isolated launch-chain selftest config'],
22
- ['codex_config_write|src/cli/install-helpers-codex-lb-selftest.ts|selftestCodexLb|writeTextAtomic', 'isolated Codex LB selftest config'],
23
21
  ['mkdtemp|src/core/codex-control/codex-sdk-env-policy.ts|prepareNativeCodexAuthBridge|mkdtemp', 'identity-checked 0700 OAuth bridge root with explicit cleanup proof'],
24
22
  ['mkdtemp|src/core/mcp-config/codex-cli-adapter.ts|CodexMcpCliAdapter|mkdtemp', 'isolated official CLI transform HOME'],
25
23
  ['mkdtemp|src/core/perf/release-latency-slo.ts|runReleaseLatencySlo|mkdtemp', 'run-local latency fixture root'],
@@ -39,6 +37,7 @@ const directCodexConfigWrites = scanDirectCodexConfigWrites();
39
37
  const commandGateContract = scanCommandGateContract();
40
38
  const directMkdtempCalls = scanDirectMkdtempCalls();
41
39
  const rustTempdirCalls = scanRustTempdirCalls();
40
+ const releaseEvidenceArchitecture = scanReleaseEvidenceArchitecture();
42
41
  const unusedPolicyCallsiteAllowlist = [...policyCallsiteAllowlist.keys()].filter((key) => !policyCallsiteAllowlistHits.has(key));
43
42
  if (releaseGates.length > 200)
44
43
  blockers.push(`release_preset_gate_budget_exceeded:${releaseGates.length}`);
@@ -61,6 +60,8 @@ if (directMkdtempCalls.length)
61
60
  blockers.push(`direct_mkdtemp_callsite:${directMkdtempCalls[0].file}:${directMkdtempCalls[0].line}`);
62
61
  if (rustTempdirCalls.length)
63
62
  blockers.push(`rust_tempdir_without_raii:${rustTempdirCalls[0].file}:${rustTempdirCalls[0].line}`);
63
+ if (!releaseEvidenceArchitecture.ok)
64
+ blockers.push(`release_evidence_architecture:${releaseEvidenceArchitecture.issues[0]}`);
64
65
  if (unusedPolicyCallsiteAllowlist.length)
65
66
  blockers.push(`unused_policy_callsite_allowlist:${unusedPolicyCallsiteAllowlist[0]}`);
66
67
  for (const row of splitReviewLineCounts) {
@@ -82,8 +83,57 @@ const result = {
82
83
  direct_mkdtemp_callsites: directMkdtempCalls.slice(0, 20),
83
84
  policy_callsite_allowlist: { used: policyCallsiteAllowlistHits.size, unused: unusedPolicyCallsiteAllowlist },
84
85
  rust_tempdir_raii: { ok: rustTempdirCalls.length === 0, direct_tempdir_callsites: rustTempdirCalls.slice(0, 20) },
86
+ release_evidence_architecture: releaseEvidenceArchitecture,
85
87
  blockers
86
88
  };
89
+ function scanReleaseEvidenceArchitecture() {
90
+ const issues = [];
91
+ const recovery = gates.find((gate) => gate.id === 'test:codex-runtime-recovery');
92
+ const recoveryInputs = Array.isArray(recovery?.cache?.inputs) ? recovery.cache.inputs.map(String) : [];
93
+ if (!recoveryInputs.includes('src/commands/bridge.ts'))
94
+ issues.push('recovery_cache_missing_active_bridge_command');
95
+ if (recoveryInputs.includes('src/commands/codex-lb.ts'))
96
+ issues.push('recovery_cache_references_retired_bridge_command');
97
+ const runtime = fs.readFileSync(path.join(root, 'runtime-required-scripts.json'), 'utf8');
98
+ const packageJson = fs.readFileSync(path.join(root, 'package.json'), 'utf8');
99
+ const manifest = fs.readFileSync(path.join(root, 'release-gates.v2.json'), 'utf8');
100
+ const retiredCatalogScript = 'codex-lb-catalog-passthrough-check.js';
101
+ if ([runtime, packageJson, manifest].some((text) => text.includes(retiredCatalogScript)))
102
+ issues.push('retired_catalog_passthrough_reference_present');
103
+ if (!runtime.includes('dist/scripts/release-physical-gates-check.js'))
104
+ issues.push('physical_gate_validator_missing_from_runtime_closure');
105
+ if (!packageJson.includes('release-physical-gates-check.js'))
106
+ issues.push('physical_gate_validator_missing_from_package_files');
107
+ const realCheck = fs.readFileSync(path.join(root, 'src', 'scripts', 'release-real-check.ts'), 'utf8');
108
+ if (!/nodeScript\(\s*['"]release-physical-gates-check\.js['"]/.test(realCheck)
109
+ || !realCheck.includes("'sks.release-physical-gates-inspection.v2'"))
110
+ issues.push('release_real_check_not_bound_to_physical_inspection');
111
+ const workflow = fs.readFileSync(path.join(root, '.github', 'workflows', 'publish-npm.yml'), 'utf8');
112
+ const linux = workflow.slice(workflow.indexOf(' linux-release-proof:'), workflow.indexOf(' macos-menubar-proof:'));
113
+ const macos = workflow.slice(workflow.indexOf(' macos-menubar-proof:'), workflow.indexOf(' pack-and-compare:'));
114
+ if (linux.includes('release-physical-gates-check.js') || linux.includes('release:check:full'))
115
+ issues.push('linux_claims_macos_release_evidence');
116
+ const macosProofSequence = [
117
+ 'npm run build:clean --silent',
118
+ 'npm run test:release --silent',
119
+ 'release-gate-dag-runner.js --preset release --full',
120
+ 'release-physical-gates-check.js',
121
+ 'release-real-check.js --skip-release-check',
122
+ 'release-check-stamp.js write'
123
+ ];
124
+ const macosProofPositions = macosProofSequence.map((required) => macos.indexOf(required));
125
+ for (const required of macosProofSequence) {
126
+ if (!macos.includes(required))
127
+ issues.push(`macos_release_evidence_step_missing:${required}`);
128
+ }
129
+ if (macosProofPositions.every((position) => position >= 0)
130
+ && macosProofPositions.some((position, index) => index > 0 && position <= macosProofPositions[index - 1])) {
131
+ issues.push('macos_release_evidence_sequence_invalid');
132
+ }
133
+ if (!workflow.includes('--require-physical-proof'))
134
+ issues.push('main_push_guard_does_not_require_physical_proof');
135
+ return { ok: issues.length === 0, issues, recovery_cache_inputs: recoveryInputs };
136
+ }
87
137
  console.log(JSON.stringify(result, null, 2));
88
138
  if (!result.ok)
89
139
  process.exit(1);
@@ -2,22 +2,65 @@
2
2
  import fs from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { providerBadgeText } from '../core/provider/provider-badge.js';
5
+ import { providerBadgeText, providerPaneLabel } from '../core/provider/provider-badge.js';
6
6
  import { resolveProviderContext } from '../core/provider/provider-context.js';
7
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-provider-context-'));
7
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-provider-badge-'));
8
8
  const codexHome = path.join(root, '.codex-home');
9
9
  await fs.mkdir(codexHome, { recursive: true });
10
10
  await fs.writeFile(path.join(codexHome, 'auth.json'), '{"account_id":"acct_fixture"}\n');
11
- const openai = await resolveProviderContext({ root, codexHome, env: { OPENAI_API_KEY: 'sk-fixture', HOME: root }, route: '$Naruto', serviceTier: 'fast' });
12
- const lb = await resolveProviderContext({ root, codexHome, env: { CODEX_LB_API_KEY: 'lb-fixture', SKS_PROVIDER: 'codex-lb', HOME: root }, route: '$Naruto', serviceTier: 'fast' });
13
- const app = await resolveProviderContext({ root, codexHome, env: { HOME: root }, route: '$Naruto', serviceTier: 'standard' });
14
- const conflict = await resolveProviderContext({ root, codexHome, env: { OPENAI_API_KEY: 'sk-fixture', CODEX_LB_API_KEY: 'lb-fixture', HOME: root } });
15
- const ok = openai.provider === 'openai'
16
- && lb.provider === 'codex-lb'
17
- && app.provider === 'codex-app'
18
- && conflict.conflict
19
- && providerBadgeText(lb) === 'Provider: codex-lb · Fast';
20
- emit({ schema: 'sks.provider-badge-context-check.v1', ok, openai, lb, app, conflict, blockers: ok ? [] : ['provider_badge_context_check_failed'] });
11
+ const bridge = await resolveProviderContext({
12
+ root,
13
+ codexHome,
14
+ env: { HOME: root, CODEX_LB_API_KEY: 'lb-fixture' },
15
+ route: '$Naruto',
16
+ serviceTier: 'fast',
17
+ desktopBridgeStatus: bridgeStatus('codex-lb')
18
+ });
19
+ const openai = await resolveProviderContext({
20
+ root,
21
+ codexHome,
22
+ env: { OPENAI_API_KEY: 'sk-fixture', HOME: root },
23
+ serviceTier: 'fast',
24
+ desktopBridgeStatus: unmanagedStatus()
25
+ });
26
+ const app = await resolveProviderContext({
27
+ root,
28
+ codexHome,
29
+ env: { HOME: root },
30
+ serviceTier: 'standard',
31
+ desktopBridgeStatus: unmanagedStatus()
32
+ });
33
+ const checks = {
34
+ bridge_badge: providerBadgeText(bridge) === 'Provider: Desktop Bridge → codex-lb · Fast',
35
+ bridge_pane: providerPaneLabel(bridge) === 'fast · desktop-bridge/codex-lb',
36
+ openai_badge: providerBadgeText(openai) === 'Provider: OpenAI API · Fast',
37
+ oauth_badge: providerBadgeText(app) === 'Provider: Codex App OAuth · Standard'
38
+ };
39
+ const ok = Object.values(checks).every(Boolean);
40
+ emit({ schema: 'sks.provider-badge-context-check.v2', ok, checks, bridge, openai, app, blockers: ok ? [] : ['provider_badge_context_check_failed'] });
41
+ function bridgeStatus(provider) {
42
+ return {
43
+ schema: 'sks.desktop-bridge-status.v3',
44
+ management: { managed: true, runtime: 'desktop-bridge', state: 'ready', reason: null },
45
+ native_identity: { configured: true },
46
+ providers: {
47
+ 'codex-lb': { credential: { state: provider === 'codex-lb' ? 'ready' : 'not_configured' } },
48
+ openrouter: { credential: { state: provider === 'openrouter' ? 'ready' : 'not_configured' } }
49
+ },
50
+ routing: { policy: { default_provider_id: provider }, selected_route: null, session_pin: null, blockers: [] },
51
+ readiness: { ready: true, blockers: [] }
52
+ };
53
+ }
54
+ function unmanagedStatus() {
55
+ return {
56
+ schema: 'sks.desktop-bridge-status.v3',
57
+ management: { managed: false, runtime: null, state: 'not_installed', reason: 'never_configured' },
58
+ native_identity: { configured: true },
59
+ providers: {},
60
+ routing: { policy: null, selected_route: null, session_pin: null, blockers: [] },
61
+ readiness: { ready: false, blockers: [] }
62
+ };
63
+ }
21
64
  function emit(report) {
22
65
  console.log(JSON.stringify(report, null, 2));
23
66
  if (!report.ok)
@@ -3,10 +3,9 @@ import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { inspectPublishPreflight } from '../core/release/publish-preflight.js';
5
5
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
6
- const dryRun = ['1', 'true'].includes(String(process.env.npm_config_dry_run || process.env.NPM_CONFIG_DRY_RUN || '').toLowerCase());
7
6
  const report = inspectPublishPreflight({
8
7
  root,
9
- requireReleaseTag: !dryRun,
8
+ requireReleaseTag: false,
10
9
  requirePhysicalReleaseGates: false,
11
10
  });
12
11
  console.log(JSON.stringify(report, null, 2));
@@ -46,12 +46,11 @@ const officialSubagentOnly = selectorMod.selectAffectedReleaseGates(root, manife
46
46
  const officialSubagentIds = new Set(officialSubagentOnly.selection.selected_gate_ids.map(String));
47
47
  assertGate(officialSubagentIds.has('naruto:canonical-stop-gate'), 'official subagent source changes must select the canonical Naruto stop gate', officialSubagentOnly.selection);
48
48
  for (const changedFile of [
49
- 'src/core/codex-lb/codex-lb-tool-output-recovery.ts',
49
+ 'src/core/codex-control/desktop-bridge-launch-guard.ts',
50
50
  'src/core/codex/codex-cli-update.ts',
51
51
  'src/core/codex-control/codex-reliability-shield.ts',
52
52
  'src/core/hooks-runtime.ts',
53
53
  'src/commands/codex.ts',
54
- 'src/commands/codex-lb.ts',
55
54
  'src/commands/doctor.ts',
56
55
  'src/core/preflight/parallel-preflight-engine.ts'
57
56
  ]) {
@@ -6,7 +6,7 @@ const governor = await importDist('core/release/release-gate-resource-governor.j
6
6
  const budget = governor.defaultReleaseGateBudget();
7
7
  assertGate(budget['cpu-light'] >= 1 && budget['cpu-light'] <= 4, 'resource governor env requests must not raise the desktop-safe cpu-light cap', budget);
8
8
  assertGate(governor.defaultReleaseGateMaxTotal() >= 1 && governor.defaultReleaseGateMaxTotal() <= 4, 'resource governor default total cap must stay desktop-safe', { max_total: governor.defaultReleaseGateMaxTotal() });
9
- const gate = (id) => ({ id, resource: ['cpu-light'], deps: [], command: 'true', side_effect: 'hermetic', timeout_ms: 1000, cache: { enabled: false, inputs: [] }, isolation: { report_dir: 'per-gate' }, preset: ['release'] });
9
+ const gate = (id) => ({ id, resource: ['cpu-light'], deps: [], command: 'true', side_effect: 'hermetic', timeout_ms: 1000, output_contract: 'sks.gate-result.v2', cache: { enabled: false, inputs: [] }, isolation: { report_dir: 'per-gate' }, preset: ['release'] });
10
10
  const fsGate = (id) => ({ ...gate(id), resource: ['fs-read'] });
11
11
  const timingGate = (id) => ({ ...gate(id), resource: ['timing-sensitive'] });
12
12
  const picked = governor.pickLaunchableReleaseGates({ ready: [gate('a'), gate('b'), gate('c')], running: [], budget });
@@ -17,6 +17,7 @@ const gate = {
17
17
  resource: ['cpu-light'],
18
18
  side_effect: 'hermetic',
19
19
  timeout_ms: 1000,
20
+ output_contract: 'sks.gate-result.v2',
20
21
  cache: { enabled: true, inputs: ['src/core/release/**'] },
21
22
  isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
22
23
  preset: ['release']
@@ -11,6 +11,7 @@ const gate = {
11
11
  resource: ['cpu-light'],
12
12
  side_effect: 'hermetic',
13
13
  timeout_ms: 1000,
14
+ output_contract: 'sks.gate-result.v2',
14
15
  cache: { enabled: true, inputs: ['package.json', 'package-lock.json', 'src/**', 'dist/build-manifest.json'] },
15
16
  isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
16
17
  preset: ['release']
@@ -6,7 +6,7 @@ import { performance } from 'node:perf_hooks';
6
6
  import { runReleaseGateBatch } from '../core/release/release-gate-batch-runner.js';
7
7
  import { assertGate, emitGate, root } from './gate-lib.js';
8
8
  const reportRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-release-full-'));
9
- const gates = Array.from({ length: 80 }, (_, i) => ({ id: `synthetic:${i + 1}`, command: `${process.execPath} -e \"setTimeout(()=>process.exit(0),1000)\"`, deps: [], resource: ['cpu-light', 'fs-read'], side_effect: 'hermetic', timeout_ms: 15000, cache: { enabled: false, inputs: [] }, isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' }, preset: ['release'] }));
9
+ const gates = Array.from({ length: 80 }, (_, i) => ({ id: `synthetic:${i + 1}`, command: `${process.execPath} -e \"setTimeout(()=>process.exit(0),1000)\"`, deps: [], resource: ['cpu-light', 'fs-read'], side_effect: 'hermetic', timeout_ms: 15000, output_contract: 'sks.gate-result.v2', cache: { enabled: false, inputs: [] }, isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' }, preset: ['release'] }));
10
10
  const started = performance.now();
11
11
  const result = await runReleaseGateBatch(root, gates, { concurrency: 40, reportRoot });
12
12
  const wallMs = Math.max(1, Math.round(performance.now() - started));
@@ -13,6 +13,7 @@ const base = {
13
13
  resource: ['cpu-light', 'fs-read'],
14
14
  side_effect: 'hermetic',
15
15
  timeout_ms: 30000,
16
+ output_contract: 'sks.gate-result.v2',
16
17
  cache: { enabled: false, inputs: [] },
17
18
  isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
18
19
  preset: ['release']
@@ -27,6 +28,11 @@ const envProofScript = [
27
28
  ].join(';');
28
29
  const passGate = { ...base, id: 'batch:pass', command: `${process.execPath} -e ${JSON.stringify(envProofScript)}` };
29
30
  const failGate = { ...base, id: 'batch:fail', command: `${process.execPath} -e "process.exit(7)"` };
31
+ const contractFailGate = {
32
+ ...base,
33
+ id: 'batch:contract-fail',
34
+ command: `${process.execPath} -e ${JSON.stringify("console.log(JSON.stringify({schema:'sks.gate-result.v2',contract_mode:'strict',ok:false,blockers:['batch_child_blocker']}))")}`
35
+ };
30
36
  const timeoutGate = { ...base, id: 'batch:timeout', timeout_ms: 50, command: `${process.execPath} -e "process.on('SIGTERM', () => {}); setInterval(() => {}, 1000)"` };
31
37
  const browserGate = { ...base, id: 'batch:browser-real', resource: ['browser-real'], side_effect: 'real-env', command: `${process.execPath} -e "process.exit(0)"` };
32
38
  assertGate(batch.isReleaseGateBatchable(passGate) === true, 'hermetic cpu-light fs-read gate must be batchable');
@@ -36,6 +42,9 @@ assertGate(result.ok === false && result.failed === 1, 'one failed child gate mu
36
42
  assertGate(result.results.some((row) => row.id === 'batch:fail' && row.ok === false && row.exit_code === 7), 'batch result must report exact failed child id', result);
37
43
  assertGate(fs.existsSync(path.join(reportRoot, 'batch-pass', 'result.json')) && fs.existsSync(path.join(reportRoot, 'batch-fail', 'result.json')), 'batch runner must preserve individual result JSON files');
38
44
  assertGate(result.results.some((row) => row.id === 'batch:pass' && row.ok === true), 'batch runner must execute children with hermetic release-gate environment', result);
45
+ assertGate(result.results.every((row) => row.output_contract === 'sks.gate-result.v2' && row.gate_result?.contract_mode === 'strict'), 'batch children must publish the strict current gate-result contract', result);
46
+ const contractFailResult = await batch.runReleaseGateBatch(root, [contractFailGate], { concurrency: 1, reportRoot });
47
+ assertGate(contractFailResult.ok === false && contractFailResult.results[0]?.gate_result?.blockers?.includes('batch_child_blocker'), 'strict child ok:false must fail the batch even with exit zero', contractFailResult);
39
48
  const timeoutResult = await batch.runReleaseGateBatch(root, [timeoutGate], { concurrency: 1, reportRoot });
40
49
  assertGate(timeoutResult.results.some((row) => row.id === 'batch:timeout' && row.ok === false && row.exit_code === 124 && row.timed_out === true), 'batch runner must report timed-out child process trees explicitly', timeoutResult);
41
50
  assertGate(timeoutResult.results.some((row) => row.id === 'batch:timeout' && row.duration_ms >= 1400), 'batch runner must wait for timed-out process tree hard-kill cleanup before resolving', timeoutResult);
@@ -10,6 +10,7 @@ const runnerCli = readText('src/scripts/release-gate-dag-runner.ts');
10
10
  const hermeticEnv = readText('src/core/release/release-gate-hermetic-env.ts');
11
11
  const scheduler = readText('src/core/release/release-gate-scheduler.ts');
12
12
  const cache = readText('src/core/release/release-gate-cache-v2.ts');
13
+ const retiredContractNoteField = ['contract', 'note'].join('_');
13
14
  const releaseCheck = String(pkg.scripts['release:check'] || '');
14
15
  const delegatedReleaseCheck = releaseCheck.match(/^npm run ([^\s&]+)(?:\s+--silent)?$/)?.[1];
15
16
  const effectiveReleaseCheck = delegatedReleaseCheck ? String(pkg.scripts[delegatedReleaseCheck] || '') : releaseCheck;
@@ -19,6 +20,7 @@ assertGate(!/&&\s*npm run\s+\w/.test(effectiveReleaseCheck.replace('npm run buil
19
20
  assertGate(!pkg.scripts['release:check:legacy'], 'retired release:check:legacy alias must stay removed');
20
21
  assertGate(pkg.scripts['release:real-check'] === 'node ./dist/scripts/release-real-check.js', 'release:real-check must expose the canonical environment-dependent proof runner');
21
22
  assertGate(manifest.schema === 'sks.release-gates.v2' && manifest.gates.length >= 10, 'release-gates.v2 manifest must exist with nodes', manifest);
23
+ assertGate(manifest.gates.every((row) => row.output_contract === 'sks.gate-result.v2' && !Object.hasOwn(row, retiredContractNoteField)), 'every release gate must declare only the strict current output contract', manifest.gates.filter((row) => row.output_contract !== 'sks.gate-result.v2' || Object.hasOwn(row, retiredContractNoteField)).map((row) => row.id));
22
24
  for (const gateId of [
23
25
  'scheduler:comprehensive',
24
26
  'doctor:fix-proves-codex-read',
@@ -33,6 +35,8 @@ assertGate(runner.includes('readReleaseGateCacheRecord') && cache.includes('dura
33
35
  assertGate(runner.includes('cpu_time_saved_ms') && runner.includes('peak_running') && runner.includes('budget_snapshot'), 'DAG summary must include CPU time saved, peak running gates, and resource budget proof');
34
36
  assertGate(runner.includes('sks.five-minute-completion-certificate.v1') && runner.includes('sks.affected-gate-graph.v1') && cache.includes('releaseGateProofBankFile'), 'DAG runner must emit completion certificates, affected graph, and proof-bank cache path');
35
37
  assertGate(runner.includes('detached: process.platform') && runner.includes('killGateProcessTree') && runner.includes('timed_out'), 'DAG runner must kill timed-out gate process trees and record timeout evidence');
38
+ assertGate(runner.includes('normalizeReleaseGateOutput') && runner.includes('gate_result: normalized.gate_result'), 'DAG runner must normalize and verify every child through the strict gate-result contract');
39
+ assertGate(runnerCli.includes('GATE_RESULT_CONTRACT_MODE') && runnerCli.includes('contract_mode: GATE_RESULT_CONTRACT_MODE'), 'DAG CLI must emit the strict current gate-result contract');
36
40
  assertGate(runner.includes('pruneOldReleaseGateRunDirs') && runner.includes('SKS_RELEASE_GATE_RUN_RETENTION'), 'DAG runner must prune stale release-gate run reports before they exhaust local disk');
37
41
  assertGate(runnerCli.includes('ensureCurrentMigrationBeforeCommand') && runnerCli.includes('release-gate-runner-preflight'), 'DAG runner CLI must self-heal project migration before hermetic gates run');
38
42
  assertGate(hermeticEnv.includes('SKS_UPDATE_MIGRATION_GATE_DISABLED'), 'hermetic release gates must not rewrite project migration receipts from temporary HOME roots');
@@ -51,6 +55,7 @@ await fsx.writeJsonAtomic(path.join(fixtureRoot, 'release-gates.v2.json'), {
51
55
  resource: ['cpu-light', 'fs-read'],
52
56
  side_effect: 'hermetic',
53
57
  timeout_ms: 50,
58
+ output_contract: 'sks.gate-result.v2',
54
59
  cache: { enabled: false, inputs: [] },
55
60
  isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
56
61
  preset: ['release']
@@ -74,6 +79,7 @@ await fsx.writeJsonAtomic(path.join(cacheRoot, 'release-gates.v2.json'), {
74
79
  resource: ['cpu-light', 'fs-read'],
75
80
  side_effect: 'hermetic',
76
81
  timeout_ms: 5000,
82
+ output_contract: 'sks.gate-result.v2',
77
83
  cache: { enabled: true, inputs: [] },
78
84
  isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
79
85
  preset: ['release']