@open-mercato/cli 0.6.8-develop.6931.1.4a1e1c786b → 0.6.8-develop.6940.1.177ea30c6e

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 (168) hide show
  1. package/.turbo/turbo-build.log +65 -1
  2. package/build.mjs +57 -7
  3. package/dist/agentic/guides/backend-ui.md +7 -1
  4. package/dist/agentic/guides/module-facts.json +565 -224
  5. package/dist/agentic/guides/module-facts.v2.json +122933 -0
  6. package/dist/agentic/guides/modules/ai_assistant.md +2 -2
  7. package/dist/agentic/guides/modules/api_docs.md +2 -2
  8. package/dist/agentic/guides/modules/api_keys.md +2 -2
  9. package/dist/agentic/guides/modules/attachments.md +2 -2
  10. package/dist/agentic/guides/modules/audit_logs.md +2 -2
  11. package/dist/agentic/guides/modules/auth.md +2 -2
  12. package/dist/agentic/guides/modules/business_rules.md +2 -2
  13. package/dist/agentic/guides/modules/catalog.md +11 -4
  14. package/dist/agentic/guides/modules/channel_gmail.md +2 -2
  15. package/dist/agentic/guides/modules/channel_imap.md +2 -2
  16. package/dist/agentic/guides/modules/checkout.md +8 -2
  17. package/dist/agentic/guides/modules/communication_channels.md +6 -6
  18. package/dist/agentic/guides/modules/configs.md +2 -2
  19. package/dist/agentic/guides/modules/content.md +2 -2
  20. package/dist/agentic/guides/modules/currencies.md +2 -2
  21. package/dist/agentic/guides/modules/customer_accounts.md +2 -2
  22. package/dist/agentic/guides/modules/customers.md +8 -8
  23. package/dist/agentic/guides/modules/dashboards.md +12 -12
  24. package/dist/agentic/guides/modules/data_sync.md +4 -4
  25. package/dist/agentic/guides/modules/design_system.md +2 -2
  26. package/dist/agentic/guides/modules/dictionaries.md +2 -2
  27. package/dist/agentic/guides/modules/directory.md +2 -2
  28. package/dist/agentic/guides/modules/entities.md +3 -3
  29. package/dist/agentic/guides/modules/eudr.md +3 -3
  30. package/dist/agentic/guides/modules/events.md +2 -2
  31. package/dist/agentic/guides/modules/feature_toggles.md +2 -2
  32. package/dist/agentic/guides/modules/gateway_stripe.md +2 -2
  33. package/dist/agentic/guides/modules/generators.md +2 -2
  34. package/dist/agentic/guides/modules/inbox_ops.md +2 -2
  35. package/dist/agentic/guides/modules/integrations.md +5 -2
  36. package/dist/agentic/guides/modules/messages.md +2 -2
  37. package/dist/agentic/guides/modules/notifications.md +2 -2
  38. package/dist/agentic/guides/modules/onboarding.md +2 -2
  39. package/dist/agentic/guides/modules/payment_gateways.md +7 -3
  40. package/dist/agentic/guides/modules/perspectives.md +2 -2
  41. package/dist/agentic/guides/modules/planner.md +2 -2
  42. package/dist/agentic/guides/modules/portal.md +2 -2
  43. package/dist/agentic/guides/modules/progress.md +2 -2
  44. package/dist/agentic/guides/modules/query_index.md +2 -2
  45. package/dist/agentic/guides/modules/record_locks.md +3 -3
  46. package/dist/agentic/guides/modules/resources.md +2 -2
  47. package/dist/agentic/guides/modules/sales.md +13 -4
  48. package/dist/agentic/guides/modules/scheduler.md +2 -2
  49. package/dist/agentic/guides/modules/search.md +4 -4
  50. package/dist/agentic/guides/modules/security.md +3 -3
  51. package/dist/agentic/guides/modules/shipping_carriers.md +2 -2
  52. package/dist/agentic/guides/modules/sso.md +2 -2
  53. package/dist/agentic/guides/modules/staff.md +7 -4
  54. package/dist/agentic/guides/modules/storage_s3.md +2 -2
  55. package/dist/agentic/guides/modules/sync_akeneo.md +4 -4
  56. package/dist/agentic/guides/modules/sync_excel.md +2 -2
  57. package/dist/agentic/guides/modules/system_status_overlays.md +2 -2
  58. package/dist/agentic/guides/modules/translations.md +2 -2
  59. package/dist/agentic/guides/modules/webhooks.md +2 -2
  60. package/dist/agentic/guides/modules/wms.md +31 -4
  61. package/dist/agentic/guides/modules/workflows.md +2 -2
  62. package/dist/agentic/guides/reference-module-facts.json +8335 -0
  63. package/dist/agentic/guides/reference-modules/example.md +489 -0
  64. package/dist/agentic/guides/spec-delivery.md +34 -0
  65. package/dist/agentic/guides/testing-debugging.md +2 -1
  66. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +3 -2
  67. package/dist/agentic/guides/upstream/manifest.json +2 -2
  68. package/dist/agentic/shared/AGENTS.md.template +10 -14
  69. package/dist/agentic/shared/ai/harness/README.md +4 -4
  70. package/dist/agentic/shared/ai/harness/RELEASE.md +4 -4
  71. package/dist/agentic/shared/ai/harness/cases.json +11350 -5088
  72. package/dist/agentic/shared/ai/harness/cases.schema.json +70 -7
  73. package/dist/agentic/shared/ai/harness/design-system-inventory.json +6697 -0
  74. package/dist/agentic/shared/ai/harness/fixtures/index.json +3 -1
  75. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +4 -0
  76. package/dist/agentic/shared/ai/harness/knowledge-change.schema.json +228 -0
  77. package/dist/agentic/shared/ai/harness/release-matrix.json +9 -4
  78. package/dist/agentic/shared/ai/harness/result.schema.json +60 -0
  79. package/dist/agentic/shared/ai/harness/routing-response.schema.json +10 -0
  80. package/dist/agentic/shared/ai/harness/source-link-inventory.json +3231 -0
  81. package/dist/agentic/shared/ai/harness/validators.json +8 -4
  82. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +5 -2
  83. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +3 -0
  84. package/dist/agentic/shared/ai/harness/writable-spec-oracles.mjs +879 -0
  85. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.7-to-0.7.0/SKILL.md +102 -0
  86. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +1 -0
  87. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +15 -0
  88. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +6 -0
  89. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +4 -0
  90. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +1 -0
  91. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +2 -0
  92. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +2 -0
  93. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +3 -1
  94. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +1 -0
  95. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +2 -0
  96. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +2 -0
  97. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +3 -3
  98. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +1 -1
  99. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/knowledge-change.md +133 -0
  100. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +1 -0
  101. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +11 -0
  102. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +1 -1
  103. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +2 -2
  104. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +14 -1
  105. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +2 -0
  106. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +19 -0
  107. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +1 -4
  108. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +1 -0
  109. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +24 -0
  110. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +3 -1
  111. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +1 -0
  112. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +13 -0
  113. package/dist/agentic/shared/ai/skills/tiers.json +2 -1
  114. package/dist/agentic/shared/ai/specs/2026-08-06-reference-module-activation.md +248 -0
  115. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +39 -2
  116. package/dist/agentic/shared/scripts/design-source-contract.mjs +108 -0
  117. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +935 -28
  118. package/dist/agentic/shared/scripts/framework-context.mjs +4 -1
  119. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1 -1
  120. package/dist/agentic/shared/scripts/validate-knowledge-change.mjs +1511 -0
  121. package/dist/lib/__integration__/TC-INT-008.spec.js +1 -0
  122. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  123. package/dist/lib/agentic-setup.js +9 -1
  124. package/dist/lib/agentic-setup.js.map +2 -2
  125. package/dist/lib/generators/module-extension-facts.js +93 -26
  126. package/dist/lib/generators/module-extension-facts.js.map +3 -3
  127. package/dist/lib/generators/module-facts-discovery.js +23 -0
  128. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  129. package/dist/lib/generators/module-facts.js +969 -41
  130. package/dist/lib/generators/module-facts.js.map +2 -2
  131. package/dist/lib/generators/module-override-targets.js +35 -11
  132. package/dist/lib/generators/module-override-targets.js.map +2 -2
  133. package/dist/lib/generators/module-registry.js +105 -2
  134. package/dist/lib/generators/module-registry.js.map +2 -2
  135. package/dist/lib/resolver.js +5 -0
  136. package/dist/lib/resolver.js.map +2 -2
  137. package/dist/lib/testing/integration-discovery.js +34 -0
  138. package/dist/lib/testing/integration-discovery.js.map +2 -2
  139. package/dist/lib/testing/integration.js +0 -2
  140. package/dist/lib/testing/integration.js.map +2 -2
  141. package/dist/mercato.js +4 -4
  142. package/dist/mercato.js.map +2 -2
  143. package/package.json +6 -6
  144. package/src/lib/__integration__/TC-INT-008.spec.ts +4 -2
  145. package/src/lib/__tests__/resolve-environment.test.ts +18 -0
  146. package/src/lib/agentic-setup.ts +10 -2
  147. package/src/lib/generators/__tests__/helpers/override-target-diagnostic-fixtures.ts +150 -0
  148. package/src/lib/generators/__tests__/module-extension-facts.test.ts +290 -0
  149. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +72 -7
  150. package/src/lib/generators/__tests__/module-facts.computed-di-token.test.ts +88 -0
  151. package/src/lib/generators/__tests__/module-facts.example-fact-coverage.test.ts +502 -0
  152. package/src/lib/generators/__tests__/module-facts.local-reference.test.ts +414 -0
  153. package/src/lib/generators/__tests__/module-facts.search-entity-id.test.ts +151 -0
  154. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +42 -1
  155. package/src/lib/generators/__tests__/module-override-targets.diagnostic-wiring.test.ts +92 -0
  156. package/src/lib/generators/__tests__/module-override-targets.test.ts +24 -0
  157. package/src/lib/generators/__tests__/module-override-targets.unknown-mode.test.ts +117 -0
  158. package/src/lib/generators/__tests__/module-subset.test.ts +60 -0
  159. package/src/lib/generators/module-extension-facts.ts +181 -25
  160. package/src/lib/generators/module-facts-discovery.ts +46 -0
  161. package/src/lib/generators/module-facts.ts +1330 -86
  162. package/src/lib/generators/module-override-targets.ts +70 -26
  163. package/src/lib/generators/module-registry.ts +135 -2
  164. package/src/lib/resolver.ts +6 -1
  165. package/src/lib/testing/__tests__/integration-discovery.test.ts +35 -0
  166. package/src/lib/testing/integration-discovery.ts +50 -0
  167. package/src/lib/testing/integration.ts +0 -2
  168. package/src/mercato.ts +3 -3
