@open-mercato/cli 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7

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 (278) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/build.mjs +7 -24
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +88 -32
  4. package/dist/agentic/codex/enforcement-rules.md +16 -2
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +50 -19
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +21 -9
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +5 -3
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +29 -3
  9. package/dist/agentic/guides/cache.md +45 -0
  10. package/dist/agentic/guides/core.md +453 -0
  11. package/dist/agentic/guides/events.md +117 -0
  12. package/dist/agentic/guides/module-facts.json +3833 -115419
  13. package/dist/agentic/guides/module-system.md +127 -12
  14. package/dist/agentic/guides/modules/ai_assistant.md +28 -227
  15. package/dist/agentic/guides/modules/api_docs.md +2 -58
  16. package/dist/agentic/guides/modules/api_keys.md +7 -92
  17. package/dist/agentic/guides/modules/attachments.md +14 -119
  18. package/dist/agentic/guides/modules/audit_logs.md +10 -131
  19. package/dist/agentic/guides/modules/auth.md +33 -276
  20. package/dist/agentic/guides/modules/business_rules.md +14 -149
  21. package/dist/agentic/guides/modules/catalog.md +43 -475
  22. package/dist/agentic/guides/modules/channel_gmail.md +3 -82
  23. package/dist/agentic/guides/modules/channel_imap.md +3 -82
  24. package/dist/agentic/guides/modules/checkout.md +27 -299
  25. package/dist/agentic/guides/modules/communication_channels.md +33 -315
  26. package/dist/agentic/guides/modules/configs.md +8 -104
  27. package/dist/agentic/guides/modules/content.md +2 -57
  28. package/dist/agentic/guides/modules/currencies.md +17 -152
  29. package/dist/agentic/guides/modules/customer_accounts.md +45 -387
  30. package/dist/agentic/guides/modules/customers.md +85 -746
  31. package/dist/agentic/guides/modules/dashboards.md +10 -155
  32. package/dist/agentic/guides/modules/data_sync.md +16 -149
  33. package/dist/agentic/guides/modules/dictionaries.md +12 -107
  34. package/dist/agentic/guides/modules/directory.md +15 -148
  35. package/dist/agentic/guides/modules/entities.md +13 -145
  36. package/dist/agentic/guides/modules/events.md +2 -59
  37. package/dist/agentic/guides/modules/feature_toggles.md +9 -119
  38. package/dist/agentic/guides/modules/gateway_stripe.md +4 -92
  39. package/dist/agentic/guides/modules/generators.md +2 -45
  40. package/dist/agentic/guides/modules/inbox_ops.md +27 -207
  41. package/dist/agentic/guides/modules/integrations.md +16 -146
  42. package/dist/agentic/guides/modules/messages.md +24 -223
  43. package/dist/agentic/guides/modules/notifications.md +8 -96
  44. package/dist/agentic/guides/modules/onboarding.md +6 -77
  45. package/dist/agentic/guides/modules/payment_gateways.md +21 -164
  46. package/dist/agentic/guides/modules/perspectives.md +7 -77
  47. package/dist/agentic/guides/modules/planner.md +18 -136
  48. package/dist/agentic/guides/modules/portal.md +2 -93
  49. package/dist/agentic/guides/modules/progress.md +14 -104
  50. package/dist/agentic/guides/modules/query_index.md +15 -154
  51. package/dist/agentic/guides/modules/record_locks.md +21 -206
  52. package/dist/agentic/guides/modules/resources.md +30 -197
  53. package/dist/agentic/guides/modules/sales.md +84 -678
  54. package/dist/agentic/guides/modules/scheduler.md +14 -133
  55. package/dist/agentic/guides/modules/search.md +6 -170
  56. package/dist/agentic/guides/modules/security.md +35 -294
  57. package/dist/agentic/guides/modules/shipping_carriers.md +18 -134
  58. package/dist/agentic/guides/modules/sso.md +26 -151
  59. package/dist/agentic/guides/modules/staff.md +57 -369
  60. package/dist/agentic/guides/modules/storage_s3.md +5 -95
  61. package/dist/agentic/guides/modules/sync_akeneo.md +3 -85
  62. package/dist/agentic/guides/modules/sync_excel.md +6 -75
  63. package/dist/agentic/guides/modules/system_status_overlays.md +2 -60
  64. package/dist/agentic/guides/modules/translations.md +10 -104
  65. package/dist/agentic/guides/modules/webhooks.md +24 -203
  66. package/dist/agentic/guides/modules/wms.md +46 -441
  67. package/dist/agentic/guides/modules/workflows.md +43 -302
  68. package/dist/agentic/guides/queue.md +53 -0
  69. package/dist/agentic/guides/search.md +115 -0
  70. package/dist/agentic/guides/shared.md +108 -0
  71. package/dist/agentic/guides/ui.md +347 -0
  72. package/dist/agentic/shared/AGENTS.md.template +311 -124
  73. package/dist/agentic/shared/ai/agentic.config.json +0 -2
  74. package/dist/agentic/shared/ai/lessons.md +3 -26
  75. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +27 -0
  76. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +27 -0
  77. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +0 -4
  78. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +241 -20
  79. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +260 -0
  80. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +601 -16
  81. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +146 -0
  82. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +310 -14
  83. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +97 -15
  84. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +84 -0
  85. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +193 -0
  86. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +152 -33
  87. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +708 -24
  88. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +34 -0
  89. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +762 -0
  90. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +820 -20
  91. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +50 -0
  92. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +97 -0
  93. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +843 -19
  94. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +271 -0
  95. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +63 -15
  96. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +467 -16
  97. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +101 -0
  98. package/dist/agentic/shared/ai/skills/tiers.json +21 -143
  99. package/dist/agentic/shared/ai/skills/tiers.schema.json +40 -52
  100. package/dist/agentic/shared/ai/specs/README.md +3 -4
  101. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +10 -226
  102. package/dist/agentic/shared/ai/trackers/github.md +4 -108
  103. package/dist/agentic/shared/scripts/install-skills.sh +694 -4
  104. package/dist/bin.js +1 -13
  105. package/dist/bin.js.map +2 -2
  106. package/dist/lib/__integration__/TC-INT-008.spec.js +8 -88
  107. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  108. package/dist/lib/agentic-init.js +3 -13
  109. package/dist/lib/agentic-init.js.map +2 -2
  110. package/dist/lib/agentic-setup.js +186 -418
  111. package/dist/lib/agentic-setup.js.map +2 -2
  112. package/dist/lib/generators/extensions/injection-widgets.js +0 -20
  113. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  114. package/dist/lib/generators/module-facts-discovery.js +7 -41
  115. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  116. package/dist/lib/generators/module-facts.js +45 -1387
  117. package/dist/lib/generators/module-facts.js.map +3 -3
  118. package/dist/lib/generators/module-registry.js +5 -11
  119. package/dist/lib/generators/module-registry.js.map +2 -2
  120. package/dist/lib/resolver.js +0 -2
  121. package/dist/lib/resolver.js.map +2 -2
  122. package/dist/lib/testing/integration.js +0 -5
  123. package/dist/lib/testing/integration.js.map +2 -2
  124. package/dist/mercato.js +1 -23
  125. package/dist/mercato.js.map +2 -2
  126. package/package.json +11 -13
  127. package/src/bin.ts +1 -33
  128. package/src/lib/__integration__/TC-INT-008.spec.ts +9 -107
  129. package/src/lib/__tests__/agentic-init.test.ts +5 -41
  130. package/src/lib/agentic-init.ts +3 -15
  131. package/src/lib/agentic-setup.ts +224 -530
  132. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +2 -88
  133. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +0 -1
  134. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +10 -54
  135. package/src/lib/generators/__tests__/module-subset.test.ts +0 -18
  136. package/src/lib/generators/extensions/injection-widgets.ts +0 -51
  137. package/src/lib/generators/module-facts-discovery.ts +7 -58
  138. package/src/lib/generators/module-facts.ts +46 -1851
  139. package/src/lib/generators/module-registry.ts +6 -16
  140. package/src/lib/resolver.ts +0 -3
  141. package/src/lib/testing/__tests__/integration.test.ts +0 -4
  142. package/src/lib/testing/integration.ts +0 -5
  143. package/src/mercato.ts +1 -26
  144. package/dist/agentic/guides/ai-workflows.md +0 -76
  145. package/dist/agentic/guides/architecture.md +0 -107
  146. package/dist/agentic/guides/backend-ui.md +0 -80
  147. package/dist/agentic/guides/contracts.md +0 -92
  148. package/dist/agentic/guides/extensions.md +0 -121
  149. package/dist/agentic/guides/framework-extension-points.md +0 -36
  150. package/dist/agentic/guides/integrations.md +0 -81
  151. package/dist/agentic/guides/modules/design_system.md +0 -102
  152. package/dist/agentic/guides/spec-delivery.md +0 -22
  153. package/dist/agentic/guides/testing-debugging.md +0 -80
  154. package/dist/agentic/guides/upstream/AGENTS.md +0 -298
  155. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +0 -316
  156. package/dist/agentic/guides/upstream/manifest.json +0 -8
  157. package/dist/agentic/shared/ai/harness/README.md +0 -50
  158. package/dist/agentic/shared/ai/harness/RELEASE.md +0 -57
  159. package/dist/agentic/shared/ai/harness/cases.json +0 -13680
  160. package/dist/agentic/shared/ai/harness/cases.schema.json +0 -133
  161. package/dist/agentic/shared/ai/harness/fixtures/index.json +0 -49
  162. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +0 -162
  163. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +0 -21
  164. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +0 -96
  165. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +0 -172
  166. package/dist/agentic/shared/ai/harness/release-matrix.json +0 -158
  167. package/dist/agentic/shared/ai/harness/release-result.schema.json +0 -219
  168. package/dist/agentic/shared/ai/harness/result.schema.json +0 -76
  169. package/dist/agentic/shared/ai/harness/routing-response.schema.json +0 -12
  170. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +0 -73
  171. package/dist/agentic/shared/ai/harness/validators.json +0 -78
  172. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +0 -2121
  173. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +0 -1096
  174. package/dist/agentic/shared/ai/lessons/_template.md +0 -16
  175. package/dist/agentic/shared/ai/review-checklist.md +0 -36
  176. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +0 -21
  177. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +0 -98
  178. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +0 -16
  179. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +0 -53
  180. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +0 -13
  181. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +0 -11
  182. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +0 -30
  183. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +0 -12
  184. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +0 -13
  185. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +0 -14
  186. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +0 -28
  187. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +0 -12
  188. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +0 -22
  189. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +0 -12
  190. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +0 -19
  191. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +0 -19
  192. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +0 -13
  193. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +0 -14
  194. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +0 -10
  195. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +0 -17
  196. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +0 -29
  197. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +0 -100
  198. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +0 -16
  199. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +0 -17
  200. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +0 -25
  201. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +0 -11
  202. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +0 -13
  203. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +0 -18
  204. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +0 -20
  205. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +0 -33
  206. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +0 -40
  207. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +0 -27
  208. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +0 -24
  209. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +0 -16
  210. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +0 -14
  211. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +0 -15
  212. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +0 -12
  213. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +0 -34
  214. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +0 -4
  215. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +0 -11
  216. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +0 -31
  217. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +0 -39
  218. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +0 -43
  219. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +0 -11
  220. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +0 -19
  221. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +0 -172
  222. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +0 -12
  223. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +0 -83
  224. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +0 -21
  225. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +0 -11
  226. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +0 -22
  227. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +0 -13
  228. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +0 -41
  229. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +0 -19
  230. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +0 -44
  231. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +0 -39
  232. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +0 -20
  233. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +0 -57
  234. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +0 -11
  235. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +0 -486
  236. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +0 -18
  237. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +0 -27
  238. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +0 -13
  239. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +0 -41
  240. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +0 -11
  241. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +0 -15
  242. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +0 -16
  243. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +0 -14
  244. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +0 -187
  245. package/dist/agentic/shared/scripts/check-lessons.mjs +0 -178
  246. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +0 -2958
  247. package/dist/agentic/shared/scripts/execution-sandbox.mjs +0 -329
  248. package/dist/agentic/shared/scripts/framework-context.mjs +0 -888
  249. package/dist/agentic/shared/scripts/install-skills.mjs +0 -1029
  250. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +0 -222
  251. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +0 -1774
  252. package/dist/lib/generators/module-extension-facts.js +0 -1840
  253. package/dist/lib/generators/module-extension-facts.js.map +0 -7
  254. package/dist/lib/generators/module-fact-sources.js +0 -70
  255. package/dist/lib/generators/module-fact-sources.js.map +0 -7
  256. package/dist/lib/generators/module-override-targets.js +0 -688
  257. package/dist/lib/generators/module-override-targets.js.map +0 -7
  258. package/dist/lib/load-env.js +0 -24
  259. package/dist/lib/load-env.js.map +0 -7
  260. package/dist/lib/telemetry-init.js +0 -358
  261. package/dist/lib/telemetry-init.js.map +0 -7
  262. package/src/lib/__tests__/agentic-setup.ownership.test.ts +0 -386
  263. package/src/lib/__tests__/injection-widget-id-validation.test.ts +0 -184
  264. package/src/lib/__tests__/load-env.test.ts +0 -65
  265. package/src/lib/__tests__/telemetry-init.test.ts +0 -298
  266. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +0 -92
  267. package/src/lib/generators/__tests__/module-extension-activations.test.ts +0 -762
  268. package/src/lib/generators/__tests__/module-extension-facts.test.ts +0 -404
  269. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +0 -108
  270. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +0 -561
  271. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +0 -119
  272. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +0 -149
  273. package/src/lib/generators/__tests__/module-override-targets.test.ts +0 -304
  274. package/src/lib/generators/module-extension-facts.ts +0 -2365
  275. package/src/lib/generators/module-fact-sources.ts +0 -108
  276. package/src/lib/generators/module-override-targets.ts +0 -863
  277. package/src/lib/load-env.ts +0 -30
  278. package/src/lib/telemetry-init.ts +0 -461
