@open-mercato/cli 0.6.7 → 0.6.8-canary.320.1.bf889f49e4

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 (335) hide show
  1. package/.turbo/turbo-build.log +77 -4
  2. package/AGENTS.md +6 -1
  3. package/build.mjs +68 -11
  4. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  5. package/dist/agentic/codex/enforcement-rules.md +2 -16
  6. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  7. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  8. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  9. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  10. package/dist/agentic/guides/ai-workflows.md +76 -0
  11. package/dist/agentic/guides/architecture.md +107 -0
  12. package/dist/agentic/guides/backend-ui.md +86 -0
  13. package/dist/agentic/guides/contracts.md +92 -0
  14. package/dist/agentic/guides/extensions.md +121 -0
  15. package/dist/agentic/guides/framework-extension-points.md +36 -0
  16. package/dist/agentic/guides/integrations.md +81 -0
  17. package/dist/agentic/guides/module-facts.json +116757 -3951
  18. package/dist/agentic/guides/module-system.md +12 -127
  19. package/dist/agentic/guides/modules/ai_assistant.md +227 -28
  20. package/dist/agentic/guides/modules/api_docs.md +58 -2
  21. package/dist/agentic/guides/modules/api_keys.md +92 -7
  22. package/dist/agentic/guides/modules/attachments.md +119 -14
  23. package/dist/agentic/guides/modules/audit_logs.md +131 -10
  24. package/dist/agentic/guides/modules/auth.md +276 -33
  25. package/dist/agentic/guides/modules/business_rules.md +149 -14
  26. package/dist/agentic/guides/modules/catalog.md +482 -43
  27. package/dist/agentic/guides/modules/channel_gmail.md +82 -3
  28. package/dist/agentic/guides/modules/channel_imap.md +82 -3
  29. package/dist/agentic/guides/modules/checkout.md +305 -27
  30. package/dist/agentic/guides/modules/communication_channels.md +315 -33
  31. package/dist/agentic/guides/modules/configs.md +104 -8
  32. package/dist/agentic/guides/modules/content.md +57 -2
  33. package/dist/agentic/guides/modules/currencies.md +152 -17
  34. package/dist/agentic/guides/modules/customer_accounts.md +387 -45
  35. package/dist/agentic/guides/modules/customers.md +746 -85
  36. package/dist/agentic/guides/modules/dashboards.md +155 -10
  37. package/dist/agentic/guides/modules/data_sync.md +149 -16
  38. package/dist/agentic/guides/modules/design_system.md +102 -0
  39. package/dist/agentic/guides/modules/dictionaries.md +107 -12
  40. package/dist/agentic/guides/modules/directory.md +148 -15
  41. package/dist/agentic/guides/modules/entities.md +145 -13
  42. package/dist/agentic/guides/modules/events.md +59 -2
  43. package/dist/agentic/guides/modules/feature_toggles.md +119 -9
  44. package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
  45. package/dist/agentic/guides/modules/generators.md +45 -2
  46. package/dist/agentic/guides/modules/inbox_ops.md +207 -27
  47. package/dist/agentic/guides/modules/integrations.md +149 -16
  48. package/dist/agentic/guides/modules/messages.md +223 -24
  49. package/dist/agentic/guides/modules/notifications.md +96 -8
  50. package/dist/agentic/guides/modules/onboarding.md +77 -6
  51. package/dist/agentic/guides/modules/payment_gateways.md +168 -21
  52. package/dist/agentic/guides/modules/perspectives.md +77 -7
  53. package/dist/agentic/guides/modules/planner.md +136 -18
  54. package/dist/agentic/guides/modules/portal.md +93 -2
  55. package/dist/agentic/guides/modules/progress.md +104 -14
  56. package/dist/agentic/guides/modules/query_index.md +154 -15
  57. package/dist/agentic/guides/modules/record_locks.md +206 -21
  58. package/dist/agentic/guides/modules/resources.md +197 -30
  59. package/dist/agentic/guides/modules/sales.md +687 -84
  60. package/dist/agentic/guides/modules/scheduler.md +133 -14
  61. package/dist/agentic/guides/modules/search.md +170 -6
  62. package/dist/agentic/guides/modules/security.md +294 -35
  63. package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
  64. package/dist/agentic/guides/modules/sso.md +151 -26
  65. package/dist/agentic/guides/modules/staff.md +372 -57
  66. package/dist/agentic/guides/modules/storage_s3.md +95 -5
  67. package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
  68. package/dist/agentic/guides/modules/sync_excel.md +75 -6
  69. package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
  70. package/dist/agentic/guides/modules/translations.md +104 -10
  71. package/dist/agentic/guides/modules/webhooks.md +203 -24
  72. package/dist/agentic/guides/modules/wms.md +469 -47
  73. package/dist/agentic/guides/modules/workflows.md +302 -43
  74. package/dist/agentic/guides/reference-module-facts.json +6942 -0
  75. package/dist/agentic/guides/reference-modules/example.md +462 -0
  76. package/dist/agentic/guides/spec-delivery.md +56 -0
  77. package/dist/agentic/guides/testing-debugging.md +80 -0
  78. package/dist/agentic/guides/upstream/AGENTS.md +300 -0
  79. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +334 -0
  80. package/dist/agentic/guides/upstream/manifest.json +8 -0
  81. package/dist/agentic/shared/AGENTS.md.template +120 -311
  82. package/dist/agentic/shared/ai/agentic.config.json +2 -0
  83. package/dist/agentic/shared/ai/harness/README.md +50 -0
  84. package/dist/agentic/shared/ai/harness/RELEASE.md +59 -0
  85. package/dist/agentic/shared/ai/harness/cases.json +20596 -0
  86. package/dist/agentic/shared/ai/harness/cases.schema.json +196 -0
  87. package/dist/agentic/shared/ai/harness/design-system-inventory.json +6326 -0
  88. package/dist/agentic/shared/ai/harness/fixtures/index.json +51 -0
  89. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +166 -0
  90. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
  91. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
  92. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
  93. package/dist/agentic/shared/ai/harness/knowledge-change.schema.json +228 -0
  94. package/dist/agentic/shared/ai/harness/release-matrix.json +163 -0
  95. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  96. package/dist/agentic/shared/ai/harness/result.schema.json +136 -0
  97. package/dist/agentic/shared/ai/harness/routing-response.schema.json +22 -0
  98. package/dist/agentic/shared/ai/harness/source-link-inventory.json +1706 -0
  99. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  100. package/dist/agentic/shared/ai/harness/validators.json +82 -0
  101. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
  102. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  103. package/dist/agentic/shared/ai/harness/writable-spec-oracles.mjs +602 -0
  104. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  105. package/dist/agentic/shared/ai/lessons.md +26 -3
  106. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  107. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
  108. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  109. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
  110. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +21 -241
  111. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +31 -0
  112. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  113. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +15 -0
  114. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  115. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  116. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  117. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  118. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  119. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  120. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  121. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  122. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  123. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  124. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +17 -601
  125. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +21 -0
  126. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +15 -0
  127. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +16 -0
  128. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  129. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +15 -310
  130. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +19 -0
  131. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +31 -0
  132. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
  133. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  134. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/knowledge-change.md +133 -0
  135. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  136. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  137. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  138. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  139. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  140. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
  141. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  142. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
  143. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
  144. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
  145. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
  146. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
  147. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +25 -708
  148. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  149. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +25 -0
  150. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  151. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  152. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
  153. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
  154. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
  155. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
  156. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
  157. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
  158. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
  159. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  160. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +32 -0
  161. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
  162. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +14 -0
  163. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  164. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +40 -0
  165. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  166. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  167. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  168. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
  169. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
  170. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
  171. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
  172. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
  173. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
  174. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
  175. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
  176. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +20 -843
  177. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +42 -0
  178. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
  179. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  180. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +43 -0
  181. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  182. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  183. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  184. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +17 -467
  185. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +29 -0
  186. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  187. package/dist/agentic/shared/ai/skills/tiers.json +143 -21
  188. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  189. package/dist/agentic/shared/ai/specs/2026-08-06-reference-module-activation.md +248 -0
  190. package/dist/agentic/shared/ai/specs/README.md +4 -3
  191. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
  192. package/dist/agentic/shared/ai/trackers/github.md +108 -4
  193. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +224 -0
  194. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  195. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +3812 -0
  196. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  197. package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
  198. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  199. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  200. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  201. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
  202. package/dist/agentic/shared/scripts/validate-knowledge-change.mjs +1172 -0
  203. package/dist/bin.js +32 -34
  204. package/dist/bin.js.map +2 -2
  205. package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
  206. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  207. package/dist/lib/agentic-init.js +13 -3
  208. package/dist/lib/agentic-init.js.map +2 -2
  209. package/dist/lib/agentic-setup.js +434 -186
  210. package/dist/lib/agentic-setup.js.map +2 -2
  211. package/dist/lib/cli-bootstrap-mode.js +32 -0
  212. package/dist/lib/cli-bootstrap-mode.js.map +7 -0
  213. package/dist/lib/dev-supervisor-manifest.js +105 -0
  214. package/dist/lib/dev-supervisor-manifest.js.map +7 -0
  215. package/dist/lib/generate-watch-events.js +15 -0
  216. package/dist/lib/generate-watch-events.js.map +2 -2
  217. package/dist/lib/generators/entity-ids.js +11 -24
  218. package/dist/lib/generators/entity-ids.js.map +2 -2
  219. package/dist/lib/generators/extensions/injection-widgets.js +20 -0
  220. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  221. package/dist/lib/generators/module-di.js +10 -23
  222. package/dist/lib/generators/module-di.js.map +2 -2
  223. package/dist/lib/generators/module-entities.js +10 -23
  224. package/dist/lib/generators/module-entities.js.map +2 -2
  225. package/dist/lib/generators/module-extension-facts.js +1882 -0
  226. package/dist/lib/generators/module-extension-facts.js.map +7 -0
  227. package/dist/lib/generators/module-fact-sources.js +70 -0
  228. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  229. package/dist/lib/generators/module-facts-discovery.js +64 -7
  230. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  231. package/dist/lib/generators/module-facts.js +1913 -52
  232. package/dist/lib/generators/module-facts.js.map +3 -3
  233. package/dist/lib/generators/module-override-targets.js +712 -0
  234. package/dist/lib/generators/module-override-targets.js.map +7 -0
  235. package/dist/lib/generators/module-registry.js +405 -36
  236. package/dist/lib/generators/module-registry.js.map +2 -2
  237. package/dist/lib/generators/route-manifest-shards.js +170 -0
  238. package/dist/lib/generators/route-manifest-shards.js.map +7 -0
  239. package/dist/lib/in-process-generate-watcher.js +12 -3
  240. package/dist/lib/in-process-generate-watcher.js.map +2 -2
  241. package/dist/lib/load-env.js +24 -0
  242. package/dist/lib/load-env.js.map +7 -0
  243. package/dist/lib/queue-worker-supervisor.js.map +2 -2
  244. package/dist/lib/resolver.js +28 -3
  245. package/dist/lib/resolver.js.map +2 -2
  246. package/dist/lib/telemetry-init.js +358 -0
  247. package/dist/lib/telemetry-init.js.map +7 -0
  248. package/dist/lib/testing/integration.js +5 -2
  249. package/dist/lib/testing/integration.js.map +2 -2
  250. package/dist/lib/utils.js +15 -8
  251. package/dist/lib/utils.js.map +2 -2
  252. package/dist/mercato.js +35 -9
  253. package/dist/mercato.js.map +2 -2
  254. package/package.json +13 -11
  255. package/src/__tests__/mercato.test.ts +144 -0
  256. package/src/bin.ts +60 -42
  257. package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
  258. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  259. package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
  260. package/src/lib/__tests__/cli-bootstrap-mode.test.ts +22 -0
  261. package/src/lib/__tests__/content-stable-writes.test.ts +74 -0
  262. package/src/lib/__tests__/dev-supervisor-manifest.test.ts +131 -0
  263. package/src/lib/__tests__/generate-watch-events.test.ts +76 -0
  264. package/src/lib/__tests__/in-process-generate-watcher.test.ts +52 -1
  265. package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
  266. package/src/lib/__tests__/load-env.test.ts +65 -0
  267. package/src/lib/__tests__/queue-worker-supervisor.test.ts +33 -0
  268. package/src/lib/__tests__/resolver.enterprise.test.ts +50 -0
  269. package/src/lib/__tests__/telemetry-init.test.ts +298 -0
  270. package/src/lib/agentic-init.ts +15 -3
  271. package/src/lib/agentic-setup.ts +573 -222
  272. package/src/lib/cli-bootstrap-mode.ts +31 -0
  273. package/src/lib/dev-supervisor-manifest.ts +142 -0
  274. package/src/lib/generate-watch-events.ts +14 -0
  275. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  276. package/src/lib/generators/__tests__/entity-ids.test.ts +32 -0
  277. package/src/lib/generators/__tests__/helpers/override-target-diagnostic-fixtures.ts +150 -0
  278. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  279. package/src/lib/generators/__tests__/module-extension-facts.test.ts +603 -0
  280. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +139 -2
  281. package/src/lib/generators/__tests__/module-facts.computed-di-token.test.ts +88 -0
  282. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  283. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  284. package/src/lib/generators/__tests__/module-facts.example-fact-coverage.test.ts +414 -0
  285. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  286. package/src/lib/generators/__tests__/module-facts.local-reference.test.ts +393 -0
  287. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  288. package/src/lib/generators/__tests__/module-facts.search-entity-id.test.ts +151 -0
  289. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +160 -0
  290. package/src/lib/generators/__tests__/module-override-targets.diagnostic-wiring.test.ts +92 -0
  291. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  292. package/src/lib/generators/__tests__/module-override-targets.test.ts +328 -0
  293. package/src/lib/generators/__tests__/module-override-targets.unknown-mode.test.ts +117 -0
  294. package/src/lib/generators/__tests__/module-subset.test.ts +266 -0
  295. package/src/lib/generators/__tests__/output-snapshots.test.ts +3 -3
  296. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +62 -0
  297. package/src/lib/generators/entity-ids.ts +10 -26
  298. package/src/lib/generators/extensions/injection-widgets.ts +51 -0
  299. package/src/lib/generators/module-di.ts +9 -25
  300. package/src/lib/generators/module-entities.ts +9 -25
  301. package/src/lib/generators/module-extension-facts.ts +2472 -0
  302. package/src/lib/generators/module-fact-sources.ts +108 -0
  303. package/src/lib/generators/module-facts-discovery.ts +104 -7
  304. package/src/lib/generators/module-facts.ts +2747 -138
  305. package/src/lib/generators/module-override-targets.ts +907 -0
  306. package/src/lib/generators/module-registry.ts +448 -35
  307. package/src/lib/generators/route-manifest-shards.ts +146 -0
  308. package/src/lib/in-process-generate-watcher.ts +13 -4
  309. package/src/lib/load-env.ts +30 -0
  310. package/src/lib/queue-worker-supervisor.ts +7 -2
  311. package/src/lib/resolver.ts +34 -3
  312. package/src/lib/telemetry-init.ts +461 -0
  313. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  314. package/src/lib/testing/integration.ts +5 -2
  315. package/src/lib/utils.ts +24 -16
  316. package/src/mercato.ts +46 -9
  317. package/dist/agentic/guides/cache.md +0 -45
  318. package/dist/agentic/guides/core.md +0 -453
  319. package/dist/agentic/guides/events.md +0 -117
  320. package/dist/agentic/guides/queue.md +0 -53
  321. package/dist/agentic/guides/search.md +0 -115
  322. package/dist/agentic/guides/shared.md +0 -108
  323. package/dist/agentic/guides/ui.md +0 -347
  324. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  325. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  326. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  327. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  328. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  329. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  330. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  331. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  332. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  333. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  334. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  335. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
