@open-mercato/cli 0.6.8-develop.7015.1.af90a2ddc7 → 0.6.8-develop.7019.1.f4c01c4b5c

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 (117) hide show
  1. package/.turbo/turbo-build.log +5 -1
  2. package/dist/agentic/guides/module-facts.json +3966 -285
  3. package/dist/agentic/guides/module-facts.v2.json +3966 -285
  4. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys/di-registrations-rich.md +11 -0
  7. package/dist/agentic/guides/modules/api_keys/di-service-tokens.md +11 -0
  8. package/dist/agentic/guides/modules/api_keys/exact-override-targets.md +1 -0
  9. package/dist/agentic/guides/modules/api_keys/index.md +4 -2
  10. package/dist/agentic/guides/modules/attachments/di-registrations-rich.md +6 -5
  11. package/dist/agentic/guides/modules/attachments/di-service-tokens.md +5 -4
  12. package/dist/agentic/guides/modules/attachments/exact-override-targets.md +6 -5
  13. package/dist/agentic/guides/modules/attachments/index.md +2 -2
  14. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  15. package/dist/agentic/guides/modules/auth/di-registrations-rich.md +3 -2
  16. package/dist/agentic/guides/modules/auth/di-service-tokens.md +3 -2
  17. package/dist/agentic/guides/modules/auth/exact-override-targets.md +3 -2
  18. package/dist/agentic/guides/modules/auth/index.md +1 -1
  19. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  20. package/dist/agentic/guides/modules/catalog/incoming-installed-contributions.md +3 -0
  21. package/dist/agentic/guides/modules/catalog/index.md +2 -2
  22. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  24. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  25. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  26. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  27. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  28. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  29. package/dist/agentic/guides/modules/configs/index.md +1 -1
  30. package/dist/agentic/guides/modules/content/index.md +1 -1
  31. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  32. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  33. package/dist/agentic/guides/modules/customers/active-extension-bindings.md +3 -0
  34. package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +7 -0
  35. package/dist/agentic/guides/modules/customers/index.md +3 -3
  36. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  37. package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
  38. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  39. package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
  40. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  41. package/dist/agentic/guides/modules/devices/index.md +1 -1
  42. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  43. package/dist/agentic/guides/modules/directory/di-registrations-rich.md +12 -0
  44. package/dist/agentic/guides/modules/directory/di-service-tokens.md +12 -0
  45. package/dist/agentic/guides/modules/directory/exact-override-targets.md +2 -0
  46. package/dist/agentic/guides/modules/directory/index.md +5 -3
  47. package/dist/agentic/guides/modules/documents/acl-features.md +17 -0
  48. package/dist/agentic/guides/modules/documents/backend-pages.md +13 -0
  49. package/dist/agentic/guides/modules/documents/contribution-resolutions.md +27 -0
  50. package/dist/agentic/guides/modules/documents/di-registrations-rich.md +21 -0
  51. package/dist/agentic/guides/modules/documents/domain-commands.md +37 -0
  52. package/dist/agentic/guides/modules/documents/encryption.md +11 -0
  53. package/dist/agentic/guides/modules/documents/entities.md +21 -0
  54. package/dist/agentic/guides/modules/documents/events.md +25 -0
  55. package/dist/agentic/guides/modules/documents/exact-override-targets.md +39 -0
  56. package/dist/agentic/guides/modules/documents/index.md +25 -0
  57. package/dist/agentic/guides/modules/documents/notifications.md +13 -0
  58. package/dist/agentic/guides/modules/documents/owned-contract-module-metadata.md +11 -0
  59. package/dist/agentic/guides/modules/documents/setup.md +11 -0
  60. package/dist/agentic/guides/modules/documents/umes-contributions.md +27 -0
  61. package/dist/agentic/guides/modules/documents/umes-hosts.md +36 -0
  62. package/dist/agentic/guides/modules/entities/index.md +1 -1
  63. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  64. package/dist/agentic/guides/modules/events/index.md +1 -1
  65. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  66. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  67. package/dist/agentic/guides/modules/generators/index.md +1 -1
  68. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  69. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  70. package/dist/agentic/guides/modules/messages/index.md +1 -1
  71. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  72. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  73. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  74. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  75. package/dist/agentic/guides/modules/planner/index.md +1 -1
  76. package/dist/agentic/guides/modules/portal/index.md +1 -1
  77. package/dist/agentic/guides/modules/progress/index.md +1 -1
  78. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  79. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  80. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  81. package/dist/agentic/guides/modules/resources/index.md +1 -1
  82. package/dist/agentic/guides/modules/sales/incoming-installed-contributions.md +2 -0
  83. package/dist/agentic/guides/modules/sales/index.md +1 -1
  84. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  85. package/dist/agentic/guides/modules/search/index.md +1 -1
  86. package/dist/agentic/guides/modules/security/index.md +1 -1
  87. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  88. package/dist/agentic/guides/modules/sso/index.md +1 -1
  89. package/dist/agentic/guides/modules/staff/index.md +1 -1
  90. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  91. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  92. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  93. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  94. package/dist/agentic/guides/modules/translations/index.md +1 -1
  95. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  96. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  97. package/dist/agentic/guides/modules/wms/index.md +1 -1
  98. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  99. package/dist/agentic/guides/reference-module-facts.json +1 -1
  100. package/dist/agentic/guides/upstream/manifest.json +1 -1
  101. package/dist/agentic/shared/AGENTS.md.template +3 -3
  102. package/dist/agentic/shared/ai/harness/README.md +2 -2
  103. package/dist/agentic/shared/ai/harness/RELEASE.md +4 -4
  104. package/dist/agentic/shared/ai/harness/cases.json +83 -0
  105. package/dist/agentic/shared/ai/harness/cases.schema.json +4 -4
  106. package/dist/agentic/shared/ai/harness/validators.json +1 -1
  107. package/dist/lib/generators/module-registry.js +77 -11
  108. package/dist/lib/generators/module-registry.js.map +2 -2
  109. package/dist/mercato.js +14 -0
  110. package/dist/mercato.js.map +2 -2
  111. package/package.json +6 -6
  112. package/src/__tests__/resolve-queue-abandon-hook.test.ts +39 -0
  113. package/src/lib/generators/__tests__/generated-registry-imports.test.ts +128 -0
  114. package/src/lib/generators/__tests__/metadata-extraction.test.ts +44 -0
  115. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +9 -5
  116. package/src/lib/generators/module-registry.ts +118 -5
  117. package/src/mercato.ts +27 -0