@@ -0,0 +1,92 @@
1
+ import { collectModuleOverrideTargets } from '../module-override-targets'
2
+ import {
3
+ FIXTURE_MODULE_ID,
4
+ FIXTURE_SOURCE_ROOT,
5
+ OVERRIDE_TARGET_DIAGNOSTIC_FIXTURES,
6
+ catalogWith,
7
+ catalogWithout,
8
+ collectWithCatalogOperations,
9
+ fixtureFacts,
10
+ realCatalogOperations,
11
+ } from './helpers/override-target-diagnostic-fixtures'
12
+
13
+ function codesFor(
14
+ diagnostics: { code: string; domain: string }[],
15
+ domain: string,
16
+ ): string[] {
17
+ return diagnostics.filter((diagnostic) => diagnostic.domain === domain).map((diagnostic) => diagnostic.code)
18
+ }
19
+
20
+ describe('override-target diagnostic wiring — the published code names the real cause', () => {
21
+ it('publishes unknown-framework-mode when the catalog describes the host with an unusable operation', () => {
22
+ const { overrideTargets, overrideTargetDiagnostics } = collectWithCatalogOperations(
23
+ catalogWith('cli', 'merge-deep'),
24
+ fixtureFacts(),
25
+ )
26
+ const cliDiagnostics = overrideTargetDiagnostics.filter((diagnostic) => diagnostic.domain === 'cli')
27
+ expect(cliDiagnostics.map((diagnostic) => diagnostic.code)).toEqual(['unknown-framework-mode'])
28
+ expect(cliDiagnostics[0]).toEqual({
29
+ code: 'unknown-framework-mode',
30
+ moduleId: FIXTURE_MODULE_ID,
31
+ domain: 'cli',
32
+ candidatePath: ['cli', 'fixture:seed'],
33
+ source: { sourcePath: `${FIXTURE_SOURCE_ROOT}/cli.ts` },
34
+ })
35
+ expect(overrideTargets.some((target) => target.domain === 'cli')).toBe(false)
36
+ })
37
+
38
+ it('publishes unknown-framework-domain when the catalog does not describe the host at all', () => {
39
+ const { overrideTargets, overrideTargetDiagnostics } = collectWithCatalogOperations(
40
+ catalogWithout('cli'),
41
+ fixtureFacts(),
42
+ )
43
+ expect(codesFor(overrideTargetDiagnostics, 'cli')).toEqual(['unknown-framework-domain'])
44
+ expect(overrideTargets.some((target) => target.domain === 'cli')).toBe(false)
45
+ })
46
+
47
+ it('never collapses the two causes: one run emits a distinct code per failing domain', () => {
48
+ const operations = catalogWithout('cli')
49
+ operations.workers = 'merge-deep'
50
+ const { overrideTargetDiagnostics } = collectWithCatalogOperations(
51
+ operations,
52
+ fixtureFacts({
53
+ ownedContracts: {
54
+ worker: [
55
+ {
56
+ kind: 'worker',
57
+ id: 'fixture.worker',
58
+ source: { sourcePath: `${FIXTURE_SOURCE_ROOT}/workers/fixture.ts`, line: 1 },
59
+ },
60
+ ],
61
+ },
62
+ }),
63
+ )
64
+ expect(codesFor(overrideTargetDiagnostics, 'cli')).toEqual(['unknown-framework-domain'])
65
+ expect(codesFor(overrideTargetDiagnostics, 'workers')).toEqual(['unknown-framework-mode'])
66
+ expect(new Set(overrideTargetDiagnostics.map((diagnostic) => diagnostic.code)).size).toBe(2)
67
+ })
68
+
69
+ it('emits the target and no diagnostic when the real catalog describes the host', () => {
70
+ const stubbed = collectWithCatalogOperations(realCatalogOperations(), fixtureFacts())
71
+ const direct = collectModuleOverrideTargets(fixtureFacts())
72
+ expect(stubbed).toEqual(direct)
73
+ expect(direct.overrideTargetDiagnostics).toEqual([])
74
+ expect(direct.overrideTargets.map((target) => target.path)).toEqual([['cli', 'fixture:seed']])
75
+ expect(direct.overrideTargets[0].modes).toEqual(['disable-replace'])
76
+ })
77
+ })
78
+
79
+ describe('every negative-fixture diagnostic code is produced by a real fixture', () => {
80
+ it.each(OVERRIDE_TARGET_DIAGNOSTIC_FIXTURES.map((fixture) => [fixture.code, fixture] as const))(
81
+ '%s',
82
+ (code, fixture) => {
83
+ const diagnostics = fixture.run()
84
+ expect(diagnostics.map((diagnostic) => diagnostic.code)).toContain(code)
85
+ },
86
+ )
87
+
88
+ it('covers each code with exactly one fixture', () => {
89
+ const codes = OVERRIDE_TARGET_DIAGNOSTIC_FIXTURES.map((fixture) => fixture.code)
90
+ expect(new Set(codes).size).toBe(codes.length)
91
+ })
92
+ })
@@ -222,6 +222,30 @@ describe('module override targets — deterministic diagnostics (never a guessed
222
222
  expect(diag?.source).toBeUndefined()
223
223
  })