@@ -0,0 +1,602 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Fixed, controller-owned grader for the two SPEC-P2 writable planning proofs. The other two
4
+ // writable runners grade TypeScript: `writable-ast-oracles.mjs` reads a target's own compiler
5
+ // facts, `writable-behavior-oracles.mjs` executes a compiled seam. A planning proof produces
6
+ // no TypeScript at all — its only permitted artifact is Markdown under `.ai/specs/` — so it
7
+ // gets its own fixed runner rather than a Markdown branch bolted onto the AST oracle. The
8
+ // case table below is hard-coded exactly like the other two runners: a case cannot bring its
9
+ // own grading rules, and the evaluator resolves this file only for the two validator IDs it
10
+ // owns.
11
+
12
+ import fs from 'node:fs'
13
+ import path from 'node:path'
14
+ import process from 'node:process'
15
+ import { fileURLToPath } from 'node:url'
16
+
17
+ const EXIT_PASS = 0
18
+ const EXIT_FAILURE = 1
19
+ const EXIT_INVALID = 2
20
+
21
+ const SPEC_ROOT = '.ai/specs'
22
+ // Everything the scaffold itself emits under `.ai/specs`. None of these is an authored
23
+ // deliverable, so a planning proof neither counts them nor may repurpose them: the folder
24
+ // README, the blank skeleton, and the shipped covering specification for reference-module
25
+ // activation that gives the `reuse-spec` routing row an honest target.
26
+ const RESERVED_SPEC_FILES = Object.freeze([
27
+ 'README.md',
28
+ 'SPEC-000-template.md',
29
+ '2026-08-06-reference-module-activation.md',
30
+ ])
31
+ const SPEC_FILE_PATTERN = /^\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*\.md$/
32
+ const MAX_SPEC_BYTES = 256 * 1024
33
+ const MODULE_ROOT = 'src/modules'
34
+ const MODULE_REGISTRY = 'src/modules.ts'
35
+ const PLACEHOLDER_TOKEN_PATTERN = /(?:^|[^A-Za-z])(?:TBD|TODO|FIXME|XXX|lorem ipsum)(?:[^A-Za-z]|$)/i
36
+ // Matches an unfilled `{…}` skeleton slot such as `{module}`, `{YYYY-MM-DD}`, or
37
+ // `{why it fits the platform}`, while leaving inline object prose like `{ items, totalCount }`
38
+ // alone: a real slot carries no comma and no padding space.
39
+ const CURLY_PLACEHOLDER_PATTERN = /\{(?!\s)[^{}\n,]{1,80}(?<!\s)\}/
40
+ const PHASE_HEADING_PATTERN = /^#{3,4}\s+Phase\s+(\d+)\b/i
41
+ const TEST_IDENTIFIER_PATTERN = /\bTEST-\d{3}\b|[\w./-]+\.(?:test|spec)\.tsx?\b|__integration__/
42
+
43
+ // --- module-shaped planning contract ------------------------------------------------------
44
+ // A module-shaped proof must ground its implementation in the reference module the app already
45
+ // ships rather than describing a second teaching module. These are the target-side facts the
46
+ // oracle reads to check that grounding; the plan under grading never supplies them.
47
+ const EXAMPLE_ROOT = 'src/modules/example'
48
+ const EXAMPLE_INVENTORY = `${EXAMPLE_ROOT}/references/surface-inventory.json`
49
+ const EXAMPLE_FILE_PATTERN = /\bsrc\/modules\/example\/[A-Za-z0-9._/-]+\.[A-Za-z0-9]+\b/g
50
+ // A directory-only or globbed hint is not a source link. `src/modules/example/api/**`,
51
+ // `src/modules/example/widgets/*.ts` and a bare `src/modules/example/commands/` all name a
52
+ // place to look rather than a file to read, which is exactly what the read policy forbids.
53
+ const EXAMPLE_GLOB_PATTERN = /\bsrc\/modules\/example\/[A-Za-z0-9._/-]*(?:\*|\/(?=[\s`)|,.]|$))/
54
+ const AFTER_OPT_IN_PATTERN = /(?:source[-\s]present|not registered|unregistered|disabled|inert)[\s\S]{0,120}?(?:opt[-\s]?in|until it is registered|registration)|(?:opt[-\s]?in|register(?:ed|ing)?)[\s\S]{0,120}?(?:source[-\s]present|not registered|unregistered|disabled|inert)/i
55
+ const SHADOW_MODULE_PATTERNS = Object.freeze([
56
+ { id: 'shadow-teaching-module', pattern: /\b(?:new|second|another|own)\s+(?:teaching|example|demo|reference|sample)\s+module\b/i },
57
+ { id: 'whole-example-copy', pattern: /\bcopy(?:ing)?\s+(?:the\s+)?(?:whole|entire|complete|full)\s+(?:example|reference)\s+(?:module|tree|directory)\b|\bcp\s+-r\s+src\/modules\/example\b/i },
58
+ { id: 'ratelimit-probe-blueprint', pattern: /\bratelimit_probe\b/i },
59
+ ])
60
+ // Fixed by the spec-first routing contract, which owns this vocabulary. `pending-emission` is
61
+ // deliberately absent: it is a lifecycle marker on the canonical ledger, not a statement about
62
+ // which mechanism a proposed surface uses.
63
+ const MECHANISM_CLASSIFICATIONS = Object.freeze([
64
+ 'emitted-example',
65
+ 'framework-only',
66
+ 'catalog-only',
67
+ 'currently-unbound',
68
+ 'negative-fixture',
69
+ ])
70
+ // Activated canonical output may never claim this one: only a deliberately broken fixture
71
+ // produces it, so a plan that proposes it is proposing a defect.
72
+ const FORBIDDEN_CLASSIFICATION = 'negative-fixture'
73
+ const TRACEABILITY_HEADING_PATTERN = /traceab/i
74
+ const REQUIREMENT_ID_PATTERN = /\bREQ-\d{3}\b/
75
+ const PHASE_REFERENCE_PATTERN = /\bPhase\s+\d+\b/i
76
+ // Reserved scaffolding must still look like scaffolding afterwards. Byte equality against the
77
+ // controller is not usable: `.ai/specs/README.md` carries the generated project name, which
78
+ // legitimately differs between the controller app and the disposable target.
79
+ const RESERVED_SPEC_MARKERS = Object.freeze({
80
+ 'README.md': /^#\s+Feature Specs/m,
81
+ 'SPEC-000-template.md': /\{Title\}/,
82
+ '2026-08-06-reference-module-activation.md': /^#\s+Reference Module Activation/m,
83
+ })
84
+
85
+ // Each requirement resolves to one distinct level-2 section, preferring the exact heading the
86
+ // scaffold's own skeleton ships (`.ai/specs/SPEC-000-template.md`) before looser synonyms, so a
87
+ // plan written from the routed template satisfies it without renaming anything.
88
+ const REQUIRED_SECTIONS = Object.freeze([
89
+ {
90
+ id: 'problem',
91
+ requirement: 'a problem statement',
92
+ patterns: [/^problem statement$/i, /problem/i, /background/i],
93
+ minWords: 80,
94
+ },
95
+ {
96
+ id: 'contracts',
97
+ requirement: 'a contract section',
98
+ patterns: [/api[,\s].*contract/i, /^data models?$/i, /contract/i, /\bapi\b/i],
99
+ minWords: 80,
100
+ },
101
+ {
102
+ id: 'tests',
103
+ requirement: 'a test coverage section',
104
+ patterns: [/integration coverage/i, /^tests?\b/i, /test/i, /verification/i],
105
+ minWords: 40,
106
+ },
107
+ {
108
+ id: 'phases',
109
+ requirement: 'a phased implementation section',
110
+ patterns: [/implementation phases?/i, /implementation plan/i, /phase/i, /implementation/i],
111
+ minWords: 80,
112
+ },
113
+ ])
114
+
115
+ const SPEC_CASES = Object.freeze({
116
+ 'OMH-223': {
117
+ kind: 'new-feature',
118
+ // The ordering proof is that planning delivered one covering spec and no implementation.
119
+ // These are the module-directory names a warehouse-transfer implementation would occupy.
120
+ implementationKeywords: ['transfer', 'warehouse'],
121
+ // Stock transfers are a module, so this proof is also the module-shaped one: its plan has
122
+ // to route implementation to the scaffold skill and name the exact reference sources it
123
+ // will adapt, rather than describing a second teaching module in prose.
124
+ moduleShaped: {
125
+ scaffoldSkill: 'om-module-scaffold',
126
+ minCapabilityIds: 2,
127
+ minExactSourcePaths: 2,
128
+ minTraceabilityRows: 2,
129
+ },
130
+ },
131
+ 'OMH-224': {
132
+ kind: 'existing-spec',
133
+ coveringSpec: `${SPEC_ROOT}/2026-08-04-service-appointment-reminders.md`,
134
+ // Fixed expectations for the contract change the prompt introduces. Hard-coded here, never
135
+ // derived from the seeded file or from the answer under grading.
136
+ amendments: [
137
+ {
138
+ id: 'rescheduled-and-cancelled',
139
+ requirement: 'the amended contract must cover rescheduled and cancelled appointments',
140
+ all: [/reschedul/i, /cancel/i],
141
+ },
142
+ {
143
+ id: 'per-channel-outcome',
144
+ requirement: 'the amended contract must record a per-channel delivery outcome including SMS',
145
+ all: [/\bsms\b/i, /per[-\s]channel|delivery (?:outcome|status|result)/i],
146
+ },
147
+ ],
148
+ // The amendment must not silently drop the behavior the covering spec already committed to.
149
+ preserved: [
150
+ {
151
+ id: 'confirmed-reminder',
152
+ requirement: 'the original confirmed-appointment reminder contract must survive the amendment',
153
+ pattern: /confirmed appointment/i,
154
+ },
155
+ ],
156
+ minChangelogEntries: 2,
157
+ implementationKeywords: ['reminder', 'appointment'],
158
+ },
159
+ })
160
+
161
+ function check(id, passed, requirement) {
162
+ return { id, passed, requirement }
163
+ }
164
+
165
+ function isSafeRelative(relative) {
166
+ if (typeof relative !== 'string' || !relative || path.isAbsolute(relative) || relative.includes('\0')) return false
167
+ return !relative.replaceAll('\\', '/').split('/').includes('..')
168
+ }
169
+
170
+ function readContainedFile(root, relative) {
171
+ if (!isSafeRelative(relative)) return null
172
+ const lexical = path.resolve(root, relative)
173
+ if (path.relative(root, lexical).startsWith('..')) return null
174
+ let stat
175
+ try { stat = fs.lstatSync(lexical) } catch { return null }
176
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_SPEC_BYTES) return null
177
+ try { return fs.readFileSync(lexical, 'utf8') } catch { return null }
178
+ }
179
+
180
+ function listSpecFiles(root) {
181
+ const directory = path.join(root, ...SPEC_ROOT.split('/'))
182
+ let entries
183
+ try { entries = fs.readdirSync(directory, { withFileTypes: true }) } catch { return [] }
184
+ return entries
185
+ .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.md'))
186
+ .map((entry) => entry.name)
187
+ .sort()
188
+ }
189
+
190
+ function authoredSpecFiles(root) {
191
+ return listSpecFiles(root).filter((name) => !RESERVED_SPEC_FILES.includes(name))
192
+ }
193
+
194
+ // Splits a Markdown document into its level-2 sections, ignoring headings inside fenced code
195
+ // blocks so an ASCII wireframe cannot fabricate a section.
196
+ function level2Sections(markdown) {
197
+ const sections = []
198
+ let fenced = false
199
+ let current = null
200
+ for (const line of markdown.split(/\r?\n/)) {
201
+ if (/^\s*(?:```|~~~)/.test(line)) fenced = !fenced
202
+ if (!fenced) {
203
+ const heading = /^##\s+(.+?)\s*$/.exec(line)
204
+ if (heading) {
205
+ current = { heading: heading[1], lines: [] }
206
+ sections.push(current)
207
+ continue
208
+ }
209
+ }
210
+ if (current) current.lines.push(line)
211
+ }
212
+ return sections.map((section) => ({ heading: section.heading, body: section.lines.join('\n') }))
213
+ }
214
+
215
+ function strippedOfCodeBlocks(body) {
216
+ const kept = []
217
+ let fenced = false
218
+ for (const line of body.split(/\r?\n/)) {
219
+ if (/^\s*(?:```|~~~)/.test(line)) { fenced = !fenced; continue }
220
+ if (!fenced) kept.push(line)
221
+ }
222
+ return kept.join('\n')
223
+ }
224
+
225
+ function wordCount(body) {
226
+ return strippedOfCodeBlocks(body).split(/\s+/).filter((token) => /[A-Za-z0-9]/.test(token)).length
227
+ }
228
+
229
+ function placeholderReason(body) {
230
+ const prose = strippedOfCodeBlocks(body)
231
+ if (PLACEHOLDER_TOKEN_PATTERN.test(prose)) return 'an unresolved TODO/TBD marker'
232
+ if (CURLY_PLACEHOLDER_PATTERN.test(prose)) return 'an unfilled {…} skeleton slot'
233
+ return null
234
+ }
235
+
236
+ function orderedPhaseNumbers(body) {
237
+ const numbers = []
238
+ let fenced = false
239
+ for (const line of body.split(/\r?\n/)) {
240
+ if (/^\s*(?:```|~~~)/.test(line)) { fenced = !fenced; continue }
241
+ if (fenced) continue
242
+ const match = PHASE_HEADING_PATTERN.exec(line)
243
+ if (match) numbers.push(Number(match[1]))
244
+ }
245
+ return numbers
246
+ }
247
+
248
+ function changelogEntryCount(markdown) {
249
+ const section = level2Sections(markdown).find((entry) => /changelog/i.test(entry.heading))
250
+ if (!section) return 0
251
+ const lines = strippedOfCodeBlocks(section.body).split(/\r?\n/)
252
+ const rows = lines.filter((line) => /^\s*\|/.test(line)
253
+ && !/^\s*\|[\s:|-]*\|\s*$/.test(line)
254
+ && !/^\s*\|\s*date\s*\|/i.test(line))
255
+ const bullets = lines.filter((line) => /^\s*[-*]\s+\S/.test(line))
256
+ return rows.length + bullets.length
257
+ }
258
+
259
+ function resolveRequiredSections(markdown) {
260
+ const sections = level2Sections(markdown)
261
+ const claimed = new Set()
262
+ const resolved = new Map()
263
+ for (const requirement of REQUIRED_SECTIONS) {
264
+ let match
265
+ for (const pattern of requirement.patterns) {
266
+ match = sections.find((section) => !claimed.has(section) && pattern.test(section.heading))
267
+ if (match) break
268
+ }
269
+ if (match) claimed.add(match)
270
+ resolved.set(requirement.id, match ?? null)
271
+ }
272
+ return resolved
273
+ }
274
+
275
+ function specStructureChecks(markdown) {
276
+ const checks = []
277
+ const resolved = resolveRequiredSections(markdown)
278
+ for (const requirement of REQUIRED_SECTIONS) {
279
+ const match = resolved.get(requirement.id)
280
+ if (!match) {
281
+ checks.push(check(`spec.section.${requirement.id}`, false, `the specification needs ${requirement.requirement}`))
282
+ continue
283
+ }
284
+ const words = wordCount(match.body)
285
+ checks.push(check(
286
+ `spec.section.${requirement.id}`,
287
+ words >= requirement.minWords,
288
+ `${requirement.requirement} needs at least ${requirement.minWords} words of substance (found ${words} under "${match.heading}")`,
289
+ ))
290
+ const placeholder = placeholderReason(match.body)
291
+ checks.push(check(
292
+ `spec.substantive.${requirement.id}`,
293
+ placeholder === null,
294
+ `${requirement.requirement} must be written, not templated${placeholder ? ` (found ${placeholder})` : ''}`,
295
+ ))
296
+ if (requirement.id === 'phases') {
297
+ const phases = orderedPhaseNumbers(match.body)
298
+ checks.push(check(
299
+ 'spec.phases.ordered',
300
+ phases.length >= 2 && phases.every((value, index) => value === index + 1),
301
+ `the implementation section needs at least two dependency-ordered phases numbered from 1 (found ${phases.join(', ') || 'none'})`,
302
+ ))
303
+ }
304
+ if (requirement.id === 'tests') {
305
+ checks.push(check(
306
+ 'spec.tests.identified',
307
+ TEST_IDENTIFIER_PATTERN.test(strippedOfCodeBlocks(match.body)),
308
+ 'the test section must name concrete test IDs or test file paths',
309
+ ))
310
+ }
311
+ }
312
+ return checks
313
+ }
314
+
315
+ function reservedSpecChecks(root) {
316
+ return RESERVED_SPEC_FILES.map((name) => {
317
+ const contents = readContainedFile(root, `${SPEC_ROOT}/${name}`)
318
+ return check(
319
+ `reserved.${name}`,
320
+ contents !== null && RESERVED_SPEC_MARKERS[name].test(contents),
321
+ `${SPEC_ROOT}/${name} must remain the emitted scaffolding, not the delivered specification`,
322
+ )
323
+ })
324
+ }
325
+
326
+ function implementationChecks(root, definition) {
327
+ const keywords = definition.implementationKeywords
328
+ const moduleDirectory = path.join(root, ...MODULE_ROOT.split('/'))
329
+ let moduleNames = []
330
+ try {
331
+ moduleNames = fs.readdirSync(moduleDirectory, { withFileTypes: true })
332
+ .filter((entry) => entry.isDirectory())
333
+ .map((entry) => entry.name.toLowerCase())
334
+ } catch { moduleNames = [] }
335
+ const registry = (readContainedFile(root, MODULE_REGISTRY) ?? '').toLowerCase()
336
+ const implicated = keywords.filter((keyword) => moduleNames.some((name) => name.includes(keyword)) || registry.includes(keyword))
337
+ return [check(
338
+ 'planning.implementation-absent',
339
+ implicated.length === 0,
340
+ `planning must not start implementation: no module directory or registry entry may name ${keywords.join(' or ')}${implicated.length ? ` (found ${implicated.join(', ')})` : ''}`,
341
+ )]
342
+ }
343
+
344
+ // The capability IDs and exact source paths the target's own reference module publishes. Read
345
+ // from the target, never from the plan: a plan cannot widen its own grounding by asserting a
346
+ // capability the shipped inventory does not carry.
347
+ function exampleGrounding(root) {
348
+ const raw = readContainedFile(root, EXAMPLE_INVENTORY)
349
+ if (raw === null) return null
350
+ let parsed
351
+ try { parsed = JSON.parse(raw) } catch { return null }
352
+ const capabilities = Array.isArray(parsed?.capabilities) ? parsed.capabilities : []
353
+ if (!capabilities.length) return null
354
+ const capabilityIds = new Set()
355
+ const sourcePaths = new Set()
356
+ for (const capability of capabilities) {
357
+ if (typeof capability?.capabilityId === 'string') capabilityIds.add(capability.capabilityId)
358
+ for (const relative of capability?.sourcePaths ?? []) if (typeof relative === 'string') sourcePaths.add(relative)
359
+ }
360
+ return { capabilityIds, sourcePaths }
361
+ }
362
+
363
+ // Splits every Markdown table in a section into its data rows. Header detection follows the
364
+ // grammar rather than the wording: a header is the line immediately above a delimiter row, so a
365
+ // data row whose first cell reads `REQ-001` is never mistaken for one.
366
+ function tableRows(body) {
367
+ const lines = body.split(/\r?\n/).map((line) => line.trim())
368
+ const isRow = (line) => line.startsWith('|') && line.endsWith('|') && line.length > 2
369
+ const cellsOf = (line) => line.slice(1, -1).split('|').map((cell) => cell.trim())
370
+ const isDelimiter = (line) => isRow(line) && cellsOf(line).every((cell) => /^:?-{2,}:?$/.test(cell))
371
+ const rows = []
372
+ for (let index = 0; index < lines.length; index += 1) {
373
+ if (!isRow(lines[index]) || isDelimiter(lines[index])) continue
374
+ // The header sits directly above the delimiter; skip both and read the body that follows.
375
+ if (index + 1 < lines.length && isDelimiter(lines[index + 1])) { index += 1; continue }
376
+ const cells = cellsOf(lines[index])
377
+ if (cells.length < 3) continue
378
+ rows.push({ cells, text: lines[index] })
379
+ }
380
+ return rows
381
+ }
382
+
383
+ function moduleShapedChecks(root, markdown, contract) {
384
+ const checks = []
385
+ const resolved = resolveRequiredSections(markdown)
386
+ const implementation = resolved.get('phases')
387
+ const prose = strippedOfCodeBlocks(markdown)
388
+
389
+ checks.push(check(
390
+ 'plan.module-scaffold-route',
391
+ implementation !== null && implementation !== undefined && implementation.body.includes(contract.scaffoldSkill),
392
+ `the implementation section must route module work to \`${contract.scaffoldSkill}\``,
393
+ ))
394
+
395
+ const grounding = exampleGrounding(root)
396
+ if (grounding === null) {
397
+ checks.push(check(
398
+ 'plan.example-inventory-readable',
399
+ false,
400
+ `the target must ship ${EXAMPLE_INVENTORY}; without it no plan can be checked against the reference module`,
401
+ ))
402
+ return checks
403
+ }
404
+
405
+ const citedCapabilities = [...grounding.capabilityIds].filter((id) => prose.includes(id))
406
+ checks.push(check(
407
+ 'plan.example-capability-ids',
408
+ citedCapabilities.length >= contract.minCapabilityIds,
409
+ `the plan must name at least ${contract.minCapabilityIds} capability IDs published by ${EXAMPLE_INVENTORY} (found ${citedCapabilities.length ? citedCapabilities.join(', ') : 'none'})`,
410
+ ))
411
+
412
+ const cited = [...new Set(prose.match(EXAMPLE_FILE_PATTERN) ?? [])]
413
+ const grounded = cited.filter((relative) => grounding.sourcePaths.has(relative) && readContainedFile(root, relative) !== null)
414
+ checks.push(check(
415
+ 'plan.example-exact-source-paths',
416
+ grounded.length >= contract.minExactSourcePaths,
417
+ `the plan must name at least ${contract.minExactSourcePaths} exact reference source files that exist and are mapped by the inventory (found ${grounded.length ? grounded.join(', ') : 'none'})`,
418
+ ))
419
+ const ungrounded = cited.filter((relative) => !grounding.sourcePaths.has(relative))
420
+ checks.push(check(
421
+ 'plan.example-paths-resolve',
422
+ ungrounded.length === 0,
423
+ `every named reference source must exist and be mapped by the inventory (unmapped: ${ungrounded.join(', ')})`,
424
+ ))
425
+ const glob = EXAMPLE_GLOB_PATTERN.exec(prose)
426
+ checks.push(check(
427
+ 'plan.example-no-directory-hint',
428
+ glob === null,
429
+ `reference sources must be exact files, not a directory or wildcard${glob ? ` (found ${glob[0]})` : ''}`,
430
+ ))
431
+
432
+ checks.push(check(
433
+ 'plan.example-after-opt-in',
434
+ AFTER_OPT_IN_PATTERN.test(prose),
435
+ 'the plan must state that the reference module is source-present and applies only after opt-in, not that it is already live',
436
+ ))
437
+
438
+ for (const { id, pattern } of SHADOW_MODULE_PATTERNS) {
439
+ checks.push(check(`plan.rejects.${id}`, !pattern.test(prose), `the plan must not propose ${id.replaceAll('-', ' ')}`))
440
+ }
441
+
442
+ const sections = level2Sections(markdown)
443
+ const traceability = sections.find((section) => TRACEABILITY_HEADING_PATTERN.test(section.heading))
444
+ if (!traceability) {
445
+ checks.push(check('plan.traceability.section', false, 'the plan needs a requirement traceability section'))
446
+ return checks
447
+ }
448
+ const rows = tableRows(traceability.body)
449
+ checks.push(check(
450
+ 'plan.traceability.rows',
451
+ rows.length >= contract.minTraceabilityRows,
452
+ `the traceability table needs at least ${contract.minTraceabilityRows} rows, one per added extension surface (found ${rows.length})`,
453
+ ))
454
+ const incomplete = rows.filter((row) => !(
455
+ REQUIREMENT_ID_PATTERN.test(row.text)
456
+ && PHASE_REFERENCE_PATTERN.test(row.text)
457
+ && TEST_IDENTIFIER_PATTERN.test(row.text)
458
+ ))
459
+ checks.push(check(
460
+ 'plan.traceability.complete',
461
+ rows.length > 0 && incomplete.length === 0,
462
+ `every traceability row must carry a requirement ID, an implementation phase, and a self-contained integration test (incomplete: ${incomplete.length})`,
463
+ ))
464
+ const classified = rows.map((row) => ({
465
+ row,
466
+ values: MECHANISM_CLASSIFICATIONS.filter((value) => row.text.includes(value)),
467
+ }))
468
+ checks.push(check(
469
+ 'plan.traceability.classification',
470
+ rows.length > 0 && classified.every((entry) => entry.values.length === 1),
471
+ `every traceability row must state exactly one mechanism classification from ${MECHANISM_CLASSIFICATIONS.join(', ')}`,
472
+ ))
473
+ checks.push(check(
474
+ 'plan.traceability.no-negative-fixture',
475
+ classified.every((entry) => !entry.values.includes(FORBIDDEN_CLASSIFICATION)),
476
+ `no proposed surface may be classified ${FORBIDDEN_CLASSIFICATION}: only a deliberately broken fixture produces it`,
477
+ ))
478
+ return checks
479
+ }
480
+
481
+ function newFeatureChecks(root, definition) {
482
+ const authored = authoredSpecFiles(root)
483
+ const checks = [check(
484
+ 'spec.single',
485
+ authored.length === 1,
486
+ `exactly one new specification must exist under ${SPEC_ROOT} (found ${authored.length ? authored.join(', ') : 'none'})`,
487
+ )]
488
+ if (authored.length !== 1) return checks
489
+ const [name] = authored
490
+ checks.push(check('spec.named', SPEC_FILE_PATTERN.test(name), `the specification must be named {YYYY-MM-DD}-{slug}.md (found ${name})`))
491
+ const markdown = readContainedFile(root, `${SPEC_ROOT}/${name}`)
492
+ if (markdown === null) {
493
+ checks.push(check('spec.readable', false, `${SPEC_ROOT}/${name} must be a readable bounded regular file`))
494
+ return checks
495
+ }
496
+ checks.push(...specStructureChecks(markdown))
497
+ if (definition.moduleShaped) checks.push(...moduleShapedChecks(root, markdown, definition.moduleShaped))
498
+ return checks
499
+ }
500
+
501
+ function existingSpecChecks(root, definition) {
502
+ const authored = authoredSpecFiles(root)
503
+ const expected = definition.coveringSpec.slice(`${SPEC_ROOT}/`.length)
504
+ const checks = [check(
505
+ 'spec.single',
506
+ authored.length === 1 && authored[0] === expected,
507
+ `${definition.coveringSpec} must be the only specification (found ${authored.length ? authored.join(', ') : 'none'})`,
508
+ )]
509
+ const markdown = readContainedFile(root, definition.coveringSpec)
510
+ if (markdown === null) {
511
+ checks.push(check('spec.readable', false, `${definition.coveringSpec} must be a readable bounded regular file`))
512
+ return checks
513
+ }
514
+ checks.push(...specStructureChecks(markdown))
515
+ const prose = strippedOfCodeBlocks(markdown)
516
+ for (const amendment of definition.amendments) {
517
+ checks.push(check(
518
+ `spec.amendment.${amendment.id}`,
519
+ amendment.all.every((pattern) => pattern.test(prose)),
520
+ amendment.requirement,
521
+ ))
522
+ }
523
+ for (const preserved of definition.preserved) {
524
+ checks.push(check(`spec.preserved.${preserved.id}`, preserved.pattern.test(prose), preserved.requirement))
525
+ }
526
+ const entries = changelogEntryCount(markdown)
527
+ checks.push(check(
528
+ 'spec.changelog.amended',
529
+ entries >= definition.minChangelogEntries,
530
+ `the covering spec changelog must record the amendment (needs at least ${definition.minChangelogEntries} entries, found ${entries})`,
531
+ ))
532
+ return checks
533
+ }
534
+
535
+ export function evaluateWritableSpecOracle({ root: requestedRoot, caseId, phase }) {
536
+ if (typeof requestedRoot !== 'string' || !path.isAbsolute(requestedRoot)) throw new Error('root must be absolute')
537
+ const definition = SPEC_CASES[caseId]
538
+ if (!definition) throw new Error(`unsupported writable spec case: ${caseId}`)
539
+ if (!['before', 'after'].includes(phase)) throw new Error('phase must be before or after')
540
+ const root = fs.realpathSync(requestedRoot)
541
+ const checks = [
542
+ ...(definition.kind === 'new-feature' ? newFeatureChecks(root, definition) : existingSpecChecks(root, definition)),
543
+ ...reservedSpecChecks(root),
544
+ ...implementationChecks(root, definition),
545
+ ]
546
+ const failures = checks.filter((entry) => !entry.passed).map((entry) => `${entry.id}: ${entry.requirement}`)
547
+ return { passed: failures.length === 0, failures, checks }
548
+ }
549
+
550
+ function usage() {
551
+ return `Grade one writable planning proof against its fixed specification contract.
552
+
553
+ Usage:
554
+ node .ai/harness/writable-spec-oracles.mjs --root /absolute/app --case ${Object.keys(SPEC_CASES).join('|')} --phase before|after [--json]`
555
+ }
556
+
557
+ function parseArgs(argv) {
558
+ const options = { root: undefined, caseId: undefined, phase: undefined, json: false, help: false }
559
+ for (let index = 0; index < argv.length; index += 1) {
560
+ const arg = argv[index]
561
+ const value = () => {
562
+ const next = argv[index + 1]
563
+ if (!next || next.startsWith('--')) throw new Error(`${arg} requires a value`)
564
+ index += 1
565
+ return next
566
+ }
567
+ if (arg === '--help' || arg === '-h') options.help = true
568
+ else if (arg === '--root') options.root = value()
569
+ else if (arg === '--case') options.caseId = value()
570
+ else if (arg === '--phase') options.phase = value()
571
+ else if (arg === '--json') options.json = true
572
+ else throw new Error(`unknown argument: ${arg}`)
573
+ }
574
+ return options
575
+ }
576
+
577
+ function emit(result, json) {
578
+ if (json) process.stdout.write(`${JSON.stringify(result)}\n`)
579
+ else process.stdout.write(`${result.passed ? 'PASS' : 'FAIL'}: ${result.failures.join('; ') || 'all specification checks passed'}\n`)
580
+ }
581
+
582
+ function main() {
583
+ let options
584
+ try { options = parseArgs(process.argv.slice(2)) } catch (error) {
585
+ process.stdout.write(`${JSON.stringify({ passed: false, failures: [error.message], checks: [] })}\n`)
586
+ return EXIT_INVALID
587
+ }
588
+ if (options.help) { process.stdout.write(`${usage()}\n`); return EXIT_PASS }
589
+ try {
590
+ const result = evaluateWritableSpecOracle(options)
591
+ emit(result, options.json)
592
+ return result.passed ? EXIT_PASS : EXIT_FAILURE
593
+ } catch (error) {
594
+ const message = error instanceof Error ? error.message : String(error)
595
+ emit({ passed: false, failures: [message], checks: [] }, options.json)
596
+ return EXIT_INVALID
597
+ }
598
+ }
599
+
600
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
601
+ process.exitCode = main()
602
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ title: "Replace with a concise durable rule"
3
+ modules: ["platform"]
4
+ areas: ["architecture"]
5
+ topics: ["replace-me", "second-topic"]
6
+ ---
7
+
8
+ # Replace with a concise durable rule
9
+
10
+ **Context**: Describe the concrete failure or correction that produced the lesson.
11
+
12
+ **Problem**: Explain why the failure recurs or why the obvious approach is unsafe.
13
+
14
+ **Rule**: State the reusable instruction future agents should follow.
15
+
16
+ **Applies to**: Name the relevant modules, paths, contracts, or workflows.
@@ -1,4 +1,27 @@
1
- # Lessons Learned
1
+ # Lessons
2
2
 
