@1agh/maude 1.0.9 → 1.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 (506) hide show
  1. package/apps/studio/acp/bridge.ts +18 -13
  2. package/apps/studio/acp/probe.ts +268 -16
  3. package/apps/studio/acp/transcript-io.ts +394 -0
  4. package/apps/studio/acp/transcript.ts +103 -38
  5. package/apps/studio/api.ts +139 -26
  6. package/apps/studio/bin/_import-figma.mjs +57 -11
  7. package/apps/studio/bin/_seed-fixture.mjs +163 -0
  8. package/apps/studio/canvas-imports.ts +89 -0
  9. package/apps/studio/client/app.jsx +257 -7
  10. package/apps/studio/client/export-center.jsx +15 -7
  11. package/apps/studio/client/panels/ReadinessList.jsx +7 -1
  12. package/apps/studio/client/panels/SyncPanel.jsx +125 -4
  13. package/apps/studio/client/report-bug.jsx +19 -1
  14. package/apps/studio/client/styles/3-shell-maude.css +34 -0
  15. package/apps/studio/client/styles/4-components.css +26 -0
  16. package/apps/studio/collab/persistence.ts +132 -16
  17. package/apps/studio/collab/registry.ts +10 -18
  18. package/apps/studio/debug-bundle.ts +13 -0
  19. package/apps/studio/dist/client.bundle.js +1128 -1128
  20. package/apps/studio/dist/styles.css +1 -1
  21. package/apps/studio/export-dialog.tsx +54 -0
  22. package/apps/studio/exporters/degraded.ts +19 -3
  23. package/apps/studio/exporters/ghostscript.ts +194 -0
  24. package/apps/studio/exporters/pdf-fonts.ts +261 -0
  25. package/apps/studio/exporters/pdf.ts +152 -3
  26. package/apps/studio/exporters/remote.ts +36 -1
  27. package/apps/studio/fs-watch.ts +45 -1
  28. package/apps/studio/http.ts +7 -1
  29. package/apps/studio/mem.ts +45 -9
  30. package/apps/studio/readiness.ts +42 -7
  31. package/apps/studio/server.ts +11 -4
  32. package/apps/studio/sync/agent.ts +136 -29
  33. package/apps/studio/sync/asset-push.ts +1 -47
  34. package/apps/studio/sync/codec.ts +122 -31
  35. package/apps/studio/sync/cold-start-apply.ts +7 -0
  36. package/apps/studio/sync/cold-start.ts +118 -22
  37. package/apps/studio/sync/comment-identity.ts +68 -0
  38. package/apps/studio/sync/connection-state.ts +15 -1
  39. package/apps/studio/sync/file-ledger.ts +117 -0
  40. package/apps/studio/sync/file-plane.ts +623 -27
  41. package/apps/studio/sync/index.ts +711 -20
  42. package/apps/studio/sync/journal-client.ts +13 -1
  43. package/apps/studio/sync/migrate-seed.ts +40 -3
  44. package/apps/studio/sync/presentation.ts +26 -3
  45. package/apps/studio/sync/retry-after.ts +169 -0
  46. package/apps/studio/sync/seed-progress.ts +218 -0
  47. package/apps/studio/sync/status.ts +122 -5
  48. package/apps/studio/sync/transport-error.ts +111 -0
  49. package/apps/studio/whats-new.json +91 -0
  50. package/cli/bin/maude.mjs +2 -0
  51. package/cli/commands/codex.mjs +191 -0
  52. package/cli/commands/harness.mjs +1051 -0
  53. package/cli/commands/help.mjs +13 -0
  54. package/cli/lib/design-link.mjs +190 -2
  55. package/cli/lib/harness/capabilities.mjs +302 -0
  56. package/cli/lib/harness/codex-runtime.mjs +889 -0
  57. package/cli/lib/harness/compatibility.mjs +27 -0
  58. package/cli/lib/harness/discover-claude.mjs +1393 -0
  59. package/cli/lib/harness/managed-state.mjs +1296 -0
  60. package/cli/lib/harness/model.mjs +86 -0
  61. package/cli/lib/harness/secrets.mjs +390 -0
  62. package/cli/lib/harness/targets/codex.mjs +1141 -0
  63. package/cli/lib/harness/targets/opencode.mjs +807 -0
  64. package/cli/lib/harness/transaction.mjs +1099 -0
  65. package/cli/lib/hubs-config.mjs +35 -8
  66. package/cli/templates/harness/opencode/maude-projector.ts +58 -0
  67. package/docs/harness-capability-matrix.md +125 -0
  68. package/docs/harness-environment-projection.md +95 -0
  69. package/package.json +32 -9
  70. package/plugins/flow/.claude-plugin/config.schema.json +8 -0
  71. package/apps/studio/.ai/cache/_stats.json +0 -7
  72. package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
  73. package/apps/studio/bin/_curl-local.test.mjs +0 -280
  74. package/apps/studio/bin/_pw-launch.test.mjs +0 -90
  75. package/apps/studio/bin/_smart-frames.test.mjs +0 -195
  76. package/apps/studio/bin/_transcribe.test.mjs +0 -80
  77. package/apps/studio/bun.lock +0 -928
  78. package/apps/studio/draw/test/animate.test.ts +0 -172
  79. package/apps/studio/draw/test/brush.test.ts +0 -213
  80. package/apps/studio/draw/test/composition.test.ts +0 -141
  81. package/apps/studio/draw/test/geometry.test.ts +0 -199
  82. package/apps/studio/draw/test/gradient.test.ts +0 -167
  83. package/apps/studio/draw/test/layout.test.ts +0 -120
  84. package/apps/studio/draw/test/morph.test.ts +0 -107
  85. package/apps/studio/draw/test/optimize.test.ts +0 -40
  86. package/apps/studio/draw/test/palette.test.ts +0 -123
  87. package/apps/studio/draw/test/primitives.test.ts +0 -129
  88. package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
  89. package/apps/studio/draw/test/serialize.test.ts +0 -105
  90. package/apps/studio/figma/assets.test.ts +0 -464
  91. package/apps/studio/figma/client.test.ts +0 -395
  92. package/apps/studio/figma/codegen-client.test.ts +0 -276
  93. package/apps/studio/figma/codegen-fonts.test.ts +0 -103
  94. package/apps/studio/figma/codegen-values.test.ts +0 -179
  95. package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
  96. package/apps/studio/figma/fig-decode.test.ts +0 -788
  97. package/apps/studio/figma/fig-differential.test.ts +0 -182
  98. package/apps/studio/figma/fig-translator.test.ts +0 -192
  99. package/apps/studio/figma/fig-vector.test.ts +0 -113
  100. package/apps/studio/figma/from-codegen.test.ts +0 -408
  101. package/apps/studio/figma/sanitize.test.ts +0 -325
  102. package/apps/studio/figma/tailwind-map.test.ts +0 -142
  103. package/apps/studio/figma/to-artboard.test.ts +0 -808
  104. package/apps/studio/figma/to-render.test.ts +0 -180
  105. package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
  106. package/apps/studio/figma/to-strokes.test.ts +0 -705
  107. package/apps/studio/figma/to-tokens.test.ts +0 -321
  108. package/apps/studio/figma/url.test.ts +0 -167
  109. package/apps/studio/footage/schema.test.ts +0 -287
  110. package/apps/studio/generation/adapters/groq.test.ts +0 -80
  111. package/apps/studio/generation/audio-library.test.ts +0 -95
  112. package/apps/studio/generation/captions.test.ts +0 -78
  113. package/apps/studio/generation/elevenlabs.test.ts +0 -223
  114. package/apps/studio/generation/gemini.test.ts +0 -350
  115. package/apps/studio/generation/jobs.test.ts +0 -128
  116. package/apps/studio/generation/keys.test.ts +0 -75
  117. package/apps/studio/generation/prefs.test.ts +0 -95
  118. package/apps/studio/generation/types.test.ts +0 -123
  119. package/apps/studio/generation/whisper-models.test.ts +0 -179
  120. package/apps/studio/test/_helpers.ts +0 -108
  121. package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
  122. package/apps/studio/test/acp-activity.test.ts +0 -154
  123. package/apps/studio/test/acp-ai-activity.test.ts +0 -182
  124. package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
  125. package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
  126. package/apps/studio/test/acp-branch-guard.test.ts +0 -123
  127. package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
  128. package/apps/studio/test/acp-bridge.test.ts +0 -301
  129. package/apps/studio/test/acp-capabilities.test.ts +0 -123
  130. package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
  131. package/apps/studio/test/acp-commands.test.ts +0 -119
  132. package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
  133. package/apps/studio/test/acp-elicitation.test.ts +0 -251
  134. package/apps/studio/test/acp-env.test.ts +0 -95
  135. package/apps/studio/test/acp-mode-banner.test.ts +0 -45
  136. package/apps/studio/test/acp-origin-gate.test.ts +0 -160
  137. package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
  138. package/apps/studio/test/acp-permission.test.ts +0 -278
  139. package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
  140. package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
  141. package/apps/studio/test/acp-session-plugins.test.ts +0 -209
  142. package/apps/studio/test/acp-toolgroup.test.ts +0 -76
  143. package/apps/studio/test/acp-transcript-view.test.ts +0 -71
  144. package/apps/studio/test/acp-transcript.test.ts +0 -238
  145. package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
  146. package/apps/studio/test/acp-usage.test.ts +0 -143
  147. package/apps/studio/test/acp-write-gate.test.ts +0 -323
  148. package/apps/studio/test/acp-write-scope.test.ts +0 -533
  149. package/apps/studio/test/active-state.test.ts +0 -186
  150. package/apps/studio/test/activity.test.ts +0 -189
  151. package/apps/studio/test/ai-activity.test.ts +0 -113
  152. package/apps/studio/test/annotate-write.test.ts +0 -1074
  153. package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
  154. package/apps/studio/test/annotations-align.test.ts +0 -88
  155. package/apps/studio/test/annotations-api.test.ts +0 -145
  156. package/apps/studio/test/annotations-bindings.test.ts +0 -262
  157. package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
  158. package/apps/studio/test/annotations-groups.test.ts +0 -231
  159. package/apps/studio/test/annotations-layer.test.ts +0 -1011
  160. package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
  161. package/apps/studio/test/annotations-snap.test.ts +0 -135
  162. package/apps/studio/test/annotations-text-format.test.ts +0 -396
  163. package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
  164. package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
  165. package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
  166. package/apps/studio/test/artboard-kinds.test.tsx +0 -83
  167. package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
  168. package/apps/studio/test/asset-api.test.ts +0 -149
  169. package/apps/studio/test/assets-s3.test.ts +0 -249
  170. package/apps/studio/test/binary-smoke.test.ts +0 -47
  171. package/apps/studio/test/boot-self-heal.test.ts +0 -87
  172. package/apps/studio/test/browse-posture.test.tsx +0 -107
  173. package/apps/studio/test/bundle-smoke.test.ts +0 -46
  174. package/apps/studio/test/camera-reveal.test.tsx +0 -173
  175. package/apps/studio/test/canvas-artifacts.test.ts +0 -125
  176. package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
  177. package/apps/studio/test/canvas-build.test.ts +0 -141
  178. package/apps/studio/test/canvas-create-api.test.ts +0 -676
  179. package/apps/studio/test/canvas-cursors.test.ts +0 -164
  180. package/apps/studio/test/canvas-edit.test.ts +0 -470
  181. package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
  182. package/apps/studio/test/canvas-header.test.ts +0 -127
  183. package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
  184. package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
  185. package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
  186. package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
  187. package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
  188. package/apps/studio/test/canvas-list-watch.test.ts +0 -388
  189. package/apps/studio/test/canvas-media-drop.test.ts +0 -187
  190. package/apps/studio/test/canvas-meta-api.test.ts +0 -707
  191. package/apps/studio/test/canvas-move-api.test.ts +0 -587
  192. package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
  193. package/apps/studio/test/canvas-pipeline.test.ts +0 -180
  194. package/apps/studio/test/canvas-rects.test.ts +0 -198
  195. package/apps/studio/test/canvas-reorder.test.ts +0 -211
  196. package/apps/studio/test/canvas-route.test.ts +0 -179
  197. package/apps/studio/test/canvas-shell-base.test.ts +0 -107
  198. package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
  199. package/apps/studio/test/canvas-url.test.ts +0 -138
  200. package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
  201. package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
  202. package/apps/studio/test/capture-core.test.ts +0 -32
  203. package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
  204. package/apps/studio/test/chat-attachments.test.ts +0 -117
  205. package/apps/studio/test/chat-context.test.ts +0 -129
  206. package/apps/studio/test/chat-markdown.test.tsx +0 -58
  207. package/apps/studio/test/clip-addressing.test.ts +0 -738
  208. package/apps/studio/test/clip-ops.test.ts +0 -922
  209. package/apps/studio/test/cloud-build.test.ts +0 -95
  210. package/apps/studio/test/cloud-connect-note.test.ts +0 -135
  211. package/apps/studio/test/cloud-endpoints.test.ts +0 -590
  212. package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
  213. package/apps/studio/test/cloud-history-posture.test.ts +0 -230
  214. package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
  215. package/apps/studio/test/cloud-renew.test.ts +0 -205
  216. package/apps/studio/test/cloud-session-role.test.ts +0 -161
  217. package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
  218. package/apps/studio/test/cold-start-apply.test.ts +0 -303
  219. package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
  220. package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
  221. package/apps/studio/test/collab-bridge.test.ts +0 -79
  222. package/apps/studio/test/collab-loopback.test.ts +0 -63
  223. package/apps/studio/test/collab-origin-gate.test.ts +0 -323
  224. package/apps/studio/test/collab-protocol.test.ts +0 -146
  225. package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
  226. package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
  227. package/apps/studio/test/collab-room.test.ts +0 -188
  228. package/apps/studio/test/collab-session-survival.test.tsx +0 -176
  229. package/apps/studio/test/collab-stress.test.ts +0 -129
  230. package/apps/studio/test/comment-mount.test.ts +0 -97
  231. package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
  232. package/apps/studio/test/comments-api.test.ts +0 -277
  233. package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
  234. package/apps/studio/test/comments-overlay.test.ts +0 -117
  235. package/apps/studio/test/compile-entry.test.ts +0 -133
  236. package/apps/studio/test/component-map.test.ts +0 -48
  237. package/apps/studio/test/config-projection.test.ts +0 -104
  238. package/apps/studio/test/config-reload.test.ts +0 -230
  239. package/apps/studio/test/config-version.test.ts +0 -88
  240. package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
  241. package/apps/studio/test/convert-to-absolute.test.ts +0 -333
  242. package/apps/studio/test/credential-grammar.test.ts +0 -107
  243. package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
  244. package/apps/studio/test/csrf-write-guard.test.ts +0 -141
  245. package/apps/studio/test/debug-bundle.test.ts +0 -105
  246. package/apps/studio/test/design-setup-readiness.test.ts +0 -155
  247. package/apps/studio/test/detach-component.test.ts +0 -94
  248. package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
  249. package/apps/studio/test/dom-selection.test.ts +0 -198
  250. package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
  251. package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
  252. package/apps/studio/test/edit-persistence.test.ts +0 -91
  253. package/apps/studio/test/edit-scope-api.test.ts +0 -119
  254. package/apps/studio/test/edit-source-command.test.ts +0 -142
  255. package/apps/studio/test/edit-suppress.test.ts +0 -170
  256. package/apps/studio/test/editing-presence.test.ts +0 -103
  257. package/apps/studio/test/element-resize.test.ts +0 -136
  258. package/apps/studio/test/element-structural-api.test.ts +0 -434
  259. package/apps/studio/test/element-structural-edit.test.ts +0 -750
  260. package/apps/studio/test/ensure-browser.test.ts +0 -92
  261. package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
  262. package/apps/studio/test/export-assemble.test.ts +0 -53
  263. package/apps/studio/test/export-browser-lane.test.ts +0 -126
  264. package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
  265. package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
  266. package/apps/studio/test/export-center.test.tsx +0 -287
  267. package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
  268. package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
  269. package/apps/studio/test/export-lane.test.ts +0 -245
  270. package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
  271. package/apps/studio/test/exporters/canva.test.ts +0 -63
  272. package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
  273. package/apps/studio/test/exporters/endpoint.test.ts +0 -121
  274. package/apps/studio/test/exporters/history.test.ts +0 -107
  275. package/apps/studio/test/exporters/html.test.ts +0 -26
  276. package/apps/studio/test/exporters/jobs.test.ts +0 -342
  277. package/apps/studio/test/exporters/pdf.test.ts +0 -85
  278. package/apps/studio/test/exporters/png.test.ts +0 -103
  279. package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
  280. package/apps/studio/test/exporters/pptx.test.ts +0 -31
  281. package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
  282. package/apps/studio/test/exporters/runtime.test.ts +0 -59
  283. package/apps/studio/test/exporters/scope.test.ts +0 -292
  284. package/apps/studio/test/exporters/svg.test.ts +0 -29
  285. package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
  286. package/apps/studio/test/exporters/zip.test.ts +0 -104
  287. package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
  288. package/apps/studio/test/figjam-v3-model.test.ts +0 -342
  289. package/apps/studio/test/figma-explode.test.ts +0 -438
  290. package/apps/studio/test/figma-provenance.test.ts +0 -108
  291. package/apps/studio/test/figma-routes.test.ts +0 -294
  292. package/apps/studio/test/file-lock.test.ts +0 -84
  293. package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -13
  294. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
  295. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
  296. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
  297. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
  298. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
  299. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
  300. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
  301. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
  302. package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
  303. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
  304. package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
  305. package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
  306. package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
  307. package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
  308. package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
  309. package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
  310. package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
  311. package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
  312. package/apps/studio/test/footage-store.test.ts +0 -100
  313. package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
  314. package/apps/studio/test/fs-watch.test.ts +0 -41
  315. package/apps/studio/test/gemma-models.test.ts +0 -245
  316. package/apps/studio/test/generate-route.test.ts +0 -106
  317. package/apps/studio/test/git-api.test.ts +0 -0
  318. package/apps/studio/test/git-branches.test.ts +0 -341
  319. package/apps/studio/test/git-cloud-posture.test.ts +0 -59
  320. package/apps/studio/test/git-grouping.test.ts +0 -106
  321. package/apps/studio/test/git-lifecycle.test.ts +0 -100
  322. package/apps/studio/test/git-log-format.test.ts +0 -95
  323. package/apps/studio/test/git-safe-rel.test.ts +0 -132
  324. package/apps/studio/test/git-system-engine.test.ts +0 -135
  325. package/apps/studio/test/git-watch.test.ts +0 -97
  326. package/apps/studio/test/github-api.test.ts +0 -577
  327. package/apps/studio/test/grid-track-handles.test.ts +0 -160
  328. package/apps/studio/test/handoff-static-frames.test.ts +0 -215
  329. package/apps/studio/test/handoff.test.ts +0 -329
  330. package/apps/studio/test/history-rollback.test.ts +0 -88
  331. package/apps/studio/test/hmr-broadcast.test.ts +0 -236
  332. package/apps/studio/test/hmr-classify.test.ts +0 -87
  333. package/apps/studio/test/hub-link.test.ts +0 -80
  334. package/apps/studio/test/identity-cache.test.ts +0 -83
  335. package/apps/studio/test/import-asset-browser.test.ts +0 -64
  336. package/apps/studio/test/import-asset-route.test.ts +0 -71
  337. package/apps/studio/test/import-asset.test.ts +0 -427
  338. package/apps/studio/test/import-brand-browser.test.ts +0 -88
  339. package/apps/studio/test/import-brand-route.test.ts +0 -120
  340. package/apps/studio/test/import-brand.test.ts +0 -206
  341. package/apps/studio/test/import-figma.test.ts +0 -667
  342. package/apps/studio/test/import-tokens.test.ts +0 -722
  343. package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
  344. package/apps/studio/test/input-router.test.ts +0 -757
  345. package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
  346. package/apps/studio/test/inspect-selections.test.ts +0 -219
  347. package/apps/studio/test/knob-props-authored.test.ts +0 -87
  348. package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
  349. package/apps/studio/test/locator.test.ts +0 -214
  350. package/apps/studio/test/marquee-overlay.test.ts +0 -94
  351. package/apps/studio/test/media-commit-chain.test.ts +0 -210
  352. package/apps/studio/test/mode-toggle.test.tsx +0 -226
  353. package/apps/studio/test/motion-sample.test.ts +0 -35
  354. package/apps/studio/test/move-artboards-command.test.ts +0 -108
  355. package/apps/studio/test/participants-chrome.test.ts +0 -65
  356. package/apps/studio/test/paths.test.ts +0 -88
  357. package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
  358. package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
  359. package/apps/studio/test/perf-harness.ts +0 -193
  360. package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
  361. package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
  362. package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
  363. package/apps/studio/test/photo-edit-api.test.ts +0 -201
  364. package/apps/studio/test/photo-filters.test.ts +0 -149
  365. package/apps/studio/test/photo-pipeline.test.ts +0 -106
  366. package/apps/studio/test/photo-taxonomy.test.ts +0 -96
  367. package/apps/studio/test/print-marks.test.ts +0 -113
  368. package/apps/studio/test/print-units.test.ts +0 -173
  369. package/apps/studio/test/read-annotations.test.ts +0 -727
  370. package/apps/studio/test/read-only-gate.test.ts +0 -188
  371. package/apps/studio/test/read-only-role.test.ts +0 -95
  372. package/apps/studio/test/readiness.test.ts +0 -226
  373. package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
  374. package/apps/studio/test/reorder-api.test.ts +0 -210
  375. package/apps/studio/test/repo-concurrency.test.ts +0 -146
  376. package/apps/studio/test/repo-lock.test.ts +0 -350
  377. package/apps/studio/test/report-proxy.test.ts +0 -125
  378. package/apps/studio/test/ripple.test.ts +0 -197
  379. package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
  380. package/apps/studio/test/runtime-bundle.test.ts +0 -69
  381. package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
  382. package/apps/studio/test/scaffold-design.test.ts +0 -122
  383. package/apps/studio/test/server-lifecycle.test.ts +0 -38
  384. package/apps/studio/test/session-runtime-state.test.ts +0 -226
  385. package/apps/studio/test/session-scope.test.ts +0 -100
  386. package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
  387. package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
  388. package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
  389. package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
  390. package/apps/studio/test/shared-doc-projection.test.ts +0 -238
  391. package/apps/studio/test/shell-importmap.test.ts +0 -56
  392. package/apps/studio/test/sizing-mode.test.ts +0 -102
  393. package/apps/studio/test/slash-commands.test.ts +0 -117
  394. package/apps/studio/test/snap-distance-pill.test.ts +0 -68
  395. package/apps/studio/test/spacing-handles.test.ts +0 -138
  396. package/apps/studio/test/specimen-select.test.ts +0 -88
  397. package/apps/studio/test/sync-agent.test.ts +0 -660
  398. package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
  399. package/apps/studio/test/sync-asset-push.test.ts +0 -931
  400. package/apps/studio/test/sync-atomic-write.test.ts +0 -62
  401. package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
  402. package/apps/studio/test/sync-autocommit.test.ts +0 -537
  403. package/apps/studio/test/sync-canvas-path.test.ts +0 -200
  404. package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
  405. package/apps/studio/test/sync-codec.test.ts +0 -230
  406. package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
  407. package/apps/studio/test/sync-cold-start.test.ts +0 -304
  408. package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
  409. package/apps/studio/test/sync-connection-state.test.ts +0 -337
  410. package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
  411. package/apps/studio/test/sync-decide-file.test.ts +0 -420
  412. package/apps/studio/test/sync-doc-name.test.ts +0 -281
  413. package/apps/studio/test/sync-echo-guard.test.ts +0 -96
  414. package/apps/studio/test/sync-file-ledger.test.ts +0 -334
  415. package/apps/studio/test/sync-file-membership.test.ts +0 -347
  416. package/apps/studio/test/sync-file-plane.test.ts +0 -976
  417. package/apps/studio/test/sync-file-pull.test.ts +0 -333
  418. package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
  419. package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
  420. package/apps/studio/test/sync-hardening.test.ts +0 -586
  421. package/apps/studio/test/sync-hub-listing.test.ts +0 -46
  422. package/apps/studio/test/sync-hubs-config.test.ts +0 -133
  423. package/apps/studio/test/sync-incident-replay.test.ts +0 -211
  424. package/apps/studio/test/sync-journal.test.ts +0 -176
  425. package/apps/studio/test/sync-meta-codec.test.ts +0 -199
  426. package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
  427. package/apps/studio/test/sync-move-retirement.test.ts +0 -231
  428. package/apps/studio/test/sync-panel-surface.test.ts +0 -153
  429. package/apps/studio/test/sync-path-pull.test.ts +0 -531
  430. package/apps/studio/test/sync-presentation.test.ts +0 -262
  431. package/apps/studio/test/sync-pull-budget.test.ts +0 -169
  432. package/apps/studio/test/sync-remote-docs.test.ts +0 -333
  433. package/apps/studio/test/sync-resync-routes.test.ts +0 -168
  434. package/apps/studio/test/sync-runtime.test.ts +0 -1675
  435. package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
  436. package/apps/studio/test/sync-settings-routes.test.ts +0 -195
  437. package/apps/studio/test/sync-settings.test.ts +0 -151
  438. package/apps/studio/test/sync-status.test.ts +0 -261
  439. package/apps/studio/test/sync-supervisor.test.ts +0 -262
  440. package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
  441. package/apps/studio/test/sync-trash.test.ts +0 -132
  442. package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
  443. package/apps/studio/test/sync-untrusted.test.ts +0 -103
  444. package/apps/studio/test/system-endpoint.test.ts +0 -144
  445. package/apps/studio/test/text-editability-stamp.test.ts +0 -131
  446. package/apps/studio/test/timeline-comp-target.test.ts +0 -139
  447. package/apps/studio/test/timeline-media-cache.test.ts +0 -41
  448. package/apps/studio/test/timeline-parse.test.ts +0 -184
  449. package/apps/studio/test/timeline-scale.test.ts +0 -65
  450. package/apps/studio/test/timeline-snap.test.ts +0 -85
  451. package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
  452. package/apps/studio/test/tool-palette.test.tsx +0 -55
  453. package/apps/studio/test/tour-overlay.test.tsx +0 -90
  454. package/apps/studio/test/trusted-request-host.test.ts +0 -66
  455. package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
  456. package/apps/studio/test/undo-stack.test.ts +0 -244
  457. package/apps/studio/test/use-agent-presence.test.tsx +0 -117
  458. package/apps/studio/test/use-annotation-resize.test.ts +0 -402
  459. package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
  460. package/apps/studio/test/use-artboard-drag.test.ts +0 -324
  461. package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
  462. package/apps/studio/test/use-collab.test.ts +0 -0
  463. package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
  464. package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
  465. package/apps/studio/test/use-selection-set.test.tsx +0 -166
  466. package/apps/studio/test/use-snap-guides.test.ts +0 -271
  467. package/apps/studio/test/use-tool-mode.test.tsx +0 -186
  468. package/apps/studio/test/use-undo-stack.test.tsx +0 -223
  469. package/apps/studio/test/video-asset.test.ts +0 -163
  470. package/apps/studio/test/video-comp-fixture.test.ts +0 -50
  471. package/apps/studio/test/video-comp.test.ts +0 -270
  472. package/apps/studio/test/video-encode-lib.test.ts +0 -63
  473. package/apps/studio/test/video-render-bridge.test.ts +0 -171
  474. package/apps/studio/test/whats-new.test.ts +0 -113
  475. package/apps/studio/test/whisper-setup.test.ts +0 -97
  476. package/apps/studio/test/workspace-containment.test.ts +0 -382
  477. package/apps/studio/test/workspace-signin.test.ts +0 -292
  478. package/apps/studio/test/ws-handshake.test.ts +0 -33
  479. package/cli/commands/cache.test.mjs +0 -166
  480. package/cli/commands/design-link.test.mjs +0 -419
  481. package/cli/commands/design.test.mjs +0 -240
  482. package/cli/commands/doctor.test.mjs +0 -237
  483. package/cli/commands/hub-workspace.test.mjs +0 -275
  484. package/cli/commands/hub.test.mjs +0 -225
  485. package/cli/commands/kg.test.mjs +0 -142
  486. package/cli/commands/preflight.test.mjs +0 -46
  487. package/cli/lib/argv.test.mjs +0 -46
  488. package/cli/lib/cache.test.mjs +0 -303
  489. package/cli/lib/cell-plan.test.mjs +0 -225
  490. package/cli/lib/config-lint.test.mjs +0 -117
  491. package/cli/lib/ddr-to-kgai.test.mjs +0 -118
  492. package/cli/lib/design-ownership.test.mjs +0 -329
  493. package/cli/lib/fetch-asset.test.mjs +0 -213
  494. package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
  495. package/cli/lib/figma-import-controls.test.mjs +0 -70
  496. package/cli/lib/flow-design-integration.test.mjs +0 -165
  497. package/cli/lib/gitignore-block.test.mjs +0 -136
  498. package/cli/lib/gitignore-drift.test.mjs +0 -156
  499. package/cli/lib/hubs-config.test.mjs +0 -165
  500. package/cli/lib/pkg-root.test.mjs +0 -111
  501. package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
  502. package/cli/lib/plugin-name-namespace.test.mjs +0 -71
  503. package/cli/lib/reconstruct-toolset.test.mjs +0 -194
  504. package/cli/lib/stack-detect.test.mjs +0 -121
  505. package/cli/lib/update-check.test.mjs +0 -32
  506. package/cli/lib/workspace-plan.test.mjs +0 -699