@@ -1,5 +1,5 @@
1
1
  # security — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/enterprise 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/enterprise 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/enterprise/src/modules/security
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # shipping_carriers — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/shipping_carriers
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # sso — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/enterprise 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/enterprise 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/enterprise/src/modules/sso
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # staff — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/staff
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # storage_s3 — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/storage-s3 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/storage-s3 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/storage-s3/src/modules/storage_s3
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # sync_akeneo — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/sync-akeneo 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/sync-akeneo 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/sync-akeneo/src/modules/sync_akeneo
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # sync_excel — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/sync_excel
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # system_status_overlays — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/enterprise 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/enterprise 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/enterprise/src/modules/system_status_overlays
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # translations — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/translations
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # warranty_claims — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/warranty_claims
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # webhooks — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/webhooks 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/webhooks 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/webhooks/src/modules/webhooks
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # wms — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/wms
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # workflows — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.6.8-develop.7015.1.af90a2ddc7; core 0.6.8-develop.7015.1.af90a2ddc7 — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.6.8-develop.7019.1.f4c01c4b5c; core 0.6.8-develop.7019.1.f4c01c4b5c — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/workflows
4
4
 
5
5
  ## Sections
@@ -1185,7 +1185,7 @@
1185
1185
  "facts": {
1186
1186
  "title": "Example Module",
1187
1187
  "description": "Demonstrates pages, APIs, entities and DI.",
1188
- "coreVersion": "0.6.8-develop.7015.1.af90a2ddc7",
1188
+ "coreVersion": "0.6.8-develop.7019.1.f4c01c4b5c",
1189
1189
  "sourcePackage": null,
1190
1190
  "sourceVersion": null,
1191
1191
  "sourceRoot": "src/modules/example",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generator": "@open-mercato/cli@0.6.8-develop.7015.1.af90a2ddc7",
3
+ "generator": "@open-mercato/cli@0.6.8-develop.7019.1.f4c01c4b5c",
4
4
  "files": {
5
5
  "AGENTS.md": "1f54610cebc4fd7e42b56d1c18859edc7ed7d3165c9af72c4530b6feaf76e457",
6
6
  "BACKWARD_COMPATIBILITY.md": "d2fdc675a9c86cab32ab024dae29f45717f441ddc305329c056fb66c5b498c27"
@@ -12,7 +12,7 @@ Route first; never probe unmatched context.
12
12
  - Put entities in `src/modules/<id>/data/entities.ts`; API routes need per-method `metadata` + `openApi`.
13
13
  - Editable records expose `updated_at`/`updatedAt`; custom update/delete clients send the version and surface 409s.
14
14
  - Run `yarn db:generate`, review scoped SQL/snapshot, and ask before applying it.
15
- - Run `yarn generate` after discovery files, `src/modules.ts`, routes, pages, events, widgets, agents, tools, or workflows change.
15
+ - Run `yarn generate` after discovery files/`src/modules.ts`/routes/pages/events/widgets/agents/tools/workflows change.
16
16
  - Contract-surface changes (route/schema/ID/export/seam/signature/event payload/CLI) MUST read `.ai/guides/upstream/BACKWARD_COMPATIBILITY.md`; tenant/org scope alone is not a contract.
17
17
  - Localize strings; use shared UI/tokens and cover loading/empty/error/conflict/keyboard/a11y.
18
18
 
@@ -23,8 +23,8 @@ Route first; never probe unmatched context.
23
23
  ## Never
24
24
 
25
25
  - Never leak tenants, trust payload scope, or treat missing scope as unrestricted.
26
- - Never edit `node_modules`, `.mercato/generated/**`, generated facts, or shipped migrations.
27
- - Never use cross-module ORM relations; use IDs/snapshots, events, enrichers, extensions, or optional DI.
26
+ - Never edit `node_modules`/`.mercato/generated/**`/generated facts/shipped migrations.
27
+ - Never use cross-module ORM relations; use IDs/snapshots/events/enrichers/extensions/optional DI.
28
28
  - Never use raw admin `fetch`/`<form>`, ad hoc crypto/cache/queues, role-name guards, or direct mutations when helpers exist.
29
29
  - Never hard-code user strings/status colors; expose secrets/transcripts; or guess answerable contracts.
30
30
 
@@ -1,6 +1,6 @@
1
1
  # Agent harness evaluations
2
2
 
3
- `cases.json` is the 233-case standalone-app contract. Run `yarn harness:validate --all` for the deterministic gate. Live routing uses a fresh read-only process per case:
3
+ `cases.json` is the 234-case standalone-app contract. Run `yarn harness:validate --all` for the deterministic gate. Live routing uses a fresh read-only process per case:
4
4
 
5
5
  UMES routing is fact-first. The additive and unified-override audit evaluations, plus their targeted cases, resolve exact/pattern hosts, outgoing contributions, correlation provenance, round-trip groups, framework-owned targets, and override domain/key/mode from the generated module sheets and `.ai/guides/framework-extension-points.md` before bounded installed source. The repository UMES umbrella spec may appear as optional source-checkout provenance; it is never required in a standalone scaffold.
6
6
 
@@ -18,7 +18,7 @@ yarn harness:release --runner codex --prepare-targets /absolute/empty-release-ta
18
18
  yarn harness:release --runner codex --portability-runner claude --prepare-targets /absolute/empty-release-targets --acknowledge-writes
19
19
  ```
20
20
 
21
- The primary runner owns all 233 routing cases, all 49 writable cases, and all generative-judge runs. No per-case fallback or mixed primary ownership is allowed. Omitting `--portability-runner` is valid and the sanitized report records `portabilityRunner: null`; explicitly requesting an unavailable or failing secondary runner fails that extended run.
21
+ The primary runner owns all 234 routing cases, all 49 writable cases, and all generative-judge runs. No per-case fallback or mixed primary ownership is allowed. Omitting `--portability-runner` is valid and the sanitized report records `portabilityRunner: null`; explicitly requesting an unavailable or failing secondary runner fails that extended run.
22
22
 
23
23
  Writable evaluation is intentionally opt-in. The expanded catalog has a 49-case writable release target, but only cases registered in `release-matrix.json` and backed by controller-owned fixtures and oracles are executable. Copy or create a fresh standalone app for one registered case, then seed only that case and mark the target disposable:
24
24
 
@@ -6,7 +6,7 @@ Run the complete per-release gate from a generated standalone app with one comma
6
6
  yarn harness:release --runner codex --prepare-targets /absolute/empty-release-targets --acknowledge-writes
7
7
  ```
8
8
 
9
- Choose exactly one blocking primary runner with `--runner codex` or `--runner claude`. That runner owns the complete 233-case routing gate and every writable/review lane. To add cross-model portability evidence, explicitly pass the other runner as `--portability-runner claude` or `--portability-runner codex`; it runs only the exact 49-case representative read-only set. The two runners must differ. Omitting the portability option is valid and is recorded as `portabilityRunner: null`; no secondary result is claimed. There is no per-case fallback or mixed primary ownership. Once requested, a portability failure or unavailable runner fails that extended release run.
9
+ Choose exactly one blocking primary runner with `--runner codex` or `--runner claude`. That runner owns the complete 234-case routing gate and every writable/review lane. To add cross-model portability evidence, explicitly pass the other runner as `--portability-runner claude` or `--portability-runner codex`; it runs only the exact 49-case representative read-only set. The two runners must differ. Omitting the portability option is valid and is recorded as `portabilityRunner: null`; no secondary result is claimed. There is no per-case fallback or mixed primary ownership. Once requested, a portability failure or unavailable runner fails that extended release run.
10
10
 
11
11
  `--prepare-targets` accepts only an absolute, new or empty regular directory outside the controller app. The controller must be a sanitized fresh scaffold: automatic preparation fails before copying when it finds `.env`, `.env.*` (except `.env.example`, `.env.sample`, and `.env.template`), credential files, or private-key files. Never use a configured development or production app as the controller. It copies the fresh scaffold once per catalog case whose `evaluationKind` is `implementation` or `regression`, while excluding `.git`, `node_modules`, build/cache/coverage output, `.ai/harness/results`, `.ai/reports`, and `.ai/framework-context`. Each target receives a guarded link to the controller's installed dependency tree. The OS sandbox resolves that link as read-only during both the writable model run and the target command gate. The release gate also hashes every dependency entry and regular-file body once before execution and once after the complete suite, and fails if any nested content or metadata changed. A generated `release-targets.json` records the local mapping.
12
12
 
@@ -34,17 +34,17 @@ For externally prepared apps, `--writable-targets /absolute/release-targets.json
34
34
  }
35
35
  ```
36
36
 
37
- The current catalog contains 233 cases, including 49 writable implementation/regression cases (21.0%). The command still derives all counts and case IDs from `cases.json`, `validators.json`, and `release-matrix.json`; those figures are documented release facts, not runner constants. The matrix keeps both supported runner model selectors, an exact all-case primary profile, an exact 49-case portability profile, and runner-neutral writable assignments. Run `yarn install-skills` first so the pinned external `om-code-review` skill and ownership evidence are present; the reusable local `om-judge-agent-session` skill ships with the scaffold. Before running a model or writing a fixture, the release command requires complete deterministic, primary live-routing, writable, trusted-oracle, target, generated-test, and generative-judge coverage. Every one of the 49 writable cases must have a judge assignment composing both skills. Missing business fixtures or release-matrix entries fail preflight and are listed by exact case ID in the report.
37
+ The current catalog contains 234 cases, including 49 writable implementation/regression cases (20.9%). The command still derives all counts and case IDs from `cases.json`, `validators.json`, and `release-matrix.json`; those figures are documented release facts, not runner constants. The matrix keeps both supported runner model selectors, an exact all-case primary profile, an exact 49-case portability profile, and runner-neutral writable assignments. Run `yarn install-skills` first so the pinned external `om-code-review` skill and ownership evidence are present; the reusable local `om-judge-agent-session` skill ships with the scaffold. Before running a model or writing a fixture, the release command requires complete deterministic, primary live-routing, writable, trusted-oracle, target, generated-test, and generative-judge coverage. Every one of the 49 writable cases must have a judge assignment composing both skills. Missing business fixtures or release-matrix entries fail preflight and are listed by exact case ID in the report.
38
38
 
39
39
  ## PR #4529 remediation evidence
40
40
 
41
- The PR's focused remediation evidence is not a release-certification substitute. Fresh emitted controllers pass deterministic 192/192, and the field-tested OMH-188–192 generative cohort passes on default Codex, Claude Sonnet, and high-effort gpt-5.4-mini. Fresh OMH-185 writable attempts fixed concrete organization-scope, command-object, module-activation, command-snapshot, schema, custom-field, UI, and Jest guidance defects at their routed owners without relaxing trusted oracles. The final attempt reached the case's fixed 600-second ceiling and is excluded from pass evidence. Issue #4670 now owns the complete selected-primary 233-case routing and 49-case writable/generated-test/review certification, prioritizing the generative cohort and recording unavailable Claude lanes without fallback or mixed-runner ownership.
41
+ The PR's focused remediation evidence is not a release-certification substitute. Fresh emitted controllers pass deterministic 192/192, and the field-tested OMH-188–192 generative cohort passes on default Codex, Claude Sonnet, and high-effort gpt-5.4-mini. Fresh OMH-185 writable attempts fixed concrete organization-scope, command-object, module-activation, command-snapshot, schema, custom-field, UI, and Jest guidance defects at their routed owners without relaxing trusted oracles. The final attempt reached the case's fixed 600-second ceiling and is excluded from pass evidence. Issue #4670 now owns the complete selected-primary 234-case routing and 49-case writable/generated-test/review certification, prioritizing the generative cohort and recording unavailable Claude lanes without fallback or mixed-runner ownership.
42
42
 
43
43
  After preflight it runs, in order:
44
44
 
45
45
  1. deterministic validation for the complete catalog;
46
46
  2. the release matrix's fixed `yarn generate`, `yarn typecheck`, `yarn lint`, and `yarn build` foundation;
47
- 3. the selected primary runner across all 233 live-routing cases, followed by the optional distinct portability runner across the exact 49-case read-only sample when requested;
47
+ 3. the selected primary runner across all 234 live-routing cases, followed by the optional distinct portability runner across the exact 49-case read-only sample when requested;
48
48
  4. fixture preparation and the selected primary runner for every writable case, including the controller-owned AST/behavior oracles and target typecheck;
49
49
  5. `yarn generate`, `yarn typecheck`, `yarn lint`, and `yarn build` in every writable target, after its trusted oracles;
50
50
  6. real generated-code execution for OMH-163 and OMH-192 through fixed Jest, OMH-164 through API-only Playwright, and OMH-165 through real-browser Playwright; and
@@ -21100,5 +21100,88 @@
21100
21100
  "OMH-039",
21101
21101
  "OMH-232"
21102
21102
  ]
21103
+ },
21104
+ {
21105
+ "id": "OMH-234",
21106
+ "title": "Reuse installed collaborative documents instead of building a private notes system",
21107
+ "family": "architecture",
21108
+ "mode": "analysis",
21109
+ "evaluationKind": "routing",
21110
+ "risk": "high",
21111
+ "prompt": "In a freshly scaffolded standalone Open Mercato app, account teams need shared rich documents attached to customers, deals, quotes, and orders. Several staff members must be able to edit at the same time, discuss selected text, mention colleagues, restore earlier versions, reuse templates, attach files, and grant viewer, commenter, editor, or owner access per document. Decide the smallest safe design against what the installed modules already provide, keep tenant, organization, and record-level visibility boundaries intact, state the access-control posture for reading, commenting, editing, sharing, and managing templates, and identify the smallest relevant validation. Do not model your own document, content, comment, share, version, attachment, or template tables before establishing whether an installed module already owns this capability.",
21112
+ "tags": [
21113
+ "architecture",
21114
+ "module-facts",
21115
+ "reuse-installed",
21116
+ "collaborative-documents"
21117
+ ],
21118
+ "owner": {
21119
+ "kind": "facts",
21120
+ "path": ".ai/guides/modules/documents/index.md",
21121
+ "ruleIds": [
21122
+ "BC-01",
21123
+ "BC-04"
21124
+ ]
21125
+ },
21126
+ "expectedRouter": {
21127
+ "required": [
21128
+ "architecture"
21129
+ ],
21130
+ "allowedExtra": [
21131
+ "module-data",
21132
+ "backend-ui",
21133
+ "umes",
21134
+ "framework-context"
21135
+ ]
21136
+ },
21137
+ "requiredSkills": [
21138
+ "om-help"
21139
+ ],
21140
+ "context": {
21141
+ "required": [
21142
+ "AGENTS.md",
21143
+ ".ai/guides/architecture.md",
21144
+ ".ai/skills/om-help/SKILL.md",
21145
+ ".ai/guides/modules/documents/index.md"
21146
+ ],
21147
+ "allowedExtra": [
21148
+ ".ai/guides/contracts.md",
21149
+ ".ai/guides/extensions.md",
21150
+ ".ai/guides/backend-ui.md"
21151
+ ],
21152
+ "forbidden": [
21153
+ ".env*",
21154
+ ".git/**"
21155
+ ]
21156
+ },
21157
+ "requiredDecisions": [
21158
+ "facts-first",
21159
+ "reuse-installed-documents",
21160
+ "per-document-access",
21161
+ "tenant-scope",
21162
+ "acl-features",
21163
+ "smallest-validation"
21164
+ ],
21165
+ "forbiddenPatterns": [
21166
+ "node_modules.{0,40}(?:write|edit|patch)",
21167
+ "(?:tenant|organization).{0,30}(?:unscoped|scope optional)"
21168
+ ],
21169
+ "validators": [
21170
+ "catalog.schema",
21171
+ "owner.reference",
21172
+ "skills.reference",
21173
+ "router.contract",
21174
+ "context.budget",
21175
+ "context.forbidden",
21176
+ "patterns.forbidden"
21177
+ ],
21178
+ "maxContextFiles": 8,
21179
+ "maxInitialContextBytes": 53248,
21180
+ "maxTotalContextBytes": 147456,
21181
+ "relatedCases": [
21182
+ "OMH-002",
21183
+ "OMH-194",
21184
+ "OMH-210"
21185
+ ]
21103
21186
  }