@@ -1,1096 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawnSync } from 'node:child_process'
4
- import fs from 'node:fs'
5
- import os from 'node:os'
6
- import path from 'node:path'
7
- import process from 'node:process'
8
- import { createRequire } from 'node:module'
9
- import vm from 'node:vm'
10
-
11
- const EXIT_PASS = 0
12
- const EXIT_FAILURE = 1
13
- const EXIT_INVALID = 2
14
- const MAX_SOURCE_BYTES = 256 * 1024
15
- const WORKER_TIMEOUT_MS = 3_000
16
-
17
- const CASES = {
18
- 'OMH-045': { file: 'src/modules/external_sync/lib/client.ts', allowCompiledImports: true },
19
- 'OMH-054': { file: 'src/modules/automation/workflows/call-api.ts' },
20
- 'OMH-057': { file: 'src/modules/harness_fixture/api/scope/route.ts' },
21
- 'OMH-060': { file: 'src/modules/harness_fixture/commands/update-record.ts' },
22
- 'OMH-061': {
23
- file: 'src/modules/harness_fixture/backend/edit/page.tsx',
24
- allowedCompiledImport: '@open-mercato/ui/backend/CrudForm',
25
- },
26
- 'OMH-070': { file: 'src/modules/harness_fixture/workers/sync.ts' },
27
- 'OMH-093': { file: 'src/modules/customer_merge/commands/merge-contacts.ts', family: 'business-command', exportName: 'mergeContacts', requiredFlags: ['scopeValid', 'survivorSelected'] },
28
- 'OMH-105': { file: 'src/modules/deal_stages/commands/change-stage.ts', family: 'business-command', exportName: 'changeDealStage', requiredFlags: ['transitionAllowed', 'requiredFieldsPresent'], allowCompiledImports: true },
29
- 'OMH-107': { file: 'src/modules/quote_approval/commands/request-discount.ts', family: 'business-command', exportName: 'requestQuoteDiscount', requiredFlags: ['approvalSatisfied', 'separationOfDuties'] },
30
- 'OMH-115': { file: 'src/modules/deal_accessibility/lib/move-deal.ts', family: 'ui-business-surface', exportName: 'moveDealAccessibly', requiredFlags: ['accessGranted', 'keyboardEquivalent'] },
31
- 'OMH-122': { file: 'src/modules/stock_reservations/commands/reserve-stock.ts', family: 'business-command', exportName: 'reserveStock', requiredFlags: ['stockAvailable', 'reservationKeyPresent'] },
32
- 'OMH-128': { file: 'src/modules/bulk_pricing/commands/update-prices.ts', family: 'async-operation', exportName: 'updatePrices' },
33
- 'OMH-130': { file: 'src/modules/demo_requests/lib/submit-demo-request.ts', family: 'ui-business-surface', exportName: 'submitDemoRequest', requiredFlags: ['scopeDerived', 'consentAccepted'] },
34
- 'OMH-133': { file: 'src/modules/portal_quote_approval/commands/approve-quote.ts', family: 'business-command', exportName: 'approvePortalQuote', requiredFlags: ['portalScoped', 'latestVersion'] },
35
- 'OMH-137': { file: 'src/modules/setup_wizard/lib/advance-setup.ts', family: 'ui-business-surface', exportName: 'advanceSetupWizard', requiredFlags: ['draftPersisted', 'transitionAllowed'] },
36
- 'OMH-140': { file: 'src/modules/invoice_dunning/workflows/run-dunning.ts', family: 'async-operation', exportName: 'runInvoiceDunning' },
37
- 'OMH-144': { file: 'src/modules/quote_assistant/ai-tools.ts', family: 'ai-safe-agent', exportName: 'saveQuoteDraftWithApproval', mode: 'mutation' },
38
- 'OMH-146': { file: 'src/modules/sales_orchestrator/ai-agents.ts', family: 'ai-safe-agent', exportName: 'coordinateSalesQuestion', mode: 'delegate' },
39
- 'OMH-149': { file: 'src/modules/smtp_email/lib/client.ts', family: 'provider-adapter', exportName: 'sendTransactionalEmail' },
40
- 'OMH-150': { file: 'src/modules/card_payments/lib/client.ts', family: 'provider-adapter', exportName: 'createCardPayment' },
41
- 'OMH-151': { file: 'src/modules/carrier_shipping/lib/client.ts', family: 'provider-adapter', exportName: 'bookCarrierShipment' },
42
- 'OMH-153': { file: 'src/modules/erp_sync/data-sync.ts', family: 'data-flow', exportName: 'synchronizeErpPage' },
43
- 'OMH-156': { file: 'src/modules/product_transfer/lib/flow.ts', family: 'data-flow', exportName: 'transferProductRows' },
44
- 'OMH-171': { file: 'src/modules/harness_fixture/api/scope/route.ts', probeCase: 'OMH-057' },
45
- 'OMH-172': { file: 'src/modules/harness_fixture/backend/edit/page.tsx', probeCase: 'OMH-061', allowedCompiledImport: '@open-mercato/ui/backend/CrudForm' },
46
- 'OMH-181': { file: 'src/modules/order_risk/widgets/injection/order-risk-review/widget.ts', family: 'data-table-extension', exportName: 'reviewOrderRisk' },
47
- 'OMH-189': {
48
- directory: 'src/modules/room_calendar_sync/lib',
49
- file: 'src/modules/room_calendar_sync/lib/export-source.ts',
50
- requiredExport: 'assertSafeEndpoint',
51
- family: 'endpoint-guard',
52
- allowCompiledImports: true,
53
- },
54
- 'OMH-192': { file: 'src/modules/library/commands/crm-loans.ts', family: 'crm-library-regression' },
55
- }
56
-
57
- const FAMILY_PROBES = {
58
- 'business-command': new vm.Script(`
59
- 'use strict';
60
- globalThis.__oracleResult = (async () => {
61
- const config = __oracleConfig
62
- const action = module.exports[config.exportName]
63
- if (typeof action !== 'function') throw new Error('required business command export is missing')
64
- const checks = []
65
- const validInput = { idempotencyKey: 'business-key', ...Object.fromEntries(config.requiredFlags.map((flag) => [flag, true])) }
66
- const invalidInput = { ...validInput, [config.requiredFlags[0]]: false, idempotencyKey: 'invalid-key' }
67
- const committed = []
68
- const durable = new Map()
69
- let active = null
70
- const effects = {
71
- async reserveIdempotency(key) { return durable.get(key) },
72
- async transaction(work) {
73
- const pending = []
74
- active = pending
75
- try {
76
- const result = await work()
77
- committed.push(...pending)
78
- for (const entry of pending.filter((item) => item.kind === 'record')) durable.set(entry.value.idempotencyKey, entry.value.result)
79
- return result
80
- } finally { active = null }
81
- },
82
- async apply(input) {
83
- if (input.failApply) throw new Error('injected mutation failure')
84
- const result = { applied: input.idempotencyKey }
85
- ;(active ?? committed).push({ kind: 'apply', value: input })
86
- return result
87
- },
88
- async record(value) { (active ?? committed).push({ kind: 'record', value }) },
89
- }
90
-
91
- let invalidRejected = false
92
- try { await action(invalidInput, effects) } catch { invalidRejected = true }
93
- checks.push({ id: 'rejects-invalid-business-invariant', passed: invalidRejected && committed.length === 0 })
94
-
95
- let failureRejected = false
96
- try { await action({ ...validInput, failApply: true }, effects) } catch { failureRejected = true }
97
- checks.push({ id: 'rolls-back-injected-mutation-failure', passed: failureRejected && committed.length === 0 && durable.size === 0 })
98
-
99
- let result
100
- let successError
101
- try { result = await action(validInput, effects) } catch (error) { successError = error }
102
- checks.push({ id: 'commits-mutation-and-lineage-together', passed: !successError && result?.applied === 'business-key' && committed.length === 2 && committed[0]?.kind === 'apply' && committed[1]?.kind === 'record' })
103
- checks.push({ id: 'records-idempotency-lineage', passed: durable.get('business-key')?.applied === 'business-key' })
104
-
105
- let duplicateError
106
- try { await action(validInput, effects) } catch (error) { duplicateError = error }
107
- checks.push({ id: 'deduplicates-retried-command', passed: !duplicateError && committed.length === 2 })
108
- return { checks }
109
- })();
110
- `),
111
- 'ui-business-surface': new vm.Script(`
112
- 'use strict';
113
- globalThis.__oracleResult = (async () => {
114
- const config = __oracleConfig
115
- const action = module.exports[config.exportName]
116
- if (typeof action !== 'function') throw new Error('required UI business export is missing')
117
- const checks = []
118
- const validInput = { focusTarget: 'row-1', ...Object.fromEntries(config.requiredFlags.map((flag) => [flag, true])) }
119
- const invalidInput = { ...validInput, [config.requiredFlags[0]]: false }
120
- const events = []
121
- const effects = {
122
- async execute(input) { events.push('execute'); return { message: 'updated', input } },
123
- async restoreFocus(target) { events.push('focus:' + target) },
124
- async announce(message) { events.push('announce:' + message) },
125
- }
126
- let invalidRejected = false
127
- try { await action(invalidInput, effects) } catch { invalidRejected = true }
128
- checks.push({ id: 'rejects-invalid-ui-business-action', passed: invalidRejected && events.length === 0 })
129
- let result
130
- let successError
131
- try { result = await action(validInput, effects) } catch (error) { successError = error }
132
- checks.push({ id: 'executes-authorized-ui-action-once', passed: !successError && result?.message === 'updated' && events.filter((entry) => entry === 'execute').length === 1 })
133
- checks.push({ id: 'restores-semantic-focus', passed: events.includes('focus:row-1') })
134
- checks.push({ id: 'announces-observable-result', passed: events.includes('announce:updated') })
135
-
136
- const failedEvents = []
137
- let failureObserved = false
138
- try {
139
- await action(validInput, {
140
- async execute() { failedEvents.push('execute'); throw new Error('injected UI failure') },
141
- async restoreFocus(target) { failedEvents.push('focus:' + target) },
142
- async announce(message) { failedEvents.push('announce:' + message) },
143
- })
144
- } catch { failureObserved = true }
145
- checks.push({ id: 'restores-focus-after-ui-failure', passed: failureObserved && failedEvents.includes('focus:row-1') })
146
- checks.push({ id: 'announces-ui-failure', passed: failedEvents.some((entry) => entry.startsWith('announce:')) })
147
- return { checks }
148
- })();
149
- `),
150
- 'data-table-extension': new vm.Script(`
151
- 'use strict';
152
- globalThis.__oracleResult = (async () => {
153
- const config = __oracleConfig
154
- const action = module.exports[config.exportName]
155
- if (typeof action !== 'function') throw new Error('required DataTable bulk action export is missing')
156
- const checks = []
157
- const rows = [{ id: 'one', updatedAt: 'v1' }, { id: 'two', updatedAt: 'v2' }]
158
-
159
- let deniedExecutions = 0
160
- let denied = false
161
- try {
162
- await action(rows, {
163
- async authorize() { return false },
164
- async checkVersion() { return true },
165
- async surfaceConflict() {},
166
- async execute() { deniedExecutions += 1 },
167
- })
168
- } catch { denied = true }
169
- checks.push({ id: 'denies-unauthorized-bulk-review', passed: denied && deniedExecutions === 0 })
170
-
171
- const conflicts = []
172
- let staleExecutions = 0
173
- let stale = false
174
- try {
175
- await action(rows, {
176
- async authorize() { return true },
177
- async checkVersion(row) { return row.id !== 'two' },
178
- async surfaceConflict(row) { conflicts.push(row.id) },
179
- async execute() { staleExecutions += 1 },
180
- })
181
- } catch { stale = true }
182
- checks.push({ id: 'checks-every-selected-version', passed: stale && conflicts.join(',') === 'two' && staleExecutions === 0 })
183
-
184
- const checked = []
185
- let executedRows = []
186
- const result = await action(rows, {
187
- async authorize(feature) { return feature === 'sales.orders.manage' },
188
- async checkVersion(row) { checked.push(row.id + ':' + row.updatedAt); return true },
189
- async surfaceConflict() {},
190
- async execute(selectedRows) { executedRows = selectedRows; return { ok: true, progressJobId: 'job-1' } },
191
- })
192
- checks.push({ id: 'executes-exact-selected-orders', passed: checked.join(',') === 'one:v1,two:v2' && executedRows === rows })
193
- checks.push({ id: 'returns-shared-progress-job', passed: result?.ok === true && result?.progressJobId === 'job-1' })
194
- return { checks }
195
- })();
196
- `),
197
- 'async-operation': new vm.Script(`
198
- 'use strict';
199
- globalThis.__oracleResult = (async () => {
200
- const config = __oracleConfig
201
- const action = module.exports[config.exportName]
202
- if (typeof action !== 'function') throw new Error('required async operation export is missing')
203
- const checks = []
204
- const items = [{ id: 'one' }, { id: 'paid-or-skipped', skip: true }, { id: 'three' }]
205
- const applied = []
206
- const progress = []
207
- const undo = []
208
- const effects = {
209
- async isCancelled() { return false },
210
- async shouldSkip(item) { return item.skip === true },
211
- async applyChunk(chunk) { applied.push(...chunk.map((item) => item.id)); return { ids: chunk.map((item) => item.id) } },
212
- async reportProgress(value) { progress.push(value) },
213
- async registerUndo(value) { undo.push(value) },
214
- }
215
- let successError
216
- try { await action(items, effects) } catch (error) { successError = error }
217
- checks.push({ id: 'skips-ineligible-or-paid-work', passed: !successError && applied.join(',') === 'one,three' })
218
- checks.push({ id: 'reports-monotonic-progress', passed: progress.length === 2 && progress[0]?.completed < progress[1]?.completed && progress[1]?.total === 3 })
219
- checks.push({ id: 'registers-compensating-undo', passed: undo.length === 2 && undo.every((entry) => Array.isArray(entry?.ids)) })
220
-
221
- const cancelledApplied = []
222
- let cancellationChecks = 0
223
- await action(items, {
224
- async isCancelled() { cancellationChecks += 1; return cancelledApplied.length >= 1 },
225
- async shouldSkip(item) { return item.skip === true },
226
- async applyChunk(chunk) { cancelledApplied.push(...chunk.map((item) => item.id)); return { ids: chunk.map((item) => item.id) } },
227
- async reportProgress() {},
228
- async registerUndo() {},
229
- })
230
- checks.push({ id: 'stops-at-cancellation-boundary', passed: cancellationChecks >= 2 && cancelledApplied.join(',') === 'one' })
231
- return { checks }
232
- })();
233
- `),
234
- 'ai-safe-agent': new vm.Script(`
235
- 'use strict';
236
- globalThis.__oracleResult = (async () => {
237
- const config = __oracleConfig
238
- const action = module.exports[config.exportName]
239
- if (typeof action !== 'function') throw new Error('required AI action export is missing')
240
- const checks = []
241
- if (config.mode === 'mutation') {
242
- const events = []
243
- let denied = false
244
- try { await action({ change: 'quote' }, { async authorize() { return false }, async prepareMutation() { events.push('prepare') }, async execute() { events.push('execute') } }) } catch { denied = true }
245
- checks.push({ id: 'denies-unauthorized-ai-mutation', passed: denied && events.length === 0 })
246
- const pending = await action({ change: 'quote' }, { async authorize() { return true }, async prepareMutation() { events.push('prepare'); return { approved: false } }, async execute() { events.push('execute') } })
247
- checks.push({ id: 'does-not-execute-pending-mutation', passed: pending?.status === 'pending' && !events.includes('execute') })
248
- let approvedError
249
- try { await action({ change: 'quote' }, { async authorize() { events.push('authorize'); return true }, async prepareMutation(input) { events.push('prepare-approved'); return { approved: true, input } }, async execute() { events.push('execute'); return { saved: true } } }) } catch (error) { approvedError = error }
250
- checks.push({ id: 'executes-only-after-approval', passed: !approvedError && events.slice(-3).join(',') === 'authorize,prepare-approved,execute' })
251
- } else {
252
- const delegated = []
253
- let denied = false
254
- try { await action({ question: 'stock' }, { async authorize() { return false }, async delegate(...args) { delegated.push(args) } }) } catch { denied = true }
255
- checks.push({ id: 'denies-unauthorized-delegation', passed: denied && delegated.length === 0 })
256
- await action({ question: 'stock', requestedMutation: true, allowedTools: ['catalog.read'] }, { async authorize() { return true }, async delegate(input, options) { delegated.push([input, options]); return { answer: 'ok' } } })
257
- checks.push({ id: 'caps-delegated-authority', passed: delegated.length === 1 && delegated[0][1]?.authority === 'read-only' })
258
- checks.push({ id: 'passes-explicit-tool-allowlist', passed: delegated[0][1]?.allowedTools?.join(',') === 'catalog.read' })
259
- }
260
- return { checks }
261
- })();
262
- `),
263
- 'provider-adapter': new vm.Script(`
264
- 'use strict';
265
- globalThis.__oracleResult = (async () => {
266
- const config = __oracleConfig
267
- const action = module.exports[config.exportName]
268
- if (typeof action !== 'function') throw new Error('required provider adapter export is missing')
269
- const checks = []
270
- let requests = 0
271
- const duplicate = await action({ idempotencyKey: 'existing', maxAttempts: 2 }, {
272
- async findExisting() { return { id: 'prior' } },
273
- async request() { requests += 1 },
274
- async reconcile(value) { return value },
275
- redact(value) { return value },
276
- })
277
- checks.push({ id: 'returns-existing-idempotent-result', passed: duplicate?.id === 'prior' && requests === 0 })
278
-
279
- let reconciled = 0
280
- let retryError
281
- let result
282
- try {
283
- result = await action({ idempotencyKey: 'new', maxAttempts: 2 }, {
284
- async findExisting() { return undefined },
285
- async request() { requests += 1; if (requests === 1) throw new Error('transient'); return { id: 'provider-id', secret: 'hidden' } },
286
- async reconcile(value) { reconciled += 1; return value },
287
- redact(value) { return { id: value.id } },
288
- })
289
- } catch (error) { retryError = error }
290
- checks.push({ id: 'retries-transient-provider-failure', passed: !retryError && requests === 2 })
291
- checks.push({ id: 'reconciles-and-redacts-result', passed: reconciled === 1 && result?.id === 'provider-id' && !Object.prototype.hasOwnProperty.call(result ?? {}, 'secret') })
292
-
293
- let terminalMessage = ''
294
- try {
295
- await action({ idempotencyKey: 'terminal', maxAttempts: 2 }, {
296
- async findExisting() { return undefined },
297
- async request() { throw new Error('secret-token-value') },
298
- async reconcile(value) { return value },
299
- redact(value) { return value },
300
- })
301
- } catch (error) { terminalMessage = String(error?.message ?? error) }
302
- checks.push({ id: 'redacts-terminal-provider-error', passed: terminalMessage.length > 0 && !terminalMessage.includes('secret-token-value') })
303
- return { checks }
304
- })();
305
- `),
306
- 'endpoint-guard': new vm.Script(`
307
- 'use strict';
308
- globalThis.__oracleResult = (async () => {
309
- const guard = module.exports.assertSafeEndpoint
310
- if (typeof guard !== 'function') throw new Error('required assertSafeEndpoint export is missing')
311
- const checks = []
312
- const unsafe = [
313
- 'http://calendar.example/events',
314
- 'https://localhost/events',
315
- 'https://127.0.0.1/events',
316
- 'https://[::1]/events',
317
- 'https://10.1.2.3/events',
318
- 'https://172.16.0.1/events',
319
- 'https://172.31.255.254/events',
320
- 'https://192.168.1.1/events',
321
- 'https://169.254.169.254/latest/meta-data',
322
- 'https://metadata.internal/latest/meta-data',
323
- ]
324
- const rejected = []
325
- for (const endpoint of unsafe) {
326
- try { await guard(endpoint) } catch { rejected.push(endpoint) }
327
- }
328
- checks.push({ id: 'rejects-non-https-and-private-endpoints', passed: rejected.length === unsafe.length })
329
- let publicError
330
- try { await guard('https://calendar.example/events') } catch (error) { publicError = error }
331
- checks.push({ id: 'accepts-public-https-endpoint', passed: !publicError })
332
- return { checks }
333
- })();
334
- `),
335
- 'data-flow': new vm.Script(`
336
- 'use strict';
337
- globalThis.__oracleResult = (async () => {
338
- const config = __oracleConfig
339
- const action = module.exports[config.exportName]
340
- if (typeof action !== 'function') throw new Error('required data-flow export is missing')
341
- const checks = []
342
- const events = []
343
- const applied = []
344
- let result
345
- let flowError
346
- try {
347
- result = await action({ cursor: 'current' }, {
348
- async fetchPage() { events.push('fetch'); return { items: [{ id: 'one', value: '=1+1' }, { id: 'bad', value: 'bad' }, { id: 'two', value: 'ok' }], nextCursor: 'next' } },
349
- sanitize(row) { events.push('sanitize:' + row.id); return { ...row, value: String(row.value).startsWith('=') ? "'" + row.value : row.value } },
350
- async apply(row) { events.push('apply:' + row.id); if (row.id === 'bad') throw new Error('row rejected'); applied.push(row) },
351
- async commitCursor(cursor) { events.push('commit:' + cursor) },
352
- })
353
- } catch (error) { flowError = error }
354
- checks.push({ id: 'isolates-bad-row', passed: !flowError && result?.errors?.length === 1 && applied.map((row) => row.id).join(',') === 'one,two' })
355
- checks.push({ id: 'neutralizes-formula-content', passed: applied[0]?.value === "'=1+1" })
356
- checks.push({ id: 'commits-cursor-after-rows', passed: events.at(-1) === 'commit:next' })
357
-
358
- let failedCommit = false
359
- let fetchRejected = false
360
- try {
361
- await action({ cursor: 'stable' }, {
362
- async fetchPage() { throw new Error('page failed') },
363
- sanitize(row) { return row },
364
- async apply() {},
365
- async commitCursor() { failedCommit = true },
366
- })
367
- } catch { fetchRejected = true }
368
- checks.push({ id: 'preserves-cursor-on-page-failure', passed: fetchRejected && !failedCommit })
369
- return { checks }
370
- })();
371
- `),
372
- 'crm-library-regression': new vm.Script(`
373
- 'use strict';
374
- globalThis.__oracleResult = (async () => {
375
- const required = ['createBook', 'undoCreateBook', 'deleteBook', 'undoDeleteBook', 'checkoutBook', 'returnLoan', 'renewLoan', 'markLoanLost']
376
- const actions = Object.fromEntries(required.map((name) => [name, module.exports[name]]))
377
- if (required.some((name) => typeof actions[name] !== 'function')) throw new Error('required CRM library export is missing')
378
-
379
- const checks = []
380
- const trustedScope = { tenantId: 'tenant-a', organizationId: 'org-a' }
381
- const attackerScope = { tenantId: 'tenant-b', organizationId: 'org-b' }
382
- const books = new Map()
383
- const loans = new Map()
384
- const idempotency = new Map()
385
- const activeByBook = new Map()
386
- const scopeEvents = []
387
- const customerEvents = []
388
- let effectCalls = 0
389
-
390
- const assertScope = (scope) => {
391
- effectCalls += 1
392
- scopeEvents.push(scope?.tenantId + ':' + scope?.organizationId)
393
- }
394
-
395
- const effects = {
396
- async createBook(record, scope) {
397
- assertScope(scope)
398
- const stored = { ...record, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null }
399
- books.set(stored.id, stored)
400
- return { ...stored }
401
- },
402
- async softDeleteBook(id, scope) {
403
- assertScope(scope)
404
- const book = books.get(id)
405
- if (!book) throw new Error('book not found')
406
- book.deletedAt = 'deleted'
407
- return { ...book }
408
- },
409
- async restoreBook(id, scope) {
410
- assertScope(scope)
411
- const book = books.get(id)
412
- if (!book) throw new Error('book not found')
413
- book.deletedAt = null
414
- return { ...book }
415
- },
416
- async resolveCustomer(id, scope) {
417
- assertScope(scope)
418
- customerEvents.push({ id, scope: { ...scope } })
419
- return { id, displayName: 'Scoped CRM customer' }
420
- },
421
- async findActiveLoan(bookId, scope) {
422
- assertScope(scope)
423
- await Promise.resolve()
424
- const loanId = activeByBook.get(bookId)
425
- return loanId ? { ...loans.get(loanId) } : undefined
426
- },
427
- async createLoan(input, scope) {
428
- assertScope(scope)
429
- await Promise.resolve()
430
- const loan = { ...input, id: 'loan-' + (loans.size + 1), status: 'active' }
431
- loans.set(loan.id, loan)
432
- activeByBook.set(loan.bookId, loan.id)
433
- return { ...loan }
434
- },
435
- async claimCheckout(input, scope) {
436
- assertScope(scope)
437
- if ('memberId' in input) throw new Error('duplicate local member leaked into checkout')
438
- const priorId = idempotency.get(input.idempotencyKey)
439
- if (priorId) return { status: 'existing', loan: { ...loans.get(priorId) } }
440
- if (activeByBook.has(input.bookId)) return { status: 'conflict' }
441
- const loan = { ...input, id: 'loan-' + (loans.size + 1), tenantId: scope.tenantId, organizationId: scope.organizationId, status: 'active' }
442
- loans.set(loan.id, loan)
443
- activeByBook.set(input.bookId, loan.id)
444
- idempotency.set(input.idempotencyKey, loan.id)
445
- return { status: 'created', loan: { ...loan } }
446
- },
447
- async findLoan(id, scope) {
448
- assertScope(scope)
449
- const loan = loans.get(id)
450
- return loan ? { ...loan } : undefined
451
- },
452
- async updateLoan(id, scope, patch) {
453
- assertScope(scope)
454
- const loan = loans.get(id)
455
- if (!loan) throw new Error('loan not found')
456
- Object.assign(loan, patch)
457
- return { ...loan }
458
- },
459
- }
460
-
461
- let missingScopeRejected = false
462
- const callsBeforeMissingScope = effectCalls
463
- try {
464
- await actions.checkoutBook({ bookId: 'missing-scope', customerEntityId: 'customer-a', idempotencyKey: 'missing-scope' }, {}, effects)
465
- } catch { missingScopeRejected = true }
466
- checks.push({ id: 'fails-closed-before-effects', passed: missingScopeRejected && effectCalls === callsBeforeMissingScope })
467
-
468
- await actions.createBook({ id: 'created-book', title: 'Created', ...attackerScope }, trustedScope, effects)
469
- await actions.undoCreateBook('created-book', trustedScope, effects)
470
- checks.push({ id: 'create-undo-soft-deletes', passed: books.get('created-book')?.deletedAt === 'deleted' })
471
-
472
- await actions.createBook({ id: 'restored-book', title: 'Restored', ...attackerScope }, trustedScope, effects)
473
- await actions.deleteBook('restored-book', trustedScope, effects)
474
- await actions.undoDeleteBook('restored-book', trustedScope, effects)
475
- checks.push({ id: 'delete-undo-restores', passed: books.get('restored-book')?.deletedAt === null })
476
- checks.push({ id: 'body-scope-cannot-override-trusted-scope', passed: [...books.values()].every((book) => book.tenantId === trustedScope.tenantId && book.organizationId === trustedScope.organizationId) })
477
-
478
- const checkoutInput = { bookId: 'shared-copy', customerEntityId: 'customer-a', ...attackerScope }
479
- const concurrent = await Promise.all([
480
- actions.checkoutBook({ ...checkoutInput, idempotencyKey: 'checkout-a' }, trustedScope, effects).catch(() => ({ status: 'conflict' })),
481
- actions.checkoutBook({ ...checkoutInput, idempotencyKey: 'checkout-b' }, trustedScope, effects).catch(() => ({ status: 'conflict' })),
482
- ])
483
- const statuses = concurrent.map((result) => result?.status).sort()
484
- checks.push({ id: 'concurrent-checkout-has-one-winner', passed: statuses.join(',') === 'conflict,created' && [...loans.values()].filter((loan) => loan.bookId === 'shared-copy' && loan.status === 'active').length === 1 })
485
-
486
- const winner = concurrent.find((result) => result?.status === 'created')
487
- const winnerKey = winner?.loan?.id === loans.get(idempotency.get('checkout-a'))?.id ? 'checkout-a' : 'checkout-b'
488
- const retry = await actions.checkoutBook({ ...checkoutInput, idempotencyKey: winnerKey }, trustedScope, effects)
489
- checks.push({ id: 'checkout-retry-is-deterministic', passed: retry?.status === 'existing' && retry?.loan?.id === winner?.loan?.id && loans.size === 1 })
490
- checks.push({ id: 'resolves-scoped-crm-customer-snapshot', passed: customerEvents.length >= 2 && customerEvents.every((event) => event.id === 'customer-a' && event.scope.tenantId === trustedScope.tenantId) && winner?.loan?.customerEntityId === 'customer-a' && winner?.loan?.customerNameSnapshot === 'Scoped CRM customer' && !('memberId' in (winner?.loan ?? {})) })
491
-
492
- for (const [id, action] of [['return-loan', actions.returnLoan], ['renew-loan', actions.renewLoan], ['lost-loan', actions.markLoanLost]]) {
493
- loans.set(id, { id, bookId: id, customerEntityId: 'customer-a', status: 'active' })
494
- await action({ id, ...attackerScope }, trustedScope, effects)
495
- }
496
- checks.push({ id: 'all-loan-actions-use-trusted-scope', passed: ['return-loan', 'renew-loan', 'lost-loan'].every((id) => loans.get(id)?.status !== 'active') && scopeEvents.every((entry) => entry === 'tenant-a:org-a') })
497
- return { checks }
498
- })();
499
- `),
500
- }
501
-
502
- const PROBES = {
503
- 'OMH-045': new vm.Script(`
504
- 'use strict';
505
- globalThis.__oracleResult = (async () => {
506
- const fetchPage = module.exports.fetchPage
507
- if (typeof fetchPage !== 'function') throw new Error('required export fetchPage is missing')
508
- const checks = []
509
- const state = { cursor: 'cursor-current' }
510
- const urls = []
511
- let attempt = 0
512
- let page
513
- let retryError
514
- const effects = {
515
- maxAttempts: 2,
516
- async fetch(url) {
517
- urls.push(url)
518
- attempt += 1
519
- if (attempt === 1) return { ok: false, status: 503, async json() { return {} } }
520
- return { ok: true, status: 200, async json() { return { items: ['record'], nextCursor: 'cursor-next' } } }
521
- },
522
- }
523
- try { page = await fetchPage('https://provider.test/records', state, effects) } catch (error) { retryError = error }
524
- checks.push({ id: 'retries-transient-response', passed: !retryError && attempt === 2 })
525
- checks.push({ id: 'retries-same-cursor', passed: urls.length === 2 && urls.every((url) => url.endsWith('cursor=cursor-current')) })
526
- checks.push({ id: 'commits-cursor-after-success', passed: state.cursor === 'cursor-next' && page?.nextCursor === 'cursor-next' })
527
-
528
- const failedState = { cursor: 'cursor-stable' }
529
- let failedAttempts = 0
530
- let terminalRejected = false
531
- try {
532
- await fetchPage('https://provider.test/records', failedState, {
533
- maxAttempts: 2,
534
- async fetch() {
535
- failedAttempts += 1
536
- return { ok: false, status: 503, async json() { return {} } }
537
- },
538
- })
539
- } catch { terminalRejected = true }
540
- checks.push({ id: 'bounds-retries', passed: terminalRejected && failedAttempts === 2 })
541
- checks.push({ id: 'preserves-cursor-after-terminal-failure', passed: failedState.cursor === 'cursor-stable' })
542
-
543
- let privateFetchCalls = 0
544
- let privateEndpointRejected = false
545
- try {
546
- await fetchPage('http://127.0.0.1/admin', { cursor: 'cursor-private' }, {
547
- maxAttempts: 2,
548
- async fetch() {
549
- privateFetchCalls += 1
550
- return { ok: true, status: 200, async json() { return { items: [] } } }
551
- },
552
- })
553
- } catch { privateEndpointRejected = true }
554
- checks.push({ id: 'rejects-private-endpoint-before-fetch', passed: privateEndpointRejected && privateFetchCalls === 0 })
555
- return { checks }
556
- })();
557
- `),
558
- 'OMH-054': new vm.Script(`
559
- 'use strict';
560
- globalThis.__oracleResult = (async () => {
561
- const callApiActivity = module.exports.callApiActivity
562
- if (typeof callApiActivity !== 'function') throw new Error('required export callApiActivity is missing')
563
- const checks = []
564
- const durable = new Set()
565
- const posts = []
566
- const events = []
567
- let transactionState = null
568
- const effects = {
569
- async reserveIdempotency(key) {
570
- events.push('reserve:' + key)
571
- if (transactionState) transactionState.add(key)
572
- else durable.add(key)
573
- return key
574
- },
575
- async transaction(work) {
576
- events.push('transaction:start')
577
- const staged = new Set()
578
- transactionState = staged
579
- try {
580
- const result = await work()
581
- for (const key of staged) durable.add(key)
582
- events.push('transaction:commit')
583
- return result
584
- } catch (error) {
585
- events.push('transaction:rollback')
586
- throw error
587
- } finally {
588
- transactionState = null
589
- }
590
- },
591
- async post(url, options) {
592
- posts.push({ url, key: options?.idempotencyKey })
593
- if (posts.length === 1) throw new Error('injected post failure')
594
- return { ok: true }
595
- },
596
- }
597
- const input = { url: 'https://provider.test/action', idempotencyKey: 'activity-key' }
598
- let firstRejected = false
599
- try { await callApiActivity(input, effects) } catch { firstRejected = true }
600
- checks.push({ id: 'injected-failure-rolls-back-transaction', passed: firstRejected && events.includes('transaction:rollback') })
601
- checks.push({ id: 'idempotency-survives-rollback', passed: durable.has('activity-key') })
602
- let retryError
603
- try { await callApiActivity(input, effects) } catch (error) { retryError = error }
604
- checks.push({ id: 'retry-reuses-idempotency-key', passed: !retryError && posts.length === 2 && posts.every((post) => post.key === 'activity-key') })
605
- checks.push({ id: 'reservation-precedes-transaction', passed: events[0] === 'reserve:activity-key' && events[1] === 'transaction:start' })
606
- return { checks }
607
- })();
608
- `),
609
- 'OMH-057': new vm.Script(`
610
- 'use strict';
611
- globalThis.__oracleResult = (async () => {
612
- const listRecords = module.exports.listRecords
613
- if (typeof listRecords !== 'function') throw new Error('required export listRecords is missing')
614
- const checks = []
615
- const calls = []
616
- const store = { async find(filter) { calls.push(filter); return ['record'] } }
617
- let missingTenantRejected = false
618
- try { await listRecords({ organizationId: 'organization-1' }, store) } catch { missingTenantRejected = true }
619
- let missingOrganizationRejected = false
620
- try { await listRecords({ tenantId: 'tenant-1' }, store) } catch { missingOrganizationRejected = true }
621
- checks.push({ id: 'rejects-missing-tenant', passed: missingTenantRejected })
622
- checks.push({ id: 'rejects-missing-organization', passed: missingOrganizationRejected })
623
- checks.push({ id: 'does-not-query-with-missing-scope', passed: calls.length === 0 })
624
- let validResult
625
- let validError
626
- try { validResult = await listRecords({ tenantId: 'tenant-1', organizationId: 'organization-1' }, store) } catch (error) { validError = error }
627
- const filter = calls[0]
628
- checks.push({
629
- id: 'queries-with-complete-scope',
630
- passed: !validError && Array.isArray(validResult) && calls.length === 1
631
- && filter?.tenant_id === 'tenant-1' && filter?.organization_id === 'organization-1',
632
- })
633
- return { checks }
634
- })();
635
- `),
636
- 'OMH-060': new vm.Script(`
637
- 'use strict';
638
- globalThis.__oracleResult = (async () => {
639
- const updateRecord = module.exports.updateRecord
640
- if (typeof updateRecord !== 'function') throw new Error('required export updateRecord is missing')
641
- const checks = []
642
- function makeStore() {
643
- const committed = []
644
- const store = {
645
- committed,
646
- async persist(value) { committed.push({ ...value }) },
647
- async flush() {},
648
- async transaction(work) {
649
- const pending = []
650
- const transactionalStore = {
651
- async persist(value) { pending.push({ ...value }) },
652
- async flush() {},
653
- }
654
- const result = await work(transactionalStore)
655
- committed.push(...pending)
656
- return result
657
- },
658
- }
659
- return store
660
- }
661
- const failedStore = makeStore()
662
- let injectedFailureObserved = false
663
- try { await updateRecord(failedStore, true) } catch { injectedFailureObserved = true }
664
- checks.push({ id: 'propagates-injected-failure', passed: injectedFailureObserved })
665
- checks.push({ id: 'rolls-back-all-phases', passed: failedStore.committed.length === 0 })
666
- const successStore = makeStore()
667
- let successError
668
- try { await updateRecord(successStore, false) } catch (error) { successError = error }
669
- checks.push({
670
- id: 'commits-complete-write-on-success',
671
- passed: !successError && successStore.committed.length === 2
672
- && successStore.committed[0]?.phase === 1 && successStore.committed[1]?.phase === 2,
673
- })
674
- return { checks }
675
- })();
676
- `),
677
- 'OMH-061': new vm.Script(`
678
- 'use strict';
679
- globalThis.__oracleResult = (async () => {
680
- const toInitialValues = module.exports.toInitialValues
681
- const toUpdatePayload = module.exports.toUpdatePayload
682
- if (typeof toInitialValues !== 'function' || typeof toUpdatePayload !== 'function') {
683
- throw new Error('required nullable round-trip exports are missing')
684
- }
685
- const checks = []
686
- const initialNull = toInitialValues({ note: null })
687
- checks.push({ id: 'loads-explicit-null', passed: initialNull?.note === null })
688
- const cleared = toUpdatePayload({ note: '' })
689
- checks.push({ id: 'serializes-clear-as-null', passed: Object.prototype.hasOwnProperty.call(cleared ?? {}, 'note') && cleared.note === null })
690
- const reloaded = toInitialValues(cleared)
691
- checks.push({ id: 'reload-preserves-null', passed: reloaded?.note === null })
692
- const text = 'kept value'
693
- const nonNull = toUpdatePayload(toInitialValues({ note: text }))
694
- checks.push({ id: 'round-trips-non-null-value', passed: nonNull?.note === text })
695
- return { checks }
696
- })();
697
- `),
698
- 'OMH-070': new vm.Script(`
699
- 'use strict';
700
- globalThis.__oracleResult = (async () => {
701
- const syncPage = module.exports.syncPage
702
- if (typeof syncPage !== 'function') throw new Error('required export syncPage is missing')
703
- const checks = []
704
- const state = { cursor: 'cursor-current' }
705
- const received = []
706
- let attempt = 0
707
- let page
708
- let retryError
709
- const provider = {
710
- maxAttempts: 2,
711
- nextCursor: 'cursor-next',
712
- async fetchPage(cursor) {
713
- received.push(cursor)
714
- attempt += 1
715
- if (attempt === 1) throw new Error('transient provider failure')
716
- return { items: ['record'], nextCursor: 'cursor-next' }
717
- },
718
- }
719
- try { page = await syncPage(state, provider) } catch (error) { retryError = error }
720
- checks.push({ id: 'retries-transient-page', passed: !retryError && attempt === 2 })
721
- checks.push({ id: 'retries-current-cursor', passed: received.length === 2 && received.every((cursor) => cursor === 'cursor-current') })
722
- checks.push({ id: 'advances-after-success', passed: state.cursor === 'cursor-next' && page?.nextCursor === 'cursor-next' })
723
-
724
- const failedState = { cursor: 'cursor-stable' }
725
- let failedAttempts = 0
726
- let terminalRejected = false
727
- try {
728
- await syncPage(failedState, {
729
- maxAttempts: 2,
730
- nextCursor: 'cursor-never',
731
- async fetchPage(cursor) {
732
- if (cursor !== 'cursor-stable') throw new Error('cursor advanced before success')
733
- failedAttempts += 1
734
- throw new Error('transient provider failure')
735
- },
736
- })
737
- } catch { terminalRejected = true }
738
- checks.push({ id: 'bounds-page-retries', passed: terminalRejected && failedAttempts === 2 })
739
- checks.push({ id: 'preserves-cursor-after-terminal-failure', passed: failedState.cursor === 'cursor-stable' })
740
- return { checks }
741
- })();
742
- `),
743
- }
744
-
745
- const ORACLE_CONFIG_SCRIPT = new vm.Script(`
746
- 'use strict';
747
- globalThis.__oracleConfig = Object.freeze({
748
- exportName: globalThis.__oracleExportName,
749
- mode: globalThis.__oracleMode,
750
- requiredFlags: Object.freeze(JSON.parse(globalThis.__oracleRequiredFlagsJson)),
751
- });
752
- `)
753
-
754
- function usage() {
755
- return `Usage: node .ai/harness/writable-behavior-oracles.mjs --root <absolute-app-path> --case <OMH-NNN> --phase <before|after> [--json]`
756
- }
757
-
758
- function parseArgs(argv) {
759
- const options = { root: undefined, caseId: undefined, phase: undefined, json: false, internalCase: undefined }
760
- for (let index = 0; index < argv.length; index += 1) {
761
- const arg = argv[index]
762
- const takeValue = () => {
763
- const value = argv[index + 1]
764
- if (!value || value.startsWith('--')) throw new Error(`${arg} requires a value`)
765
- index += 1
766
- return value
767
- }
768
- if (arg === '--root') options.root = takeValue()
769
- else if (arg === '--case') options.caseId = takeValue()
770
- else if (arg === '--phase') options.phase = takeValue()
771
- else if (arg === '--json') options.json = true
772
- else if (arg === '--internal-case') options.internalCase = takeValue()
773
- else if (arg === '--help' || arg === '-h') options.help = true
774
- else throw new Error(`unknown argument: ${arg}`)
775
- }
776
- return options
777
- }
778
-
779
- function isPathInside(root, candidate) {
780
- const relative = path.relative(root, candidate)
781
- return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
782
- }
783
-
784
- function safeMessage(error, root) {
785
- let message = String(error?.message ?? error ?? 'unknown error')
786
- for (const sensitive of [root, process.env.HOME, os.homedir()].filter(Boolean).sort((a, b) => b.length - a.length)) {
787
- message = message.split(sensitive).join('<redacted-path>')
788
- }
789
- return message
790
- .replace(/\b(?:sk|ghp|github_pat|xox[baprs])[-_A-Za-z0-9]{8,}\b/g, '<redacted-token>')
791
- .slice(0, 500)
792
- }
793
-
794
- function hasExportModifier(ts, node) {
795
- return Boolean(node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword))
796
- }
797
-
798
- function unwrapTypeExpression(ts, node) {
799
- let current = node
800
- while (current && (
801
- ts.isParenthesizedExpression(current)
802
- || ts.isAsExpression(current)
803
- || ts.isTypeAssertionExpression(current)
804
- || ts.isNonNullExpression(current)
805
- || (typeof ts.isSatisfiesExpression === 'function' && ts.isSatisfiesExpression(current))
806
- )) current = current.expression
807
- return current
808
- }
809
-
810
- function sourceDeclaresConcreteExport(ts, file, source, exportName) {
811
- const ast = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS)
812
- const localFunctions = new Set()
813
- let direct = false
814
- let exportedLocally = false
815
- for (const statement of ast.statements) {
816
- if (ts.isFunctionDeclaration(statement) && statement.name?.text === exportName) {
817
- localFunctions.add(exportName)
818
- if (hasExportModifier(ts, statement)) direct = true
819
- continue
820
- }
821
- if (ts.isVariableStatement(statement)) {
822
- for (const declaration of statement.declarationList.declarations) {
823
- if (!ts.isIdentifier(declaration.name) || declaration.name.text !== exportName) continue
824
- const initializer = declaration.initializer ? unwrapTypeExpression(ts, declaration.initializer) : undefined
825
- if (!initializer || (!ts.isArrowFunction(initializer) && !ts.isFunctionExpression(initializer))) continue
826
- localFunctions.add(exportName)
827
- if (hasExportModifier(ts, statement)) direct = true
828
- }
829
- continue
830
- }
831
- if (!ts.isExportDeclaration(statement) || statement.moduleSpecifier || !statement.exportClause || !ts.isNamedExports(statement.exportClause)) continue
832
- exportedLocally ||= statement.exportClause.elements.some((element) =>
833
- (element.propertyName?.text ?? element.name.text) === exportName && element.name.text === exportName)
834
- }
835
- return direct || (exportedLocally && localFunctions.has(exportName))
836
- }
837
-
838
- function findExportSource(root, caseRecord) {
839
- const ts = loadTargetTypeScript(root)
840
- const directory = path.resolve(root, caseRecord.directory)
841
- if (!isPathInside(root, directory)) throw new Error('oracle target directory escapes the app root')
842
- const realRoot = fs.realpathSync(root)
843
- const realDirectory = fs.realpathSync(directory)
844
- if (!isPathInside(realRoot, realDirectory)) throw new Error('oracle target directory resolves outside the app root')
845
- const candidates = []
846
- const pending = [realDirectory]
847
- while (pending.length) {
848
- const current = pending.pop()
849
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
850
- if (entry.isSymbolicLink()) throw new Error('oracle target directory contains a symbolic link')
851
- const absolute = path.join(current, entry.name)
852
- if (entry.isDirectory()) {
853
- pending.push(absolute)
854
- continue
855
- }
856
- if (!entry.isFile() || !/\.[cm]?tsx?$/.test(entry.name) || /\.(?:d|test|spec)\.[cm]?tsx?$/.test(entry.name)) continue
857
- const stat = fs.statSync(absolute)
858
- if (stat.size > MAX_SOURCE_BYTES) throw new Error(`oracle target exceeds ${MAX_SOURCE_BYTES} bytes`)
859
- const source = fs.readFileSync(absolute, 'utf8')
860
- if (sourceDeclaresConcreteExport(ts, absolute, source, caseRecord.requiredExport)) candidates.push({ absolute, source })
861
- }
862
- }
863
- if (candidates.length !== 1) {
864
- throw new Error(`oracle target must contain exactly one exported ${caseRecord.requiredExport}; found ${candidates.length}`)
865
- }
866
- return candidates[0]
867
- }
868
-
869
- function readTargetSource(root, caseRecord) {
870
- if (caseRecord.directory) {
871
- const match = findExportSource(root, caseRecord)
872
- return { source: match.source, file: path.relative(root, match.absolute) }
873
- }
874
- const candidate = path.resolve(root, caseRecord.file)
875
- if (!isPathInside(root, candidate)) throw new Error('oracle target escapes the app root')
876
- const realRoot = fs.realpathSync(root)
877
- const realCandidate = fs.realpathSync(candidate)
878
- if (!isPathInside(realRoot, realCandidate)) throw new Error('oracle target resolves outside the app root')
879
- const stat = fs.statSync(realCandidate)
880
- if (!stat.isFile()) throw new Error(`oracle target is not a file: ${caseRecord.file}`)
881
- if (stat.size > MAX_SOURCE_BYTES) throw new Error(`oracle target exceeds ${MAX_SOURCE_BYTES} bytes`)
882
- return { source: fs.readFileSync(realCandidate, 'utf8'), file: caseRecord.file }
883
- }
884
-
885
- function loadTargetTypeScript(root) {
886
- const targetRequire = createRequire(path.join(root, 'package.json'))
887
- let resolved
888
- try { resolved = targetRequire.resolve('typescript') } catch { throw new Error('target app TypeScript compiler is unavailable') }
889
- return targetRequire(resolved)
890
- }
891
-
892
- function transpileTarget(root, caseRecord, source) {
893
- const ts = loadTargetTypeScript(root)
894
- const result = ts.transpileModule(source, {
895
- fileName: caseRecord.file,
896
- reportDiagnostics: true,
897
- compilerOptions: {
898
- module: ts.ModuleKind.CommonJS,
899
- target: ts.ScriptTarget.ES2022,
900
- jsx: ts.JsxEmit.React,
901
- esModuleInterop: true,
902
- },
903
- })
904
- const diagnostics = (result.diagnostics ?? []).filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)
905
- if (diagnostics.length) {
906
- const codes = diagnostics.slice(0, 8).map((diagnostic) => `TS${diagnostic.code}`).join(', ')
907
- throw new Error(`target TypeScript compilation failed (${codes})`)
908
- }
909
- let output = result.outputText
910
- let importStub = ''
911
- if (caseRecord.allowCompiledImports) {
912
- importStub = `
913
- const __harnessPublicAddress = '1.1.1.1'
914
- const __harnessDnsPromises = Object.freeze({
915
- async lookup(hostname, options) {
916
- const address = hostname === 'localhost' ? '127.0.0.1' : hostname === '[::1]' ? '::1' : hostname === 'metadata.internal' ? '169.254.169.254' : /^\\d+(?:\\.\\d+){3}$/.test(hostname) ? hostname : __harnessPublicAddress
917
- return options?.all ? [{ address, family: address.includes(':') ? 6 : 4 }] : { address, family: address.includes(':') ? 6 : 4 }
918
- },
919
- })
920
- const __harnessDns = Object.freeze({
921
- lookup(hostname, options, callback) {
922
- const resolvedCallback = typeof options === 'function' ? options : callback
923
- const address = hostname === 'localhost' ? '127.0.0.1' : hostname === '[::1]' ? '::1' : hostname === 'metadata.internal' ? '169.254.169.254' : /^\\d+(?:\\.\\d+){3}$/.test(hostname) ? hostname : __harnessPublicAddress
924
- if (typeof options === 'object' && options?.all) resolvedCallback(null, [{ address, family: address.includes(':') ? 6 : 4 }])
925
- else resolvedCallback(null, address, address.includes(':') ? 6 : 4)
926
- },
927
- })
928
- const __harnessNet = Object.freeze({
929
- isIP(value) {
930
- if (/^\\d+(?:\\.\\d+){3}$/.test(value)) return 4
931
- if (String(value).includes(':')) return 6
932
- return 0
933
- },
934
- })
935
- const __harnessImportStub = new Proxy(function (...args) {
936
- const callback = args.find((argument) => typeof argument === 'function')
937
- return callback ? callback() : __harnessImportStub
938
- }, {
939
- get(_target, property) { return property === 'then' ? undefined : __harnessImportStub },
940
- construct() { return __harnessImportStub },
941
- })
942
- `
943
- output = output
944
- .replace(/\brequire\((['"])node:dns\/promises\1\)/g, '__harnessDnsPromises')
945
- .replace(/\brequire\((['"])node:dns\1\)/g, '__harnessDns')
946
- .replace(/\brequire\((['"])node:net\1\)/g, '__harnessNet')
947
- output = output.replace(/\brequire\((['"])[^'"]+\1\)/g, '__harnessImportStub')
948
- } else if (caseRecord.allowedCompiledImport) {
949
- const escaped = caseRecord.allowedCompiledImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
950
- const importExpression = new RegExp(`require\\(["']${escaped}["']\\)`, 'g')
951
- output = output.replace(importExpression, '({ CrudForm: function CrudForm() {} })')
952
- }
953
- if (/\brequire\s*\(/.test(output)) throw new Error('behavior-oracle target must not execute imports')
954
- return `${importStub}${output}`
955
- }
956
-
957
- function executeWorker(caseId, compiledSource) {
958
- const child = spawnSync(process.execPath, [
959
- '--permission', path.resolve(process.argv[1]), '--internal-case', caseId,
960
- ], {
961
- input: compiledSource,
962
- encoding: 'utf8',
963
- timeout: WORKER_TIMEOUT_MS,
964
- maxBuffer: 512 * 1024,
965
- env: { NO_COLOR: '1' },
966
- })
967
- if (child.error?.code === 'ETIMEDOUT' || child.signal) throw new Error('behavior probe timed out')
968
- if (child.error) throw child.error
969
- if (child.status !== 0) throw new Error(child.stderr.trim() || 'behavior probe worker failed')
970
- let parsed
971
- try { parsed = JSON.parse(child.stdout) } catch { throw new Error('behavior probe worker returned invalid JSON') }
972
- if (!Array.isArray(parsed?.checks) || parsed.checks.some((check) => typeof check?.id !== 'string' || typeof check?.passed !== 'boolean')) {
973
- throw new Error('behavior probe worker returned an invalid result')
974
- }
975
- return parsed
976
- }
977
-
978
- async function internalRun(caseId) {
979
- const caseRecord = CASES[caseId]
980
- const probeScript = PROBES[caseRecord?.probeCase ?? caseId] ?? FAMILY_PROBES[caseRecord?.family]
981
- if (!probeScript) throw new Error(`unsupported behavior-oracle case: ${caseId}`)
982
- const chunks = []
983
- let size = 0
984
- for await (const chunk of process.stdin) {
985
- size += chunk.length
986
- if (size > MAX_SOURCE_BYTES * 4) throw new Error('compiled target exceeds worker input limit')
987
- chunks.push(chunk)
988
- }
989
- const compiledSource = Buffer.concat(chunks).toString('utf8')
990
- const context = vm.createContext(Object.create(null), {
991
- name: `writable-behavior-oracle-${caseId}`,
992
- codeGeneration: { strings: false, wasm: false },
993
- })
994
- new vm.Script(`
995
- 'use strict';
996
- class OracleURLSearchParams {
997
- #entries = []
998
- constructor(query = '') {
999
- for (const pair of String(query).replace(/^\\?/, '').split('&')) {
1000
- if (!pair) continue
1001
- const separator = pair.indexOf('=')
1002
- const key = separator < 0 ? pair : pair.slice(0, separator)
1003
- const value = separator < 0 ? '' : pair.slice(separator + 1)
1004
- this.#entries.push([decodeURIComponent(key), decodeURIComponent(value)])
1005
- }
1006
- }
1007
- get(key) { return this.#entries.find(([name]) => name === String(key))?.[1] ?? null }
1008
- set(key, value) {
1009
- const name = String(key)
1010
- this.#entries = this.#entries.filter(([candidate]) => candidate !== name)
1011
- this.#entries.push([name, String(value)])
1012
- }
1013
- toString() { return this.#entries.map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value)).join('&') }
1014
- }
1015
- class OracleURL {
1016
- constructor(input) {
1017
- const match = /^(https?):\\/\\/([^/?#]+)([^?#]*)(?:\\?([^#]*))?(?:#(.*))?$/.exec(String(input))
1018
- if (!match) throw new TypeError('invalid absolute URL')
1019
- this.protocol = match[1] + ':'
1020
- const authority = match[2]
1021
- const bracketEnd = authority.startsWith('[') ? authority.indexOf(']') : -1
1022
- const portIndex = bracketEnd > -1
1023
- ? (authority[bracketEnd + 1] === ':' ? bracketEnd + 1 : -1)
1024
- : authority.lastIndexOf(':')
1025
- this.hostname = bracketEnd > -1 ? authority.slice(0, bracketEnd + 1) : portIndex > -1 ? authority.slice(0, portIndex) : authority
1026
- this.port = portIndex > -1 ? authority.slice(portIndex + 1) : ''
1027
- this.pathname = match[3] || '/'
1028
- this.hash = match[5] ? '#' + match[5] : ''
1029
- this.searchParams = new OracleURLSearchParams(match[4] || '')
1030
- }
1031
- toString() {
1032
- const query = this.searchParams.toString()
1033
- return this.protocol + '//' + this.hostname + (this.port ? ':' + this.port : '') + this.pathname + (query ? '?' + query : '') + this.hash
1034
- }
1035
- get href() { return this.toString() }
1036
- }
1037
- globalThis.URL = OracleURL
1038
- globalThis.URLSearchParams = OracleURLSearchParams
1039
- globalThis.module = { exports: Object.create(null) }
1040
- globalThis.exports = globalThis.module.exports
1041
- `).runInContext(context, { timeout: 250 })
1042
- new vm.Script(`'use strict';\n${compiledSource}`, { filename: caseRecord.file }).runInContext(context, { timeout: 1_000 })
1043
- Object.defineProperties(context, {
1044
- __oracleExportName: { value: caseRecord.exportName ?? null },
1045
- __oracleMode: { value: caseRecord.mode ?? null },
1046
- __oracleRequiredFlagsJson: { value: JSON.stringify(caseRecord.requiredFlags ?? []) },
1047
- })
1048
- ORACLE_CONFIG_SCRIPT.runInContext(context, { timeout: 250 })
1049
- probeScript.runInContext(context, { timeout: 1_000 })
1050
- const serialized = await new vm.Script(`(async () => JSON.stringify(await globalThis.__oracleResult))()`).runInContext(context, { timeout: 1_000 })
1051
- process.stdout.write(`${serialized}\n`)
1052
- }
1053
-
1054
- function emit(result, json) {
1055
- if (json) process.stdout.write(`${JSON.stringify(result)}\n`)
1056
- else {
1057
- const marker = result.passed ? 'PASS' : 'FAIL'
1058
- process.stdout.write(`${marker}: ${result.failures.join('; ') || 'all behavior checks passed'}\n`)
1059
- }
1060
- }
1061
-
1062
- async function main() {
1063
- let options
1064
- try { options = parseArgs(process.argv.slice(2)) } catch (error) {
1065
- console.error(error.message)
1066
- console.error(usage())
1067
- return EXIT_INVALID
1068
- }
1069
- if (options.internalCase) {
1070
- try { await internalRun(options.internalCase); return EXIT_PASS } catch (error) {
1071
- console.error(safeMessage(error))
1072
- return EXIT_INVALID
1073
- }
1074
- }
1075
- if (options.help) { console.log(usage()); return EXIT_PASS }
1076
- const root = options.root ? path.resolve(options.root) : undefined
1077
- try {
1078
- if (!options.root || !path.isAbsolute(options.root)) throw new Error('--root must be an absolute path')
1079
- if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) throw new Error('--root must identify an existing directory')
1080
- if (!CASES[options.caseId]) throw new Error(`--case must be one of: ${Object.keys(CASES).join(', ')}`)
1081
- if (!['before', 'after'].includes(options.phase)) throw new Error('--phase must be before or after')
1082
- const caseRecord = CASES[options.caseId]
1083
- const target = readTargetSource(root, caseRecord)
1084
- const compiled = transpileTarget(root, { ...caseRecord, file: target.file }, target.source)
1085
- const probe = executeWorker(options.caseId, compiled)
1086
- const failures = probe.checks.filter((check) => !check.passed).map((check) => `${check.id}: behavior requirement was not met`)
1087
- const result = { passed: failures.length === 0, failures, checks: probe.checks }
1088
- emit(result, options.json)
1089
- return result.passed ? EXIT_PASS : EXIT_FAILURE
1090
- } catch (error) {
1091
- emit({ passed: false, failures: [safeMessage(error, root)], checks: [] }, options.json)
1092
- return EXIT_INVALID
1093
- }
1094
- }
1095
-
1096
- process.exitCode = await main()