224
224
 
225
+ it('never guesses a mode: the framework catalog describes every adapter host', () => {
226
+ const { overrideTargets, overrideTargetDiagnostics } = collectModuleOverrideTargets(baseFacts())
227
+ expect(overrideTargetDiagnostics.filter((diagnostic) => diagnostic.code === 'unknown-framework-domain')).toEqual([])
228
+ expect(overrideTargets.length).toBeGreaterThan(0)
229
+ for (const target of overrideTargets) {
230
+ expect(target.modes).toHaveLength(1)
231
+ expect(['disable-replace', 'replace', 'additive']).toContain(target.modes[0])
232
+ }
233
+ })
234
+
235
+ it('routes every widget contribution by its own kind/payload discriminant', () => {
236
+ const { overrideTargets } = collectModuleOverrideTargets(baseFacts())
237
+ const widgets = overrideTargets
238
+ .filter((target) => target.domain === 'widgets')
239
+ .map((target) => `${target.path.slice(0, 2).join('/')} ${target.key}`)
240
+ .sort()
241
+ expect(widgets).toEqual([
242
+ 'widgets/components section:demo.login',
243
+ 'widgets/dashboard demo.card',
244
+ 'widgets/injection demo.table-widget',
245
+ 'widgets/injection demo.widget',
246
+ ])
247
+ })
248
+
225
249
  it('emits missing-source when an acl feature has no indexed provenance', () => {
226
250
  const facts = baseFacts({ factSources: [] })
227
251
  const { overrideTargets, overrideTargetDiagnostics } = collectModuleOverrideTargets(facts)
@@ -0,0 +1,117 @@
1
+ import type { ModuleExtensionHostFact } from '@open-mercato/shared/modules/widgets/extension-points'
2
+ import { getFrameworkOverrideHostOperations, getFrameworkOverrideModes } from '../module-extension-facts'
3
+ import {
4
+ MODULE_OVERRIDE_MODES,
5
+ MODULE_OVERRIDE_TARGET_DIAGNOSTIC_CODES,
6
+ resolveFrameworkOverrideModes,
7
+ } from '../module-override-targets'
8
+
9
+ /**
10
+ * A catalog host declaring an operation this generator does not know. Every real
11
+ * catalog host declares a valid mode today, so a stub is the only way to observe
12
+ * what the raw operation map does with one — which is the whole point of the map
13
+ * existing beside the validated mode map.
14
+ */
15
+ const CATALOG_HOST_WITH_UNRECOGNIZED_OPERATION: ModuleExtensionHostFact = {
16
+ key: 'framework.module-override.fixture.aheadOfGenerator',
17
+ id: 'module-override:fixture.aheadOfGenerator',
18
+ resolution: 'framework',
19
+ family: 'module-override',
20
+ ownerModule: 'framework',
21
+ capabilities: ['module-override'],
22
+ operations: ['merge-deep'],
23
+ bound: true,
24
+ stability: 'stable',
25
+ source: {
26
+ kind: 'framework',
27
+ path: 'packages/shared/src/modules/overrides.ts',
28
+ symbol: 'ModuleOverrides.fixture.aheadOfGenerator',
29
+ },
30
+ }
31
+
32
+ describe('unknown-framework-mode override-target diagnostic', () => {
33
+ it('is part of the closed diagnostic-code set', () => {
34
+ expect([...MODULE_OVERRIDE_TARGET_DIAGNOSTIC_CODES]).toEqual([
35
+ 'missing-owned-fact',
36
+ 'missing-source',
37
+ 'unsupported-dynamic-key',
38
+ 'unknown-framework-domain',
39
+ 'unknown-framework-mode',
40
+ ])
41
+ })
42
+
43
+ it('distinguishes an absent catalog host from a host whose declared mode is unrecognized', () => {
44
+ expect(resolveFrameworkOverrideModes(undefined)).toEqual({ outcome: 'unknown-framework-domain' })
45
+ expect(resolveFrameworkOverrideModes('merge-deep')).toEqual({
46
+ outcome: 'unknown-framework-mode',
47
+ operation: 'merge-deep',
48
+ })
49
+ })
50
+
51
+ it('resolves every public mode and nothing else', () => {
52
+ for (const mode of MODULE_OVERRIDE_MODES) {
53
+ expect(resolveFrameworkOverrideModes(mode)).toEqual({ outcome: 'resolved', modes: [mode] })
54
+ }
55
+ expect(resolveFrameworkOverrideModes('')).toEqual({ outcome: 'unknown-framework-mode', operation: '' })
56
+ expect(resolveFrameworkOverrideModes('DISABLE-REPLACE')).toEqual({
57
+ outcome: 'unknown-framework-mode',
58
+ operation: 'DISABLE-REPLACE',
59
+ })
60
+ })
61
+
62
+ it('never guesses a target: an unrecognized mode yields no modes at all', () => {
63
+ const resolution = resolveFrameworkOverrideModes('replace-all')
64
+ expect(resolution.outcome).toBe('unknown-framework-mode')
65
+ expect('modes' in resolution).toBe(false)
66
+ })
67
+ })
68
+
69
+ describe('framework override host operations', () => {
70
+ const operations = getFrameworkOverrideHostOperations()
71
+
72
+ it('exposes the raw declared operation for every catalog host, unfiltered', () => {
73
+ expect(Object.keys(operations).length).toBeGreaterThan(0)
74
+ expect(operations['routes.api']).toBe('disable-replace')
75
+ expect(operations['setup']).toBe('replace')
76
+ expect(operations['ai.extensions']).toBe('additive')
77
+ expect(operations['nav.groupOrder']).toBe('additive')
78
+ expect(operations['no.such.host']).toBeUndefined()
79
+ })
80
+
81
+ it('keeps the validated mode map a strict subset of the raw operations', () => {
82
+ const modes = getFrameworkOverrideModes()
83
+ for (const [host, mode] of Object.entries(modes)) {
84
+ expect(operations[host]).toBe(mode)
85
+ expect(MODULE_OVERRIDE_MODES).toContain(mode)
86
+ }
87
+ for (const [host, operation] of Object.entries(operations)) {
88
+ const resolution = resolveFrameworkOverrideModes(operation)
89
+ expect(resolution.outcome === 'resolved').toBe(host in modes)
90
+ }
91
+ })
92
+ })
93
+
94
+ describe('a catalog operation the generator does not recognize', () => {
95
+ const hosts = [CATALOG_HOST_WITH_UNRECOGNIZED_OPERATION]
96
+ const dottedHost = 'fixture.aheadOfGenerator'
97
+
98
+ it('survives the raw operation map unfiltered', () => {
99
+ expect(getFrameworkOverrideHostOperations(hosts)).toEqual({ [dottedHost]: 'merge-deep' })
100
+ })
101
+
102
+ it('is dropped from the validated mode map, which is exactly why the raw map must not filter', () => {
103
+ expect(getFrameworkOverrideModes(hosts)).toEqual({})
104
+ })
105
+
106
+ it('reads as a catalog this generator has fallen behind, never as a host that does not exist', () => {
107
+ const rawOperations = getFrameworkOverrideHostOperations(hosts)
108
+ const filteredOperations = getFrameworkOverrideModes(hosts)
109
+ expect(resolveFrameworkOverrideModes(rawOperations[dottedHost])).toEqual({
110
+ outcome: 'unknown-framework-mode',
111
+ operation: 'merge-deep',
112
+ })
113
+ expect(resolveFrameworkOverrideModes(filteredOperations[dottedHost])).toEqual({
114
+ outcome: 'unknown-framework-domain',
115
+ })
116
+ })
117
+ })
@@ -1908,6 +1908,66 @@ describe('all generated files are valid with varying subsets', () => {
1908
1908
  expect(sudoTargets).toContain('sudoTargets')
1909
1909
  })
1910
1910
 
1911
+ it('rejects generator plugins that declare the same output file', async () => {
1912
+ scaffoldModule(tmpDir, 'security_ext', 'pkg', ['security.mfa-providers.ts'])
1913
+ touchFile(
1914
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'security_ext', 'generators.ts'),
1915
+ SECURITY_GENERATORS_CONTENT.replace(
1916
+ "outputFileName: 'security-sudo.generated.ts'",
1917
+ "outputFileName: 'security-mfa-providers.generated.ts'",
1918
+ ),
1919
+ )
1920
+ const resolver = createMockResolver(tmpDir, [
1921
+ { id: 'security_ext', from: '@open-mercato/core' },
1922
+ ])
1923
+
1924
+ await expect(generateModuleRegistry({ resolver, quiet: true })).rejects.toThrow(
1925
+ "declare the same output file 'security-mfa-providers.generated.ts'",
1926
+ )
1927
+ })
1928
+
1929
+ it('rejects runtime imports in generators.ts before loading the plugin', async () => {
1930
+ scaffoldModule(tmpDir, 'security_ext', 'pkg', ['security.mfa-providers.ts'])
1931
+ touchFile(
1932
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'security_ext', 'generators.ts'),
1933
+ `import { randomUUID } from 'node:crypto'\n${SECURITY_GENERATORS_CONTENT}`,
1934
+ )
1935
+ const resolver = createMockResolver(tmpDir, [
1936
+ { id: 'security_ext', from: '@open-mercato/core' },
1937
+ ])
1938
+
1939
+ await expect(generateModuleRegistry({ resolver, quiet: true })).rejects.toThrow(
1940
+ 'generators.ts may use only `import type`',
1941
+ )
1942
+ })
1943
+
1944
+ it('repairs stale plugin output and removes it when the declaring module is disabled', async () => {
1945
+ scaffoldModule(tmpDir, 'security_ext', 'pkg', ['security.mfa-providers.ts'])
1946
+ touchFile(
1947
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'security_ext', 'generators.ts'),
1948
+ SECURITY_GENERATORS_CONTENT,
1949
+ )
1950
+ const activeResolver = createMockResolver(tmpDir, [
1951
+ { id: 'security_ext', from: '@open-mercato/core' },
1952
+ ])
1953
+ await generateModuleRegistry({ resolver: activeResolver, quiet: true })
1954
+
1955
+ const outputPath = path.join(tmpDir, 'output', 'generated', 'security-mfa-providers.generated.ts')
1956
+ const checksumPath = path.join(tmpDir, 'output', 'generated', 'security-mfa-providers.generated.checksum')
1957
+ const manifestPath = path.join(tmpDir, 'output', 'generated', '.generator-plugin-outputs.json')
1958
+ const expected = fs.readFileSync(outputPath, 'utf8')
1959
+ fs.writeFileSync(outputPath, 'stale output\n')
1960
+
1961
+ await generateModuleRegistry({ resolver: activeResolver, quiet: true })
1962
+ expect(fs.readFileSync(outputPath, 'utf8')).toBe(expected)
1963
+
1964
+ const disabledResolver = createMockResolver(tmpDir, [])
1965
+ await generateModuleRegistry({ resolver: disabledResolver, quiet: true })
1966
+ expect(fs.existsSync(outputPath)).toBe(false)
1967
+ expect(fs.existsSync(checksumPath)).toBe(false)
1968
+ expect(fs.existsSync(manifestPath)).toBe(false)
1969
+ })
1970
+
1911
1971
  it('notifications.generated.ts uses typed fallback for legacy "types" export', async () => {
1912
1972
  scaffoldModule(tmpDir, 'notif_mod', 'pkg', ['notifications.ts'])
1913
1973
  const resolver = createMockResolver(tmpDir, [