21104
21187
  ]
@@ -3,8 +3,8 @@
3
3
  "$id": "https://open-mercato.dev/schemas/standalone-harness-cases.schema.json",
4
4
  "title": "Open Mercato standalone harness case catalog",
5
5
  "type": "array",
6
- "minItems": 233,
7
- "maxItems": 233,
6
+ "minItems": 234,
7
+ "maxItems": 234,
8
8
  "items": {
9
9
  "type": "object",
10
10
  "additionalProperties": false,
@@ -15,7 +15,7 @@
15
15
  "maxTotalContextBytes", "relatedCases"
16
16
  ],
17
17
  "properties": {
18
- "id": { "type": "string", "pattern": "^OMH-(00[1-9]|0[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-3])$" },
18
+ "id": { "type": "string", "pattern": "^OMH-(00[1-9]|0[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-4])$" },
19
19
  "title": { "type": "string", "minLength": 12, "maxLength": 180 },
20
20
  "family": { "enum": ["architecture", "module", "umes", "integration", "ai-workflow", "bugfix", "business", "testing"] },
21
21
  "mode": { "enum": ["analysis", "one-shot", "spec", "bugfix", "review"] },
@@ -168,7 +168,7 @@
168
168
  "maxInitialContextBytes": { "type": "integer", "minimum": 4096, "maximum": 98304 },
169
169
  "maxTotalContextBytes": { "type": "integer", "minimum": 8192, "maximum": 1048576 },
170
170
  "timeoutMs": { "type": "integer", "minimum": 1000, "maximum": 600000 },
171
- "relatedCases": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "pattern": "^OMH-(00[1-9]|0[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-3])$" } },
171
+ "relatedCases": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "pattern": "^OMH-(00[1-9]|0[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-4])$" } },
172
172
  "source": {
173
173
  "type": "object",
174
174
  "additionalProperties": false,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "catalog": {
4
- "expectedCaseCount": 233,
4
+ "expectedCaseCount": 234,
5
5
  "maxContextFiles": 16,
6
6
  "maxInitialContextBytes": 98304,
7
7
  "maxTotalContextBytes": 1048576,
@@ -320,6 +320,38 @@ function extractObjectPropertiesFromAst(sourceFile, exportName) {
320
320
  }
321
321
  return Object.keys(result).length > 0 ? result : null;
322
322
  }
323
+ function namedObjectLiteralDeclaresProperty(sourceFile, exportName, propertyName) {
324
+ let source = "";
325
+ try {
326
+ source = fs.readFileSync(sourceFile, "utf8");
327
+ } catch {
328
+ return false;
329
+ }
330
+ const parsed = ts.createSourceFile(sourceFile, source, ts.ScriptTarget.Latest, true, inferScriptKind(sourceFile));
331
+ const exportedNames = /* @__PURE__ */ new Set();
332
+ for (const statement of parsed.statements) {
333
+ if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause) && !statement.moduleSpecifier) {
334
+ for (const element of statement.exportClause.elements) exportedNames.add(element.name.text);
335
+ }
336
+ }
337
+ for (const statement of parsed.statements) {
338
+ if (!ts.isVariableStatement(statement)) continue;
339
+ const exportsDirectly = (ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
340
+ for (const declaration of statement.declarationList.declarations) {
341
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== exportName) continue;
342
+ if (!exportsDirectly && !exportedNames.has(exportName)) continue;
343
+ const objectLiteral2 = unwrapObjectLiteralExpression(declaration.initializer);
344
+ if (!objectLiteral2) return false;
345
+ return objectLiteral2.properties.some((property) => {
346
+ const name = property.name;
347
+ if (!name) return false;
348
+ const key = ts.isIdentifier(name) ? name.text : ts.isStringLiteral(name) ? name.text : null;
349
+ return key === propertyName;
350
+ });
351
+ }
352
+ }
353
+ return false;
354
+ }
323
355
  function extractNamedObjectLiteralExport(sourceFile, exportName) {
324
356
  const literal = extractNamedObjectLiteralSource(sourceFile, exportName);
325
357
  if (literal) {
@@ -1238,6 +1270,7 @@ function normalizeWorkerMetadata(raw) {
1238
1270
  if (typeof source.concurrency === "number") normalized.concurrency = source.concurrency;
1239
1271
  if (typeof source.lockDuration === "number") normalized.lockDuration = source.lockDuration;
1240
1272
  if (typeof source.maxStalledCount === "number") normalized.maxStalledCount = source.maxStalledCount;
1273
+ if (typeof source.onJobAbandoned === "function") normalized.hasJobAbandonedHook = true;
1241
1274
  return Object.keys(normalized).length > 0 ? normalized : null;
1242
1275
  }
1243
1276
  async function loadSubscriberMetadata(sourceFile) {
@@ -1246,7 +1279,12 @@ async function loadSubscriberMetadata(sourceFile) {
1246
1279
  }
1247
1280
  async function loadWorkerMetadata(sourceFile) {
1248
1281
  const sourceModule = await loadModuleExportsFromSource(sourceFile);
1249
- return normalizeWorkerMetadata(sourceModule?.metadata) ?? normalizeWorkerMetadata(extractNamedObjectLiteralExport(sourceFile, "metadata"));
1282
+ const metadata = normalizeWorkerMetadata(sourceModule?.metadata) ?? normalizeWorkerMetadata(extractNamedObjectLiteralExport(sourceFile, "metadata"));
1283
+ if (!metadata) return null;
1284
+ if (!metadata.hasJobAbandonedHook && namedObjectLiteralDeclaresProperty(sourceFile, "metadata", "onJobAbandoned")) {
1285
+ metadata.hasJobAbandonedHook = true;
1286
+ }
1287
+ return metadata;
1250
1288
  }
1251
1289
  async function discoverSubscribers(roots, imps, modId) {
1252
1290
  const subscribers = [];
@@ -1708,12 +1746,16 @@ function processSubscribers(discovered) {
1708
1746
  }
1709
1747
  return subscribers;
1710
1748
  }
1749
+ const ABANDON_HOOK_FACTORY = "createLazyModuleWorkerAbandonHook";
1750
+ function workersDeclareAbandonHook(discovered) {
1751
+ return discovered.some(({ metadata }) => metadata.hasJobAbandonedHook === true);
1752
+ }
1711
1753
  function processWorkers(discovered) {
1712
1754
  const workers = [];
1713
1755
  for (const { id, importPath, metadata } of discovered) {
1714
1756
  const workerId = metadata.id ?? id;
1715
1757
  workers.push(
1716
- `{ id: ${toLiteral(workerId)}, queue: ${toLiteral(metadata.queue)}, concurrency: ${toLiteral(metadata.concurrency ?? 1)}${metadata.lockDuration === void 0 ? "" : `, lockDuration: ${toLiteral(metadata.lockDuration)}`}${metadata.maxStalledCount === void 0 ? "" : `, maxStalledCount: ${toLiteral(metadata.maxStalledCount)}`}, handler: createLazyModuleWorker(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)}) }`
1758
+ `{ id: ${toLiteral(workerId)}, queue: ${toLiteral(metadata.queue)}, concurrency: ${toLiteral(metadata.concurrency ?? 1)}${metadata.lockDuration === void 0 ? "" : `, lockDuration: ${toLiteral(metadata.lockDuration)}`}${metadata.maxStalledCount === void 0 ? "" : `, maxStalledCount: ${toLiteral(metadata.maxStalledCount)}`}${metadata.hasJobAbandonedHook ? `, onJobAbandoned: ${ABANDON_HOOK_FACTORY}(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)})` : ""}, handler: createLazyModuleWorker(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)}) }`
1717
1759
  );
1718
1760
  }
1719
1761
  return workers;
@@ -1920,6 +1962,7 @@ function renderAstModuleRegistryFile(options) {
1920
1962
  namedImports: [
1921
1963
  { name: "createLazyModuleSubscriber" },
1922
1964
  { name: "createLazyModuleWorker" },
1965
+ ...options.includeAbandonHookImport ? [{ name: ABANDON_HOOK_FACTORY }] : [],
1923
1966
  { name: "Module", isTypeOnly: true }
1924
1967
  ]
1925
1968
  });
@@ -2010,9 +2053,10 @@ function buildLazyRouteComponentExpression(importPath) {
2010
2053
  return `async (props: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; const Component = (mod.default ?? mod) as any; return createElement(Component, props) }`;
2011
2054
  }
2012
2055
  function renderAstLegacyModuleRegistryOutput(options) {
2056
+ const needsAbandonHookImport = options.includeAbandonHookImport || options.moduleEntries.some((entry) => entry.includes(ABANDON_HOOK_FACTORY));
2013
2057
  const importSection = [
2014
2058
  ...options.includeCreateElementImport ? ["import { createElement } from 'react'"] : [],
2015
- "import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'",
2059
+ needsAbandonHookImport ? "import { createLazyModuleSubscriber, createLazyModuleWorker, createLazyModuleWorkerAbandonHook, type Module } from '@open-mercato/shared/modules/registry'" : "import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'",
2016
2060
  ...options.imports.map((entry) => serializeGeneratedImport(entry))
2017
2061
  ].join("\n");
2018
2062
  return `// AUTO-GENERATED by mercato generate registry
@@ -2279,6 +2323,13 @@ function processWorkersAst(discovered) {
2279
2323
  { name: "concurrency", value: metadata.concurrency ?? 1 },
2280
2324
  ...metadata.lockDuration === void 0 ? [] : [{ name: "lockDuration", value: metadata.lockDuration }],
2281
2325
  ...metadata.maxStalledCount === void 0 ? [] : [{ name: "maxStalledCount", value: metadata.maxStalledCount }],
2326
+ ...metadata.hasJobAbandonedHook ? [{
2327
+ name: "onJobAbandoned",
2328
+ value: callExpression(identifier(ABANDON_HOOK_FACTORY), [
2329
+ arrowFunction({ body: importExpression(importPath) }),
2330
+ workerId
2331
+ ])
2332
+ }] : [],
2282
2333
  {
2283
2334
  name: "handler",
2284
2335
  value: callExpression(identifier("createLazyModuleWorker"), [
@@ -2588,6 +2639,7 @@ async function generateModuleRegistryFromDiscovery(options) {
2588
2639
  const requiresByModule = /* @__PURE__ */ new Map();
2589
2640
  const commandLoaderEntries = [];
2590
2641
  let hasRouteComponents = false;
2642
+ let hasAbandonHookWorkers = false;
2591
2643
  const umesConflictSources = [];
2592
2644
  const seenBackendRoutePatterns = /* @__PURE__ */ new Map();
2593
2645
  for (const discovered of discovery.modules) {
@@ -2791,7 +2843,9 @@ async function generateModuleRegistryFromDiscovery(options) {
2791
2843
  extraImports: runtimeImports
2792
2844
  }));
2793
2845
  subscribers.push(...processSubscribers(await discovered.getSubscribers()));
2794
- workers.push(...processWorkers(await discovered.getWorkers()));
2846
+ const discoveredWorkersForModule = await discovered.getWorkers();
2847
+ if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true;
2848
+ workers.push(...processWorkers(discoveredWorkersForModule));
2795
2849
  {
2796
2850
  const parts = [];
2797
2851
  if (fieldsImportName)
@@ -2943,13 +2997,15 @@ async function generateModuleRegistryFromDiscovery(options) {
2943
2997
  fileName: "modules.generated.ts",
2944
2998
  imports,
2945
2999
  moduleEntries: moduleDecls,
2946
- includeCreateElementImport: hasRouteComponents
3000
+ includeCreateElementImport: hasRouteComponents,
3001
+ includeAbandonHookImport: hasAbandonHookWorkers
2947
3002
  });
2948
3003
  const runtimeOutput = renderAstLegacyModuleRegistryOutput({
2949
3004
  fileName: "modules.runtime.generated.ts",
2950
3005
  imports: runtimeImports,
2951
3006
  moduleEntries: runtimeModuleDecls,
2952
- includeCreateElementImport: true
3007
+ includeCreateElementImport: true,
3008
+ includeAbandonHookImport: hasAbandonHookWorkers
2953
3009
  });
2954
3010
  const frontendRoutesOutput = renderAstLegacyManifestOutput({
2955
3011
  fileName: "frontend-routes.generated.ts",
@@ -3137,6 +3193,7 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3137
3193
  const requiresByModule = /* @__PURE__ */ new Map();
3138
3194
  const commandLoaderEntries = [];
3139
3195
  let hasRouteComponents = false;
3196
+ let hasAbandonHookWorkers = false;
3140
3197
  const seenBackendRoutePatterns = /* @__PURE__ */ new Map();
3141
3198
  for (const discovered of discovery.modules) {
3142
3199
  const { modId, roots, imps, appImportBase } = discovered;
@@ -3282,7 +3339,9 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3282
3339
  i18nLocaleShards.set(locale, shard);
3283
3340
  }
3284
3341
  subscribers.push(...processSubscribersAst(await discovered.getSubscribers()));
3285
- workers.push(...processWorkersAst(await discovered.getWorkers()));
3342
+ const discoveredWorkersForModule = await discovered.getWorkers();
3343
+ if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true;
3344
+ workers.push(...processWorkersAst(discoveredWorkersForModule));
3286
3345
  {
3287
3346
  dashboardWidgetsValue = buildModuleDashboardWidgetsValue(discovered.dashboardWidgetEntries);
3288
3347
  }
@@ -3399,19 +3458,22 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3399
3458
  generator: "registry (app version)",
3400
3459
  imports,
3401
3460
  moduleEntries: moduleDecls,
3402
- includeCreateElementImport: hasRouteComponents
3461
+ includeCreateElementImport: hasRouteComponents,
3462
+ includeAbandonHookImport: hasAbandonHookWorkers
3403
3463
  });
3404
3464
  const bootstrapOutput = renderAstModuleRegistryFile({
3405
3465
  fileName: "modules.bootstrap.generated.ts",
3406
3466
  generator: "registry (bootstrap version)",
3407
3467
  imports: bootstrapImports,
3408
- moduleEntries: bootstrapModuleDecls
3468
+ moduleEntries: bootstrapModuleDecls,
3469
+ includeAbandonHookImport: hasAbandonHookWorkers
3409
3470
  });
3410
3471
  const i18nOutput = renderAstModuleRegistryFile({
3411
3472
  fileName: "modules.i18n.generated.ts",
3412
3473
  generator: "registry (i18n version)",
3413
3474
  imports: i18nImports,
3414
- moduleEntries: i18nModuleDecls
3475
+ moduleEntries: i18nModuleDecls,
3476
+ includeAbandonHookImport: hasAbandonHookWorkers
3415
3477
  });
3416
3478
  const legacyOutput = renderAstLegacyAliasFile({
3417
3479
  fileName: "bootstrap-modules.generated.ts",
@@ -3486,6 +3548,7 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3486
3548
  return result;
3487
3549
  }
3488
3550
  async function generateModuleRegistryCliFromDiscovery(options) {
3551
+ let hasAbandonHookWorkers = false;
3489
3552
  const { resolver, quiet = false } = options;
3490
3553
  const { discovery } = options;
3491
3554
  const result = createGeneratorResult();
@@ -3604,6 +3667,7 @@ async function generateModuleRegistryCliFromDiscovery(options) {
3604
3667
  subscribers.push(...processSubscribersAst(await discovered.getSubscribers()));
3605
3668
  const discoveredWorkers = await discovered.getWorkers();
3606
3669
  const workerGenerationEntries = collectWorkerGenerationEntries(discoveredWorkers, modId);
3670
+ if (workersDeclareAbandonHook(discoveredWorkers)) hasAbandonHookWorkers = true;
3607
3671
  workers.push(...processWorkersAst(discoveredWorkers));
3608
3672
  devSupervisorWorkers.push(...workerGenerationEntries.map(({ importPath: _importPath, ...worker }) => worker));
3609
3673
  {
@@ -3720,7 +3784,8 @@ async function generateModuleRegistryCliFromDiscovery(options) {
3720
3784
  fileName: "modules.cli.generated.ts",
3721
3785
  generator: "registry (CLI version)",
3722
3786
  imports,
3723
- moduleEntries: moduleDecls
3787
+ moduleEntries: moduleDecls,
3788
+ includeAbandonHookImport: hasAbandonHookWorkers
3724
3789
  });
3725
3790
  const legacyOutput = renderAstLegacyAliasFile({
3726
3791
  fileName: "cli-modules.generated.ts",
@@ -3806,6 +3871,7 @@ export {
3806
3871
  generateModuleRegistryApp,
3807
3872
  generateModuleRegistryCli,
3808
3873
  hasNamedExport,
3874
+ namedObjectLiteralDeclaresProperty,
3809
3875
  resetConventionWarnings,
3810
3876
  resolveNamedObjectExport,
3811
3877
  warnIfPageMetaMissingMetadataExport,