@@ -83,28 +83,55 @@ export function saveHubsConfig(config) {
83
83
  *
84
84
  * @param {string} url
85
85
  * @param {string} token
86
- * `role` is the ONE prior field carried across a relink (Cloud Phase 25 C2).
87
- * It records what the workspace vouched for at sign-in, and this path has no
88
- * fresher answer — the CLI does not sign in. Dropping it would silently show
89
- * a viewer the editing UI again, which is the exact experience the flag
86
+ * `role` and `expiresAt` are the prior fields carried across a relink (Cloud
87
+ * Phase 25 C2; `expiresAt` added 2026-09-03).
88
+ *
89
+ * `role` records what the workspace vouched for at sign-in, and this path has
90
+ * no fresher answer — the CLI does not sign in. Dropping it would silently
91
+ * show a viewer the editing UI again, which is the exact experience the flag
90
92
  * exists to prevent; a stale value costs a redundant hidden affordance and
91
- * self-corrects at the next workspace sign-in. Nothing ELSE is preserved:
92
- * `adoptedAt` is a per-machine attestation that a relink is entitled to clear.
93
+ * self-corrects at the next workspace sign-in.
94
+ *
95
+ * `expiresAt` is carried ONLY when the incoming token is byte-identical to the
96
+ * stored one — it describes the token, not the machine, so a new token
97
+ * invalidates it. Dropping it unconditionally is what left a linked project
98
+ * with no pre-expiry renewal timer at all.
99
+ *
100
+ * Nothing ELSE is preserved: `adoptedAt` is a per-machine attestation that a
101
+ * relink is entitled to clear.
93
102
  *
94
103
  * @param {{ adoptedAt?: number }} [extra]
95
104
  */
96
105
  export function addHub(url, token, extra = {}) {
97
106
  const norm = normalizeUrl(url);
98
107
  const cfg = loadHubsConfig();
99
- const priorRole = cfg.hubs[norm]?.role;
108
+ const prior = cfg.hubs[norm];
109
+ const priorRole = prior?.role;
100
110
  // LOCAL CONSENT SURVIVES EVERY RE-SAVE. `role` is a cache of what the hub
101
111
  // said about you at sign-in; `codeModulesAllowed` is what YOU said about the
102
112
  // hub, and a login response must not be able to change it.
103
- const priorConsent = cfg.hubs[norm]?.codeModulesAllowed;
113
+ const priorConsent = prior?.codeModulesAllowed;
114
+ // THE EXPIRY BELONGS TO THE TOKEN, so it survives exactly when the token does.
115
+ //
116
+ // `expiresAt` is written only by the sign-in and renew paths in
117
+ // `apps/studio/` — the CLI has never written it and, before this, never
118
+ // carried it either. So `maude design link --adopt` after a workspace
119
+ // sign-in silently dropped it, and a record with no expiry means
120
+ // `scheduleRenewal()` arms no timer at all (sync/index.ts): the credential
121
+ // then dies mid-session with nothing having tried to renew it.
122
+ //
123
+ // CONDITIONAL ON TOKEN IDENTITY, and that is the whole subtlety. Carrying it
124
+ // unconditionally would be worse than dropping it: a stale expiry against a
125
+ // NEW token arms the timer at the wrong instant and burns a slot in the
126
+ // `renewalsSinceProgress` cap for nothing. Same argument as `role` above —
127
+ // "this path has no fresher answer" — except that a relink CAN supply a new
128
+ // token, which is what makes the check necessary.
129
+ const priorExpiry = prior?.token === token ? prior?.expiresAt : undefined;
104
130
  cfg.hubs[norm] = {
105
131
  token,
106
132
  linkedAt: Date.now(),
107
133
  ...(priorRole ? { role: priorRole } : {}),
134
+ ...(typeof priorExpiry === 'number' ? { expiresAt: priorExpiry } : {}),
108
135
  ...(typeof priorConsent === 'boolean' ? { codeModulesAllowed: priorConsent } : {}),
109
136
  ...extra,
110
137
  };
@@ -0,0 +1,58 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const inventoryPath = join(
6
+ dirname(fileURLToPath(import.meta.url)),
7
+ 'maude-projector.inventory.json'
8
+ );
9
+
10
+ function mergeAbsent(target, additions) {
11
+ for (const [name, value] of Object.entries(additions ?? {})) {
12
+ if (!Object.hasOwn(target, name)) target[name] = value;
13
+ }
14
+ }
15
+
16
+ export async function MaudeProjectorPlugin() {
17
+ let inventory = null;
18
+ try {
19
+ inventory = JSON.parse(await readFile(inventoryPath, 'utf8'));
20
+ } catch {
21
+ return {};
22
+ }
23
+ if (inventory?.schemaVersion !== 1 || inventory.target !== 'opencode') return {};
24
+
25
+ for (const [name, value] of Object.entries(inventory.environment ?? {})) {
26
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(name) || typeof value !== 'string') continue;
27
+ const reference =
28
+ /^\$\{([A-Z_][A-Z0-9_]*)\}$/.exec(value)?.[1] ??
29
+ /^\{env:([A-Z_][A-Z0-9_]*)\}$/.exec(value)?.[1];
30
+ if (reference) {
31
+ if (process.env[reference] !== undefined) process.env[name] = process.env[reference];
32
+ } else {
33
+ process.env[name] = value;
34
+ }
35
+ }
36
+
37
+ return {
38
+ config: async (config) => {
39
+ config.agent ??= {};
40
+ config.command ??= {};
41
+ config.mcp ??= {};
42
+ config.permission ??= {};
43
+ mergeAbsent(config.agent, inventory.config.agent);
44
+ mergeAbsent(config.command, inventory.config.command);
45
+ mergeAbsent(config.mcp, inventory.config.mcp);
46
+ mergeAbsent(config.permission, inventory.config.permission);
47
+ config.instructions ??= [];
48
+ for (const path of inventory.config.instructions ?? []) {
49
+ if (!config.instructions.includes(path)) config.instructions.push(path);
50
+ }
51
+ config.skills ??= {};
52
+ config.skills.paths ??= [];
53
+ for (const path of inventory.config.skills?.paths ?? []) {
54
+ if (!config.skills.paths.includes(path)) config.skills.paths.push(path);
55
+ }
56
+ },
57
+ };
58
+ }
@@ -0,0 +1,125 @@
1
+ # Harness Capability Matrix
2
+
3
+ Reviewed: 2026-09-01
4
+
5
+ This document freezes the initial semantic contract for projecting a Claude Code and Maude environment into OpenCode and Codex. Claude remains the authoring source. A target status describes one representation only; it is not a claim of full parity.
6
+
7
+ ## Tested Versions
8
+
9
+ | Harness | Version | Verification |
10
+ | --- | --- | --- |
11
+ | Claude Code | 2.1.241 | Installed CLI plus current settings, hooks, subagent, skill, plugin, and MCP documentation. |
12
+ | OpenCode | 1.18.25 | Installed CLI; `opencode debug config` run with isolated `HOME` and `XDG_CONFIG_HOME`. |
13
+ | Codex | 0.152.0 | Installed CLI; generated config passed `codex --strict-config doctor --json` in an isolated `CODEX_HOME`. |
14
+
15
+ Generated output is valid only for a target version accepted by the versioned capability registry. Unknown target versions fail `check` and do not mutate target state.
16
+
17
+ ## Status Contract
18
+
19
+ - `native`: the target documents compatible representation and relevant semantics.
20
+ - `degraded`: Maude can emit a narrower or differently-invoked representation and reports the exact loss.
21
+ - `unsupported`: no safe equivalent exists; Maude emits no active behavior.
22
+ - Security-relevant `degraded` records fail strict mode unless an explicit target override acknowledges that exact degradation.
23
+ - Security-relevant `unsupported` records always fail strict mode.
24
+ - Unknown capabilities fail closed and cannot be promoted by an override.
25
+
26
+ ## Capability Matrix
27
+
28
+ | Source category | OpenCode | Codex | Fail-closed behavior |
29
+ | --- | --- | --- | --- |
30
+ | Global and project `CLAUDE.md` instructions | `native` fallback, `degraded` when a target instruction file shadows it | `native` through `project_doc_fallback_filenames`, `degraded` when `AGENTS.md` shadows it | Report the shadowing file; never overwrite or adopt it implicitly. |
31
+ | Imported Claude instructions | `degraded` to explicit `instructions` paths/globs | `degraded` to the `AGENTS.md` chain or a generated reference | Preserve source order and report import-semantic differences. |
32
+ | `.claude/rules/**/*.md` | `degraded` to scoped instruction globs | `degraded` to generated skill/instruction references | Do not claim Claude path-condition semantics when the target cannot enforce them. |
33
+ | Scalar Claude settings | `degraded` per registered key | `degraded` per registered key and allowed scope | Unknown keys are reported and omitted. |
34
+ | Map/list setting precedence | `degraded`; lower into OpenCode's merge layers | `degraded`; lower into Codex's CLI/project/profile/user/system layers | Retain all contributing sources; refuse ambiguous security conflicts. |
35
+ | User/project marketplace plugin selection | `degraded`; lower individual compatible assets | `native` plugin lifecycle in the pre-launch bridge; `degraded` in static projection | Runtime mirrors preserve selected plugin identity; static projection never claims lifecycle parity. |
36
+ | Claude custom commands | `degraded` to OpenCode commands | `degraded` to Codex skills/plugins | Preserve body and description; report frontmatter, argument, and invocation differences. |
37
+ | Claude subagents | `degraded` to OpenCode agents | `unsupported`; Codex custom agents inherit the parent tool registry | Do not emit a role whose Claude tool restrictions cannot be enforced; remove previously managed roles and report them. |
38
+ | Claude skills | `native` for target-valid `SKILL.md` assets | `native` for target-valid skills/plugin assets | Validate frontmatter and references; malformed skills are invalid source, not skipped. |
39
+ | STDIO MCP | `native` local MCP | `native` STDIO MCP | Preserve command/args and environment references without spawning the server. |
40
+ | Streamable HTTP MCP | `native` remote MCP | `native` HTTP MCP | Validate URL and reference syntax; never make a discovery-time request. |
41
+ | Legacy SSE MCP | `degraded` to remote transport after compatibility validation | `degraded` to streamable HTTP after compatibility validation | Disabled unless endpoint compatibility is explicitly proven. |
42
+ | Disabled MCP | `native` with `enabled: false` | `native` with `enabled: false` | A disabled source can never become enabled implicitly. |
43
+ | MCP OAuth | `degraded`; target OAuth options differ | `degraded`; target OAuth options differ | Unsupported callback/client-secret fields remain disabled and reported. |
44
+ | MCP headers and environment | `native` for `{env:NAME}`-style references after syntax conversion | `native` for target environment-backed header fields | Never interpolate variables; reject credential-shaped literals. |
45
+ | Command hooks with equivalent event, input, output, blocking, timeout, and failure semantics | `degraded` through exact compatible plugin hooks | `native` only for proven Codex hook equivalents and persisted hook trust | Any failed equivalence dimension leaves the hook disabled. |
46
+ | HTTP hooks | `unsupported` without a separately reviewed target-native implementation | `unsupported` | Emit no active hook and fail strict mode. |
47
+ | Prompt hooks | `unsupported` | `unsupported` | Parse for inventory, emit no active hook, fail strict mode. |
48
+ | Agent hooks | `unsupported` | `unsupported` | Parse for inventory, emit no active hook, fail strict mode. |
49
+ | Unknown hook handlers | `unsupported` | `unsupported` | Preserve the source inventory record, emit no active hook, and fail strict mode. |
50
+ | Hook matchers, output, and blocking decisions | `degraded` only when all semantics match | `degraded` only when all semantics match | Never approximate lifecycle events or discard hook output/errors. |
51
+ | Permission allow rules | `degraded` per exactly expressible tool/path/command scope | `degraded` through approval, sandbox, and exec-policy intersections | A scoped rule can never become blanket allow. |
52
+ | Permission ask rules | `degraded` where the target has the same approval boundary | `degraded` through approval policy plus sandbox/exec policy | If exact scope is unavailable, retain deny/default posture and report loss. |
53
+ | Permission deny rules | `degraded` per exactly expressible scope | `degraded` through sandbox and exec-policy denial; runtime `Read(path)` rules become stricter filesystem denies | Deny wins; an unrepresentable deny blocks migration or keeps runtime bypass inert. |
54
+ | Claude permission modes | `unsupported` as direct mappings | `degraded` to narrower approval/sandbox combinations; trusted runtime `bypassPermissions` uses a no-prompt permission profile | Static projection stays narrow. Runtime bypass activates only when every ask/deny rule is conservatively representable. |
55
+ | Generic environment injection | `degraded` through a bounded thin plugin using references | `degraded` through shell environment policy and MCP-specific fields | Preserve names/references only; do not read current values. |
56
+ | Keychain identifiers | `degraded` reference-only | `degraded` reference-only | Never read, copy, log, or back up resolved credentials. |
57
+ | `.ai/` state | `degraded` reference/register in place | `degraded` reference/register in place | Never copy the state tree into target directories. |
58
+ | `.design/` state | `degraded` reference/register in place | `degraded` reference/register in place | Never copy the state tree into target directories. |
59
+ | kgai prompt integration | `degraded` through bounded target-native prompt/plugin wiring | `degraded` through bounded target-native instruction/hook wiring | Treat graph output as untrusted data; transcript-dependent behavior remains disabled. |
60
+ | Target config ownership | `native` managed JSON entries/files through Maude manifest | `native` managed TOML entries/files through Maude manifest | Existing state requires preview and explicit adoption; external edits stop all writes. |
61
+
62
+ ## Precedence Findings
63
+
64
+ Claude inputs are normalized from least to most specific while preserving every contributor: global instructions/settings/assets/MCP, user-enabled plugin assets, project instructions/settings/assets/MCP, project-enabled plugin assets, then `.claude/settings.local.json`. Claude settings lists generally accumulate, scalar/map conflicts prefer the higher-precedence layer, hooks accumulate, and permissions resolve deny before ask before allow.
65
+
66
+ OpenCode merges remote, global, custom-config, project, `.opencode`, inline, managed, and MDM configuration layers. Later conflicting values win; the projector therefore owns individual entries and one thin plugin registration rather than replacing a complete `opencode.json`.
67
+
68
+ Codex resolves CLI overrides, trusted project layers from root to current directory, the selected profile, user config, system config, and defaults, while managed requirements constrain the result. Project config cannot own provider/authentication, notification, profile-selection, or telemetry-routing settings.
69
+
70
+ ## TOML Writer Decision
71
+
72
+ Codex managed merge will use exact-pinned `@decimalturn/toml-patch@3.0.5` under Node 20. It is dependency-free, supports TOML 1.1, and preserves comments, whitespace, formatting, and existing order. The writer must parse the complete existing document, mutate only manifest-owned keys in the parsed object, and patch the complete object back. Passing a partial object is forbidden because omitted user keys may be removed. Tests must cover comments, unknown keys, arrays of tables, idempotence, and malformed input.
73
+
74
+ ## Active Adapter Audit
75
+
76
+ `~/Dotfiles/opencode/claude-parity.ts` is prior art, not a correctness oracle. The current adapter:
77
+
78
+ - widens any scoped `Bash(...)` allow to blanket Bash access;
79
+ - broadens `auto` and `bypassPermissions` to unrestricted read/edit/Bash/web/external-directory access;
80
+ - drops ask rules, most deny rules, MCP permission rules, and unknown tools;
81
+ - resolves `${NAME}` header references to live values;
82
+ - silently ignores malformed JSON/frontmatter and unsupported agent fields;
83
+ - approximates Claude `SessionStart`/`Stop`, runs discovered commands detached, and discards output/errors/timeouts/blocking behavior;
84
+ - injects a Codex MCP with independent `approval_policy="never"` and `sandbox_mode="workspace-write"`;
85
+ - has no ownership manifest, adoption, conflict detection, atomic transaction, rollback, or exhaustive inventory assertion.
86
+
87
+ None of these behaviors may be carried into the projector.
88
+
89
+ ## Contract Transcript
90
+
91
+ The T0 spike used isolated homes and did not load user secrets:
92
+
93
+ ```text
94
+ $ claude --version
95
+ 2.1.241 (Claude Code)
96
+
97
+ $ opencode --version
98
+ 1.18.25
99
+
100
+ $ HOME=<isolated> XDG_CONFIG_HOME=<isolated> opencode debug config
101
+ { "$schema": "https://opencode.ai/config.json", "agent": {}, "plugin": [], "command": {} ... }
102
+
103
+ $ codex --version
104
+ codex-cli 0.152.0
105
+
106
+ $ pnpm view @decimalturn/toml-patch version time.modified engines dependencies
107
+ 3.0.5; updated 2026-08-30; Node >=16; no dependencies
108
+ ```
109
+
110
+ Executable conformance in later gates must use isolated `HOME`, `XDG_CONFIG_HOME`, and `CODEX_HOME`, committed safe fixtures, and no arbitrary discovered hooks.
111
+
112
+ ## Sources
113
+
114
+ - [Claude Code settings](https://docs.anthropic.com/en/docs/claude-code/settings)
115
+ - [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks)
116
+ - [Claude Code subagents](https://docs.anthropic.com/en/docs/claude-code/sub-agents)
117
+ - [Claude Code skills](https://docs.anthropic.com/en/docs/claude-code/skills)
118
+ - [OpenCode configuration](https://opencode.ai/docs/config/)
119
+ - [OpenCode plugins](https://opencode.ai/docs/plugins/)
120
+ - [OpenCode configuration source](https://github.com/anomalyco/opencode/blob/dev/packages/core/src/config.ts)
121
+ - [Codex configuration reference](https://developers.openai.com/codex/config-file/config-reference)
122
+ - [Codex AGENTS.md discovery](https://developers.openai.com/codex/agent-configuration/agents-md)
123
+ - [Codex hooks](https://developers.openai.com/codex/hooks)
124
+ - [Codex skills](https://developers.openai.com/codex/build-skills)
125
+ - [`@decimalturn/toml-patch`](https://www.npmjs.com/package/@decimalturn/toml-patch)
@@ -0,0 +1,95 @@
1
+ # Harness environment projection
2
+
3
+ Claude Code and Maude remain the authoring source. `maude harness` generates managed,
4
+ target-native OpenCode and Codex files without resolving secret references or copying
5
+ `.ai` and `.design` state.
6
+
7
+ Every command requires exactly one scope: `--global` or `--project <root>`. Projection
8
+ verbs require `--targets opencode,codex`; `adopt` and `remove` require one `--target`.
9
+
10
+ ```sh
11
+ maude harness migrate --from claude --targets opencode,codex --project .
12
+ maude harness migrate --from claude --targets opencode,codex --project . --yes
13
+ maude harness sync --targets opencode,codex --project .
14
+ maude harness check --targets opencode,codex --project . --strict --json
15
+ maude harness status --project . --json
16
+ ```
17
+
18
+ `migrate`, `diff`, and `adopt` preview by default. Mutations run only after complete
19
+ target-specific validation and confirmation or `--yes`. Confirmation never overrides
20
+ literal-secret rejection, unsupported security semantics, invalid sources or targets,
21
+ or ownership conflicts. `sync` commits all requested targets in one managed transaction.
22
+
23
+ Machine-local ownership, backups, recovery material, and manifests live below
24
+ `~/.config/maude/harness/`. `remove` touches manifest-owned output only and restores an
25
+ adopted backup when present. Generated reports contain references and hashes, not secret
26
+ values.
27
+
28
+ Exit codes are stable: `0` clean/success, `1` drift or declined preview, `2` usage,
29
+ `3` strict/unsupported security failure, `4` ownership conflict, `5` invalid source,
30
+ `6` invalid target/config/version, and `7` interrupted transaction or required recovery.
31
+
32
+ No postinstall, startup, plugin hook, or `maude init` path invokes harness mutation.
33
+
34
+ ## Codex pre-launch bridge
35
+
36
+ `maude codex [args...]` is a separate machine-local bridge for daily Codex use. Before
37
+ launching the real Codex executable it reads the effective Claude plugin selection for
38
+ the current project when Codex marks that project trusted (otherwise only global Claude
39
+ selection applies), materializes owner-only runtime marketplace mirrors below
40
+ `~/.config/maude/harness/codex-runtime/`, and reconciles those plugins through Codex's
41
+ native `plugin marketplace` and `plugin add/remove` commands. `maude codex sync --json`
42
+ performs the same reconciliation without starting a session.
43
+
44
+ For a Codex-trusted project whose effective Claude settings select
45
+ `permissions.defaultMode: "bypassPermissions"`, the bridge adds a launch-only Codex
46
+ permission profile with `approval_policy="never"`, root filesystem write access, and
47
+ full network access. Claude `Read(path)` deny rules become stricter Codex filesystem
48
+ denies. Any Claude ask rule or unrepresentable deny keeps runtime bypass inert. These
49
+ overrides are passed on the command line and are not persisted. A pre-existing
50
+ `sandbox_mode`, an explicit sandbox/permission-profile override, a remote target, or an
51
+ ambiguous working-directory override keeps the launch fail-closed so the deny profile
52
+ cannot be silently disabled. Any `-C`/`--cd` path is authorized first and then forwarded
53
+ only in canonical form; path identity and trust are checked again immediately before
54
+ launch. Runtime-only defaults such as the skill context budget are also passed as CLI
55
+ overrides instead of rewriting user-owned Codex config.
56
+
57
+ `-C` and `--cd` are resolved before project trust and settings discovery, so authority
58
+ cannot be borrowed from one project and applied to another. Remote and Codex Cloud
59
+ sessions are rejected before runtime reconciliation, so local plugins, MCP state, and
60
+ permissions are never mutated for a remote launch. Runtime reconciliation is serialized
61
+ by the same crash-safe scope-lock primitive used by transactional harness writes.
62
+
63
+ Claude commands are exposed as namespaced Codex skills. Claude agents are reported but
64
+ not installed: Codex custom agents inherit the parent's tool registry, so a read-only
65
+ sandbox cannot enforce Claude agent tool restrictions. The bridge removes role files it
66
+ managed before this limitation was proven. Standalone Claude MCP servers are merged with
67
+ plugin MCP servers. Bearer credentials must reference an environment variable or exactly
68
+ match one already present in the launch environment; credential values are never pooled
69
+ into bridge state or persisted config. The current Codex CLI cannot write pass-through
70
+ environment names for STDIO MCP servers without materializing values, so the runtime bridge
71
+ rejects those entries; the transactional `maude harness` lowerer remains the supported path.
72
+
73
+ The bridge never writes into the current repository and does not copy `.ai` or `.design`.
74
+ A `CODEX_HOME` inside the current project is rejected rather than weakening that boundary.
75
+ A shell launcher may safely shadow the real Codex binary when it sets
76
+ `MAUDE_CODEX_REAL` to that binary before executing `maude codex`. This runtime path
77
+ complements the explicit, transactional `maude harness` migration workflow; it does not
78
+ adopt repository files or retire existing adapters by itself.
79
+
80
+ Real-workspace conformance can keep sources and generated target files physically
81
+ separate: set `MAUDE_HARNESS_SOURCE_HOME` to the read-only Claude source home and
82
+ `MAUDE_HARNESS_PROJECT_TARGET_ROOT` to a temporary project target root. These are
83
+ diagnostic/operator controls; normal migration should leave both unset. A temporary
84
+ source mirror may set the path-delimited `MAUDE_HARNESS_ALLOWED_PLUGIN_ROOTS` to
85
+ explicit read-only plugin cache roots referenced by its installation index.
86
+
87
+ The npm package contains the harness command, lowerers, capability/compatibility registry,
88
+ OpenCode runtime template, and Codex pre-launch bridge. Test fixtures, tests, backups, machine-local manifests,
89
+ logs, workspace metadata, and secrets are not part of that published harness surface.
90
+
91
+ Harness output is version-locked to the target versions listed by the capability registry
92
+ and to the machine-local manifest schema. `check` refuses an unsupported target or schema
93
+ before writing anything and prints two recovery choices: upgrade Maude for a newer registry,
94
+ or install the target version supported by the current Maude release. Never delete an
95
+ incompatible manifest or its backups as an upgrade shortcut.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "1.0.9",
3
+ "version": "1.2.0",
4
4
  "description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -51,20 +51,39 @@
51
51
  "test:e2e:desktop:onboarding": "pnpm --filter @maude/desktop-e2e e2e:onboarding",
52
52
  "test:e2e:desktop:acp-cold-start": "pnpm --filter @maude/desktop-e2e e2e:acp-cold-start",
53
53
  "test:e2e:desktop:parity": "pnpm --filter @maude/desktop-e2e e2e:parity",
54
+ "test:e2e:desktop:sidecar-respawn": "pnpm --filter @maude/desktop-e2e e2e:sidecar-respawn",
54
55
  "test:e2e:desktop:parity:cloud": "pnpm --filter @maude/desktop-e2e e2e:parity:cloud"
55
56
  },
56
57
  "optionalDependencies": {
57
- "@1agh/maude-darwin-arm64": "1.0.9",
58
- "@1agh/maude-darwin-x64": "1.0.9",
59
- "@1agh/maude-linux-arm64": "1.0.9",
60
- "@1agh/maude-linux-arm64-musl": "1.0.9",
61
- "@1agh/maude-linux-x64": "1.0.9",
62
- "@1agh/maude-linux-x64-musl": "1.0.9",
63
- "@1agh/maude-win32-x64": "1.0.9"
58
+ "@1agh/maude-darwin-arm64": "1.2.0",
59
+ "@1agh/maude-darwin-x64": "1.2.0",
60
+ "@1agh/maude-linux-arm64": "1.2.0",
61
+ "@1agh/maude-linux-arm64-musl": "1.2.0",
62
+ "@1agh/maude-linux-x64": "1.2.0",
63
+ "@1agh/maude-linux-x64-musl": "1.2.0",
64
+ "@1agh/maude-win32-x64": "1.2.0"
64
65
  },
65
66
  "files": [
66
67
  "cli",
68
+ "!cli/.platform-binary-path",
69
+ "!cli/**/*.test.mjs",
70
+ "!cli/fixtures",
71
+ "!cli/**/fixtures",
72
+ "!cli/**/backups",
73
+ "!cli/**/logs",
74
+ "!cli/**/.DS_Store",
67
75
  "apps/studio",
76
+ "!apps/studio/.ai",
77
+ "!apps/studio/bun.lock",
78
+ "!apps/studio/test",
79
+ "!apps/studio/**/test",
80
+ "!apps/studio/**/tests",
81
+ "!apps/studio/**/fixtures",
82
+ "!apps/studio/**/backups",
83
+ "!apps/studio/**/logs",
84
+ "!apps/studio/**/*.test.*",
85
+ "!apps/studio/**/*.spec.*",
86
+ "!apps/studio/**/.DS_Store",
68
87
  "plugins/design/dependencies.json",
69
88
  "plugins/design/dependencies.schema.json",
70
89
  "plugins/design/templates",
@@ -72,6 +91,8 @@
72
91
  "plugins/flow/dependencies.schema.json",
73
92
  "plugins/flow/templates",
74
93
  "plugins/flow/.claude-plugin/config.schema.json",
94
+ "docs/harness-capability-matrix.md",
95
+ "docs/harness-environment-projection.md",
75
96
  "LICENSE",
76
97
  "README.md"
77
98
  ],
@@ -104,9 +125,11 @@
104
125
  "lucide-react": "^1.24.0"
105
126
  },
106
127
  "dependencies": {
128
+ "@decimalturn/toml-patch": "3.0.5",
107
129
  "ajv": "^8.20.0",
108
130
  "ajv-formats": "^3.0.1",
109
131
  "happy-dom": "20.10.6",
110
- "svgo": "^4.0.1"
132
+ "svgo": "^4.0.1",
133
+ "yaml": "2.9.0"
111
134
  }
112
135
  }
@@ -459,6 +459,14 @@
459
459
  "minLength": 1
460
460
  }
461
461
  },
462
+ "qualityScoped": {
463
+ "type": "object",
464
+ "description": "Scoped (changed-files / changed-packages-only) gate variants for the IMPLEMENTATION inner loop (`/flow:utils-verify`, per-task `/flow:execute`, `/flow:bug-fix`). Same flat shape as `quality` — gate name → shell command string, keys mirror `quality` (conventionally `lint`, `format`, `typecheck`, `tests`). During implementation these run INSTEAD of the repo-wide `quality.*` commands; a missing key means the inner loop defers that gate to `/flow:validate` (it never falls back to the repo-wide command). `/flow:validate` and `/flow:done` ignore this block entirely — the full `quality.*` pipeline remains the single outer gate. Monorepo example: `\"lint\": \"turbo run lint --filter='[origin/main]'\"` — note changed-only `[base]`, NOT dependents-inclusive `...[base]`, which selects nearly the whole monorepo whenever a shared package is touched; dependent breakage is `/flow:validate`'s job.",
465
+ "additionalProperties": {
466
+ "type": "string",
467
+ "minLength": 1
468
+ }
469
+ },
462
470
  "integrations": {
463
471
  "type": "object",
464
472
  "additionalProperties": false,
@@ -1,7 +0,0 @@
1
- {
2
- "hits": {},
3
- "misses": {
4
- "security": 2
5
- },
6
- "since": 1783681711349
7
- }
@@ -1,165 +0,0 @@
1
- // DDR-185 security addendum, round 3 — the hardened agent-browser wrapper
2
- // behind `maude design agent-browser-safe`
3
- // (apps/studio/bin/_agent-browser-safe.mjs). This suite locks the SECURITY
4
- // CORE: the closed subcommand allow-list (no cookies/clipboard/storage/
5
- // connect/chat/eval reachable — `eval` was REMOVED in round 3, see below),
6
- // the fixed positional-argument ARITY per subcommand with NO flag
7
- // vocabulary at all, and the `--config`-pin + full env-clearing that closes
8
- // the project-level `agent-browser.json` auto-discovery bypass a live
9
- // adversarial pass found round 2 never considered.
10
-
11
- import { describe, expect, test } from 'bun:test';
12
- import { readFileSync } from 'node:fs';
13
- import {
14
- ALLOWED_SUBCOMMANDS,
15
- CLEARED_ENV_KEYS,
16
- SUBCOMMAND_ARITY,
17
- validateArgv,
18
- } from './_agent-browser-safe.mjs';
19
-
20
- describe('validateArgv — subcommand allow-list', () => {
21
- for (const sub of ALLOWED_SUBCOMMANDS) {
22
- test(`accepts "${sub}" with its minimum expected argument count`, () => {
23
- const [min] = SUBCOMMAND_ARITY[sub];
24
- const args = Array.from({ length: min }, (_, i) => `arg${i}`);
25
- expect(validateArgv([sub, ...args])).toBeNull();
26
- });
27
- }
28
-
29
- test('round 3: "eval" is REMOVED — arbitrary JS execution can\'t be argv-constrained (ethical-hacker Finding 1)', () => {
30
- // Round 2 kept eval on the allow-list; a live adversarial pass proved
31
- // eval defeats --allowed-domains entirely (window.location.href escapes
32
- // the navigation gate; a no-cors fetch() exfiltrates without even
33
- // needing a domain match) — no argv check can close that, so the
34
- // capability itself is cut, mirroring this DDR's own `find` precedent.
35
- expect(ALLOWED_SUBCOMMANDS).not.toContain('eval');
36
- const reason = validateArgv(['eval', '1+1']);
37
- expect(reason).toContain('eval');
38
- });
39
-
40
- for (const sub of [
41
- 'cookies',
42
- 'clipboard',
43
- 'storage',
44
- 'connect',
45
- 'chat',
46
- 'upload',
47
- 'auth',
48
- 'profiles',
49
- ]) {
50
- test(`rejects the credential/session-hijack-capable subcommand "${sub}" (ethical-hacker Finding D)`, () => {
51
- const reason = validateArgv([sub, 'get']);
52
- expect(reason).toContain(sub);
53
- });
54
- }
55
-
56
- test('rejects when no subcommand is given', () => {
57
- expect(validateArgv([])).not.toBeNull();
58
- });
59
- });
60
-
61
- describe('validateArgv — fixed arity, NO flag vocabulary at all', () => {
62
- test('rejects an extra argument beyond the expected arity, whatever it looks like', () => {
63
- const reason = validateArgv(['open', 'http://localhost:1', 'extra']);
64
- expect(reason).toContain('exactly 1');
65
- });
66
-
67
- test('rejects the --provider/-p remote-cloud-browser PoC verbatim (round-2 finding)', () => {
68
- expect(
69
- validateArgv(['open', 'http://localhost:1', '--provider', 'browserbase'])
70
- ).not.toBeNull();
71
- expect(validateArgv(['open', '-p', 'browserbase', 'http://localhost:1'])).not.toBeNull();
72
- });
73
-
74
- test('rejects the persistent-profile PoC verbatim (still closed under the arity model)', () => {
75
- expect(
76
- validateArgv(['open', '--profile', 'Default', 'https://github.com/settings/tokens'])
77
- ).not.toBeNull();
78
- });
79
-
80
- test('rejects the CDP-attach PoC verbatim (still closed under the arity model)', () => {
81
- expect(validateArgv(['snapshot', '--cdp', '9222'])).not.toBeNull();
82
- });
83
-
84
- test('a positional value that itself starts with "-" is NOT mistaken for a flag (arity-based, not prefix-based)', () => {
85
- // agent-browser-safe get "-weird-thing" must still work — the arity
86
- // model looks at COUNT, not at whether a token happens to start with a
87
- // dash.
88
- expect(validateArgv(['get', '-weird-thing'])).toBeNull();
89
- });
90
-
91
- test('screenshot accepts 0 or 1 positional args (optional path)', () => {
92
- expect(validateArgv(['screenshot'])).toBeNull();
93
- expect(validateArgv(['screenshot', '/tmp/out.png'])).toBeNull();
94
- expect(validateArgv(['screenshot', '/tmp/out.png', 'extra'])).not.toBeNull();
95
- });
96
-
97
- test('get accepts 1 or 2 positional args (thing + optional selector)', () => {
98
- expect(validateArgv(['get', 'text'])).toBeNull();
99
- expect(validateArgv(['get', 'text', '.selector'])).toBeNull();
100
- expect(validateArgv(['get'])).not.toBeNull();
101
- });
102
-
103
- test('snapshot and close accept exactly 0 arguments', () => {
104
- expect(validateArgv(['snapshot'])).toBeNull();
105
- expect(validateArgv(['snapshot', '-i'])).not.toBeNull();
106
- expect(validateArgv(['close'])).toBeNull();
107
- expect(validateArgv(['close', '--all'])).not.toBeNull();
108
- });
109
-
110
- test('an ordinary allowed invocation with no extra args passes', () => {
111
- expect(validateArgv(['open', 'http://localhost:3000/'])).toBeNull();
112
- expect(validateArgv(['screenshot', '/tmp/out.png'])).toBeNull();
113
- });
114
- });
115
-
116
- describe('CLEARED_ENV_KEYS', () => {
117
- test('round 3: covers AGENT_BROWSER_CONFIG (defense in depth alongside the --config pin)', () => {
118
- expect(CLEARED_ENV_KEYS).toContain('AGENT_BROWSER_CONFIG');
119
- });
120
-
121
- test('round 2: covers the three vars a live verification pass found missing from round 1', () => {
122
- for (const key of [
123
- 'AGENT_BROWSER_AUTO_CONNECT',
124
- 'AGENT_BROWSER_PROXY',
125
- 'AGENT_BROWSER_ENGINE',
126
- ]) {
127
- expect(CLEARED_ENV_KEYS).toContain(key);
128
- }
129
- });
130
-
131
- test('covers all four standard proxy env vars, both cases', () => {
132
- for (const key of [
133
- 'HTTP_PROXY',
134
- 'HTTPS_PROXY',
135
- 'ALL_PROXY',
136
- 'http_proxy',
137
- 'https_proxy',
138
- 'all_proxy',
139
- ]) {
140
- expect(CLEARED_ENV_KEYS).toContain(key);
141
- }
142
- });
143
-
144
- test('still covers the original round-1 set', () => {
145
- for (const key of [
146
- 'AGENT_BROWSER_PROFILE',
147
- 'AGENT_BROWSER_SESSION_NAME',
148
- 'AGENT_BROWSER_ACTION_POLICY',
149
- 'AGENT_BROWSER_CONFIRM_ACTIONS',
150
- 'AGENT_BROWSER_CONFIRM_INTERACTIVE',
151
- ]) {
152
- expect(CLEARED_ENV_KEYS).toContain(key);
153
- }
154
- });
155
- });
156
-
157
- describe('the forced --config target (round 3: closes ./agent-browser.json auto-discovery)', () => {
158
- test('_agent-browser-safe-config.json exists and is valid, empty JSON', () => {
159
- // Must stay valid JSON — agent-browser exits with an error if --config
160
- // points to a missing/invalid file, which would break every allowed
161
- // subcommand, not just fail safe.
162
- const raw = readFileSync(new URL('./_agent-browser-safe-config.json', import.meta.url), 'utf8');
163
- expect(JSON.parse(raw)).toEqual({});
164
- });
165
- });