3
- Record patterns, mistakes, and insights discovered during development.
4
- AI agents will update this file as they work on the codebase.
3
+ This catalog indexes 0 focused lessons without loading their full text. Route the task first, then read only records whose **modules**, standalone-harness **areas**, or **topics** match the work.
4
+
5
+ ## How to use this catalog
6
+
7
+ 1. Start with the exact module ID when one is named by the task.
8
+ 2. Add every matching area from the standalone harness router: `architecture`, `module-data`, `umes`, `backend-ui`, `integration`, `ai-workflow`, `debugging`, `testing`, `framework-context`, or `spec-pr`.
9
+ 3. Use topics to narrow cross-cutting concerns such as `data-scoping`, `optimistic-locking`, `query-index`, or `generated-files`.
10
+ 4. Open only the linked lesson records that match; do not bulk-read `.ai/lessons/`.
11
+
12
+ Useful searches:
13
+
14
+ ```bash
15
+ rg -n '\b<module-or-topic>\b' .ai/lessons.md
16
+ rg -l '"<area>"|"<module>"|"<topic>"' .ai/lessons/*.md
17
+ ```
18
+
19
+ ## Adding or updating a lesson
20
+
21
+ - Copy `.ai/lessons/_template.md` to one focused `.ai/lessons/<kebab-case-slug>.md`; update an existing record instead of duplicating it.
22
+ - Preserve the front matter keys `title`, `modules`, `areas`, and `topics`. Use `platform` only when no module or package owns the lesson, and put the primary area first.
23
+ - Add or update exactly one catalog row under its primary area below. Keep the title stable when code or specs cite it.
24
+ - Put hard boundaries in `AGENTS.md`; lessons explain recurring evidence and the durable rule.
25
+ - Run `node scripts/check-lessons.mjs` before committing.
26
+
27
+ ## Catalog