@bleedingdev/modern-js-create 3.5.0-ultramodern.9 → 3.5.0-ultramodern.90

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 (566) hide show
  1. package/README.md +25 -25
  2. package/bin/run.js +0 -0
  3. package/dist/cjs/cli/flags.cjs +357 -0
  4. package/dist/cjs/cli/help.cjs +108 -0
  5. package/dist/cjs/cli/package-source.cjs +188 -0
  6. package/dist/cjs/cli/project-setup.cjs +162 -0
  7. package/dist/cjs/cli/prompts.cjs +100 -0
  8. package/dist/cjs/index.cjs +35 -585
  9. package/dist/cjs/locale/en.cjs +5 -6
  10. package/dist/cjs/locale/zh.cjs +5 -6
  11. package/dist/cjs/ultramodern-package-source.cjs +0 -32
  12. package/dist/cjs/ultramodern-release-cohort.cjs +206 -0
  13. package/dist/cjs/ultramodern-tooling/commands/cloudflare-output-verify.cjs +136 -0
  14. package/dist/cjs/ultramodern-tooling/commands/context.cjs +131 -0
  15. package/dist/cjs/ultramodern-tooling/commands/mf-types.cjs +55 -0
  16. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.cjs +212 -0
  17. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.cjs +50 -0
  18. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.cjs +87 -0
  19. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.cjs +58 -0
  20. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.cjs +100 -0
  21. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.cjs +100 -0
  22. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.cjs +132 -0
  23. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.cjs +67 -0
  24. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +206 -0
  25. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.cjs +60 -0
  26. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.cjs +67 -0
  27. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.cjs +67 -0
  28. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.cjs +148 -0
  29. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/help.cjs +54 -0
  30. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/install.cjs +52 -0
  31. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/io.cjs +350 -0
  32. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +200 -0
  33. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-source.cjs +89 -0
  34. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.cjs +476 -0
  35. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.cjs +376 -0
  36. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.cjs +99 -0
  37. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.cjs +135 -0
  38. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect.cjs +425 -0
  39. package/dist/cjs/ultramodern-tooling/commands/options.cjs +59 -0
  40. package/dist/cjs/ultramodern-tooling/commands/routes-generate.cjs +122 -0
  41. package/dist/cjs/ultramodern-tooling/commands/skills.cjs +49 -0
  42. package/dist/cjs/ultramodern-tooling/commands/sync-delivery-unit.cjs +40 -0
  43. package/dist/cjs/ultramodern-tooling/commands/validate.cjs +53 -0
  44. package/dist/cjs/ultramodern-tooling/commands.cjs +27 -491
  45. package/dist/cjs/ultramodern-tooling/config/constants.cjs +48 -0
  46. package/dist/cjs/ultramodern-tooling/config/index.cjs +104 -0
  47. package/dist/cjs/ultramodern-tooling/config/json.cjs +61 -0
  48. package/dist/cjs/ultramodern-tooling/config/load.cjs +62 -0
  49. package/dist/cjs/ultramodern-tooling/config/metadata.cjs +92 -0
  50. package/dist/cjs/ultramodern-tooling/config/normalize.cjs +236 -0
  51. package/dist/cjs/ultramodern-tooling/config/synthesize.cjs +157 -0
  52. package/dist/cjs/ultramodern-tooling/config/types.cjs +18 -0
  53. package/dist/cjs/ultramodern-tooling/config.cjs +23 -133
  54. package/dist/cjs/ultramodern-workspace/add-shell.cjs +274 -0
  55. package/dist/cjs/ultramodern-workspace/add-vertical/constants.cjs +52 -0
  56. package/dist/cjs/ultramodern-workspace/add-vertical/execute.cjs +183 -0
  57. package/dist/cjs/ultramodern-workspace/add-vertical/plan.cjs +255 -0
  58. package/dist/cjs/ultramodern-workspace/add-vertical/preflight.cjs +256 -0
  59. package/dist/cjs/ultramodern-workspace/add-vertical/shell-files.cjs +122 -0
  60. package/dist/cjs/ultramodern-workspace/add-vertical/topology.cjs +152 -0
  61. package/dist/cjs/ultramodern-workspace/add-vertical/transaction.cjs +327 -0
  62. package/dist/cjs/ultramodern-workspace/add-vertical/workspace-state.cjs +137 -0
  63. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +22 -571
  64. package/dist/cjs/ultramodern-workspace/api/checkout-cart.cjs +145 -0
  65. package/dist/cjs/ultramodern-workspace/api/client.cjs +224 -0
  66. package/dist/cjs/ultramodern-workspace/api/contracts.cjs +251 -0
  67. package/dist/cjs/ultramodern-workspace/api/names.cjs +98 -0
  68. package/dist/cjs/ultramodern-workspace/api/rpc.cjs +264 -0
  69. package/dist/cjs/ultramodern-workspace/api/service.cjs +252 -0
  70. package/dist/cjs/ultramodern-workspace/api/shared.cjs +270 -0
  71. package/dist/cjs/ultramodern-workspace/api.cjs +49 -1005
  72. package/dist/cjs/ultramodern-workspace/app-files.cjs +51 -69
  73. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +346 -0
  74. package/dist/cjs/ultramodern-workspace/bridge-config/cli.cjs +71 -0
  75. package/dist/cjs/ultramodern-workspace/bridge-config/defaults.cjs +43 -0
  76. package/dist/cjs/ultramodern-workspace/bridge-config/gates.cjs +93 -0
  77. package/dist/cjs/ultramodern-workspace/bridge-config/index.cjs +60 -0
  78. package/dist/cjs/ultramodern-workspace/bridge-config/normalize.cjs +68 -0
  79. package/dist/cjs/ultramodern-workspace/bridge-config/schema.cjs +78 -0
  80. package/dist/cjs/ultramodern-workspace/bridge-config/shared.cjs +110 -0
  81. package/dist/cjs/ultramodern-workspace/bridge-config/validation.cjs +68 -0
  82. package/dist/cjs/ultramodern-workspace/bridge-config/workspace-packages.cjs +108 -0
  83. package/dist/cjs/ultramodern-workspace/bridge-config.cjs +32 -297
  84. package/dist/cjs/ultramodern-workspace/codesmith.cjs +10 -1
  85. package/dist/cjs/ultramodern-workspace/contracts.cjs +80 -26
  86. package/dist/cjs/ultramodern-workspace/delivery-unit-schema/types.cjs +291 -0
  87. package/dist/cjs/ultramodern-workspace/delivery-unit-schema/up-projection.cjs +188 -0
  88. package/dist/cjs/ultramodern-workspace/delivery-unit-stamp.cjs +56 -0
  89. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +153 -0
  90. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +75 -0
  91. package/dist/cjs/ultramodern-workspace/demo-components.cjs +474 -205
  92. package/dist/cjs/ultramodern-workspace/descriptors.cjs +101 -16
  93. package/dist/cjs/ultramodern-workspace/effect-diagnostics.cjs +114 -0
  94. package/dist/cjs/ultramodern-workspace/fs-io.cjs +26 -36
  95. package/dist/cjs/ultramodern-workspace/generation-result.cjs +36 -3
  96. package/dist/cjs/ultramodern-workspace/index.cjs +28 -0
  97. package/dist/cjs/ultramodern-workspace/locales.cjs +3 -17
  98. package/dist/cjs/ultramodern-workspace/mf-validation/constants.cjs +69 -0
  99. package/dist/cjs/ultramodern-workspace/mf-validation/discovery.cjs +77 -0
  100. package/dist/cjs/ultramodern-workspace/mf-validation/inspect.cjs +84 -0
  101. package/dist/cjs/ultramodern-workspace/mf-validation/object-literal.cjs +188 -0
  102. package/dist/cjs/ultramodern-workspace/mf-validation/path-utils.cjs +165 -0
  103. package/dist/cjs/ultramodern-workspace/mf-validation/syntax.cjs +201 -0
  104. package/dist/cjs/ultramodern-workspace/mf-validation/types.cjs +18 -0
  105. package/dist/cjs/ultramodern-workspace/mf-validation/validate.cjs +112 -0
  106. package/dist/cjs/ultramodern-workspace/mf-validation.cjs +6 -529
  107. package/dist/cjs/ultramodern-workspace/module-federation/config.cjs +336 -0
  108. package/dist/cjs/ultramodern-workspace/module-federation/index.cjs +73 -0
  109. package/dist/cjs/ultramodern-workspace/module-federation/reexport-module.cjs +111 -0
  110. package/dist/cjs/ultramodern-workspace/module-federation/remote-refs.cjs +106 -0
  111. package/dist/cjs/ultramodern-workspace/module-federation/shared-config.cjs +53 -0
  112. package/dist/cjs/ultramodern-workspace/module-federation.cjs +18 -477
  113. package/dist/cjs/ultramodern-workspace/naming.cjs +1 -7
  114. package/dist/cjs/ultramodern-workspace/overlay-baseline-guard.cjs +408 -0
  115. package/dist/cjs/ultramodern-workspace/overlays.cjs +39 -5
  116. package/dist/cjs/ultramodern-workspace/package-json.cjs +54 -301
  117. package/dist/cjs/ultramodern-workspace/package-source.cjs +11 -2
  118. package/dist/cjs/ultramodern-workspace/policy.cjs +801 -24
  119. package/dist/cjs/ultramodern-workspace/public-surface.cjs +1 -22
  120. package/dist/cjs/ultramodern-workspace/routes.cjs +8 -157
  121. package/dist/cjs/ultramodern-workspace/shared-patches.cjs +41 -0
  122. package/dist/cjs/ultramodern-workspace/shells.cjs +151 -0
  123. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +147 -0
  124. package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +200 -0
  125. package/dist/cjs/ultramodern-workspace/types.cjs +27 -0
  126. package/dist/cjs/ultramodern-workspace/versions.cjs +52 -37
  127. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +174 -0
  128. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +118 -88
  129. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +642 -0
  130. package/dist/cjs/ultramodern-workspace/write-app.cjs +148 -0
  131. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +45 -67
  132. package/dist/cjs/ultramodern-workspace/zerops.cjs +108 -0
  133. package/dist/esm/cli/flags.js +262 -0
  134. package/dist/esm/cli/help.js +67 -0
  135. package/dist/esm/cli/package-source.js +124 -0
  136. package/dist/esm/cli/project-setup.js +105 -0
  137. package/dist/esm/cli/prompts.js +51 -0
  138. package/dist/esm/index.js +19 -565
  139. package/dist/esm/locale/en.js +5 -6
  140. package/dist/esm/locale/zh.js +5 -6
  141. package/dist/esm/ultramodern-package-source.js +1 -24
  142. package/dist/esm/ultramodern-release-cohort.js +133 -0
  143. package/dist/esm/ultramodern-tooling/commands/cloudflare-output-verify.js +88 -0
  144. package/dist/esm/ultramodern-tooling/commands/context.js +66 -0
  145. package/dist/esm/ultramodern-tooling/commands/mf-types.js +17 -0
  146. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +154 -0
  147. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +12 -0
  148. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +38 -0
  149. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +10 -0
  150. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +45 -0
  151. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +51 -0
  152. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +83 -0
  153. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +16 -0
  154. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +157 -0
  155. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +12 -0
  156. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +7 -0
  157. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +15 -0
  158. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +97 -0
  159. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/help.js +16 -0
  160. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/install.js +14 -0
  161. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/io.js +285 -0
  162. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +156 -0
  163. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +51 -0
  164. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +417 -0
  165. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +329 -0
  166. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +24 -0
  167. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +80 -0
  168. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect.js +376 -0
  169. package/dist/esm/ultramodern-tooling/commands/options.js +18 -0
  170. package/dist/esm/ultramodern-tooling/commands/routes-generate.js +74 -0
  171. package/dist/esm/ultramodern-tooling/commands/skills.js +11 -0
  172. package/dist/esm/ultramodern-tooling/commands/sync-delivery-unit.js +1 -0
  173. package/dist/esm/ultramodern-tooling/commands/validate.js +15 -0
  174. package/dist/esm/ultramodern-tooling/commands.js +23 -482
  175. package/dist/esm/ultramodern-tooling/config/constants.js +4 -0
  176. package/dist/esm/ultramodern-tooling/config/index.js +7 -0
  177. package/dist/esm/ultramodern-tooling/config/json.js +10 -0
  178. package/dist/esm/ultramodern-tooling/config/load.js +13 -0
  179. package/dist/esm/ultramodern-tooling/config/metadata.js +37 -0
  180. package/dist/esm/ultramodern-tooling/config/normalize.js +173 -0
  181. package/dist/esm/ultramodern-tooling/config/synthesize.js +108 -0
  182. package/dist/esm/ultramodern-tooling/config/types.js +0 -0
  183. package/dist/esm/ultramodern-tooling/config.js +1 -120
  184. package/dist/esm/ultramodern-workspace/add-shell.js +221 -0
  185. package/dist/esm/ultramodern-workspace/add-vertical/constants.js +5 -0
  186. package/dist/esm/ultramodern-workspace/add-vertical/execute.js +135 -0
  187. package/dist/esm/ultramodern-workspace/add-vertical/plan.js +205 -0
  188. package/dist/esm/ultramodern-workspace/add-vertical/preflight.js +198 -0
  189. package/dist/esm/ultramodern-workspace/add-vertical/shell-files.js +65 -0
  190. package/dist/esm/ultramodern-workspace/add-vertical/topology.js +108 -0
  191. package/dist/esm/ultramodern-workspace/add-vertical/transaction.js +254 -0
  192. package/dist/esm/ultramodern-workspace/add-vertical/workspace-state.js +67 -0
  193. package/dist/esm/ultramodern-workspace/add-vertical.js +6 -544
  194. package/dist/esm/ultramodern-workspace/api/checkout-cart.js +86 -0
  195. package/dist/esm/ultramodern-workspace/api/client.js +183 -0
  196. package/dist/esm/ultramodern-workspace/api/contracts.js +201 -0
  197. package/dist/esm/ultramodern-workspace/api/names.js +33 -0
  198. package/dist/esm/ultramodern-workspace/api/rpc.js +205 -0
  199. package/dist/esm/ultramodern-workspace/api/service.js +211 -0
  200. package/dist/esm/ultramodern-workspace/api/shared.js +226 -0
  201. package/dist/esm/ultramodern-workspace/api.js +6 -984
  202. package/dist/esm/ultramodern-workspace/app-files.js +51 -51
  203. package/dist/esm/ultramodern-workspace/backend-federation.js +281 -0
  204. package/dist/esm/ultramodern-workspace/bridge-config/cli.js +30 -0
  205. package/dist/esm/ultramodern-workspace/bridge-config/defaults.js +5 -0
  206. package/dist/esm/ultramodern-workspace/bridge-config/gates.js +52 -0
  207. package/dist/esm/ultramodern-workspace/bridge-config/index.js +3 -0
  208. package/dist/esm/ultramodern-workspace/bridge-config/normalize.js +30 -0
  209. package/dist/esm/ultramodern-workspace/bridge-config/schema.js +31 -0
  210. package/dist/esm/ultramodern-workspace/bridge-config/shared.js +51 -0
  211. package/dist/esm/ultramodern-workspace/bridge-config/validation.js +27 -0
  212. package/dist/esm/ultramodern-workspace/bridge-config/workspace-packages.js +67 -0
  213. package/dist/esm/ultramodern-workspace/bridge-config.js +1 -270
  214. package/dist/esm/ultramodern-workspace/codesmith.js +10 -1
  215. package/dist/esm/ultramodern-workspace/contracts.js +81 -27
  216. package/dist/esm/ultramodern-workspace/delivery-unit-schema/types.js +235 -0
  217. package/dist/esm/ultramodern-workspace/delivery-unit-schema/up-projection.js +129 -0
  218. package/dist/esm/ultramodern-workspace/delivery-unit-stamp.js +18 -0
  219. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +104 -0
  220. package/dist/esm/ultramodern-workspace/delivery-unit.js +21 -0
  221. package/dist/esm/ultramodern-workspace/demo-components.js +461 -204
  222. package/dist/esm/ultramodern-workspace/descriptors.js +75 -17
  223. package/dist/esm/ultramodern-workspace/effect-diagnostics.js +76 -0
  224. package/dist/esm/ultramodern-workspace/fs-io.js +27 -18
  225. package/dist/esm/ultramodern-workspace/generation-result.js +33 -3
  226. package/dist/esm/ultramodern-workspace/index.js +4 -0
  227. package/dist/esm/ultramodern-workspace/locales.js +3 -4
  228. package/dist/esm/ultramodern-workspace/mf-validation/constants.js +19 -0
  229. package/dist/esm/ultramodern-workspace/mf-validation/discovery.js +28 -0
  230. package/dist/esm/ultramodern-workspace/mf-validation/inspect.js +46 -0
  231. package/dist/esm/ultramodern-workspace/mf-validation/object-literal.js +144 -0
  232. package/dist/esm/ultramodern-workspace/mf-validation/path-utils.js +89 -0
  233. package/dist/esm/ultramodern-workspace/mf-validation/syntax.js +154 -0
  234. package/dist/esm/ultramodern-workspace/mf-validation/types.js +0 -0
  235. package/dist/esm/ultramodern-workspace/mf-validation/validate.js +57 -0
  236. package/dist/esm/ultramodern-workspace/mf-validation.js +3 -516
  237. package/dist/esm/ultramodern-workspace/module-federation/config.js +289 -0
  238. package/dist/esm/ultramodern-workspace/module-federation/index.js +4 -0
  239. package/dist/esm/ultramodern-workspace/module-federation/reexport-module.js +67 -0
  240. package/dist/esm/ultramodern-workspace/module-federation/remote-refs.js +65 -0
  241. package/dist/esm/ultramodern-workspace/module-federation/shared-config.js +12 -0
  242. package/dist/esm/ultramodern-workspace/module-federation.js +2 -458
  243. package/dist/esm/ultramodern-workspace/naming.js +2 -2
  244. package/dist/esm/ultramodern-workspace/overlay-baseline-guard.js +350 -0
  245. package/dist/esm/ultramodern-workspace/overlays.js +28 -5
  246. package/dist/esm/ultramodern-workspace/package-json.js +48 -283
  247. package/dist/esm/ultramodern-workspace/package-source.js +7 -1
  248. package/dist/esm/ultramodern-workspace/policy.js +784 -10
  249. package/dist/esm/ultramodern-workspace/public-surface.js +2 -2
  250. package/dist/esm/ultramodern-workspace/routes.js +9 -143
  251. package/dist/esm/ultramodern-workspace/shared-patches.js +3 -0
  252. package/dist/esm/ultramodern-workspace/shells.js +92 -0
  253. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
  254. package/dist/esm/ultramodern-workspace/tsconfigs.js +137 -0
  255. package/dist/esm/ultramodern-workspace/types.js +16 -1
  256. package/dist/esm/ultramodern-workspace/versions.js +35 -26
  257. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +124 -0
  258. package/dist/esm/ultramodern-workspace/workspace-scripts.js +115 -85
  259. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +604 -0
  260. package/dist/esm/ultramodern-workspace/write-app.js +105 -0
  261. package/dist/esm/ultramodern-workspace/write-workspace.js +47 -66
  262. package/dist/esm/ultramodern-workspace/zerops.js +70 -0
  263. package/dist/esm-node/cli/flags.js +263 -0
  264. package/dist/esm-node/cli/help.js +68 -0
  265. package/dist/esm-node/cli/package-source.js +125 -0
  266. package/dist/esm-node/cli/project-setup.js +106 -0
  267. package/dist/esm-node/cli/prompts.js +52 -0
  268. package/dist/esm-node/index.js +19 -565
  269. package/dist/esm-node/locale/en.js +5 -6
  270. package/dist/esm-node/locale/zh.js +5 -6
  271. package/dist/esm-node/ultramodern-package-source.js +1 -24
  272. package/dist/esm-node/ultramodern-release-cohort.js +134 -0
  273. package/dist/esm-node/ultramodern-tooling/commands/cloudflare-output-verify.js +89 -0
  274. package/dist/esm-node/ultramodern-tooling/commands/context.js +67 -0
  275. package/dist/esm-node/ultramodern-tooling/commands/mf-types.js +18 -0
  276. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +155 -0
  277. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +13 -0
  278. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +39 -0
  279. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +11 -0
  280. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +46 -0
  281. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +52 -0
  282. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +84 -0
  283. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +17 -0
  284. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +158 -0
  285. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +13 -0
  286. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +8 -0
  287. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +16 -0
  288. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +98 -0
  289. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/help.js +17 -0
  290. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/install.js +15 -0
  291. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/io.js +286 -0
  292. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +157 -0
  293. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +52 -0
  294. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +418 -0
  295. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +330 -0
  296. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +25 -0
  297. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +81 -0
  298. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect.js +377 -0
  299. package/dist/esm-node/ultramodern-tooling/commands/options.js +19 -0
  300. package/dist/esm-node/ultramodern-tooling/commands/routes-generate.js +75 -0
  301. package/dist/esm-node/ultramodern-tooling/commands/skills.js +12 -0
  302. package/dist/esm-node/ultramodern-tooling/commands/sync-delivery-unit.js +2 -0
  303. package/dist/esm-node/ultramodern-tooling/commands/validate.js +16 -0
  304. package/dist/esm-node/ultramodern-tooling/commands.js +23 -482
  305. package/dist/esm-node/ultramodern-tooling/config/constants.js +5 -0
  306. package/dist/esm-node/ultramodern-tooling/config/index.js +8 -0
  307. package/dist/esm-node/ultramodern-tooling/config/json.js +11 -0
  308. package/dist/esm-node/ultramodern-tooling/config/load.js +14 -0
  309. package/dist/esm-node/ultramodern-tooling/config/metadata.js +38 -0
  310. package/dist/esm-node/ultramodern-tooling/config/normalize.js +174 -0
  311. package/dist/esm-node/ultramodern-tooling/config/synthesize.js +109 -0
  312. package/dist/esm-node/ultramodern-tooling/config/types.js +1 -0
  313. package/dist/esm-node/ultramodern-tooling/config.js +1 -120
  314. package/dist/esm-node/ultramodern-workspace/add-shell.js +222 -0
  315. package/dist/esm-node/ultramodern-workspace/add-vertical/constants.js +6 -0
  316. package/dist/esm-node/ultramodern-workspace/add-vertical/execute.js +136 -0
  317. package/dist/esm-node/ultramodern-workspace/add-vertical/plan.js +206 -0
  318. package/dist/esm-node/ultramodern-workspace/add-vertical/preflight.js +199 -0
  319. package/dist/esm-node/ultramodern-workspace/add-vertical/shell-files.js +66 -0
  320. package/dist/esm-node/ultramodern-workspace/add-vertical/topology.js +109 -0
  321. package/dist/esm-node/ultramodern-workspace/add-vertical/transaction.js +255 -0
  322. package/dist/esm-node/ultramodern-workspace/add-vertical/workspace-state.js +68 -0
  323. package/dist/esm-node/ultramodern-workspace/add-vertical.js +6 -544
  324. package/dist/esm-node/ultramodern-workspace/api/checkout-cart.js +87 -0
  325. package/dist/esm-node/ultramodern-workspace/api/client.js +184 -0
  326. package/dist/esm-node/ultramodern-workspace/api/contracts.js +202 -0
  327. package/dist/esm-node/ultramodern-workspace/api/names.js +34 -0
  328. package/dist/esm-node/ultramodern-workspace/api/rpc.js +206 -0
  329. package/dist/esm-node/ultramodern-workspace/api/service.js +212 -0
  330. package/dist/esm-node/ultramodern-workspace/api/shared.js +227 -0
  331. package/dist/esm-node/ultramodern-workspace/api.js +6 -984
  332. package/dist/esm-node/ultramodern-workspace/app-files.js +51 -51
  333. package/dist/esm-node/ultramodern-workspace/backend-federation.js +282 -0
  334. package/dist/esm-node/ultramodern-workspace/bridge-config/cli.js +31 -0
  335. package/dist/esm-node/ultramodern-workspace/bridge-config/defaults.js +6 -0
  336. package/dist/esm-node/ultramodern-workspace/bridge-config/gates.js +53 -0
  337. package/dist/esm-node/ultramodern-workspace/bridge-config/index.js +4 -0
  338. package/dist/esm-node/ultramodern-workspace/bridge-config/normalize.js +31 -0
  339. package/dist/esm-node/ultramodern-workspace/bridge-config/schema.js +32 -0
  340. package/dist/esm-node/ultramodern-workspace/bridge-config/shared.js +52 -0
  341. package/dist/esm-node/ultramodern-workspace/bridge-config/validation.js +28 -0
  342. package/dist/esm-node/ultramodern-workspace/bridge-config/workspace-packages.js +68 -0
  343. package/dist/esm-node/ultramodern-workspace/bridge-config.js +1 -270
  344. package/dist/esm-node/ultramodern-workspace/codesmith.js +10 -1
  345. package/dist/esm-node/ultramodern-workspace/contracts.js +81 -27
  346. package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/types.js +236 -0
  347. package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/up-projection.js +130 -0
  348. package/dist/esm-node/ultramodern-workspace/delivery-unit-stamp.js +19 -0
  349. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +105 -0
  350. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +22 -0
  351. package/dist/esm-node/ultramodern-workspace/demo-components.js +461 -204
  352. package/dist/esm-node/ultramodern-workspace/descriptors.js +75 -17
  353. package/dist/esm-node/ultramodern-workspace/effect-diagnostics.js +77 -0
  354. package/dist/esm-node/ultramodern-workspace/fs-io.js +27 -18
  355. package/dist/esm-node/ultramodern-workspace/generation-result.js +33 -3
  356. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  357. package/dist/esm-node/ultramodern-workspace/locales.js +3 -4
  358. package/dist/esm-node/ultramodern-workspace/mf-validation/constants.js +20 -0
  359. package/dist/esm-node/ultramodern-workspace/mf-validation/discovery.js +29 -0
  360. package/dist/esm-node/ultramodern-workspace/mf-validation/inspect.js +47 -0
  361. package/dist/esm-node/ultramodern-workspace/mf-validation/object-literal.js +145 -0
  362. package/dist/esm-node/ultramodern-workspace/mf-validation/path-utils.js +90 -0
  363. package/dist/esm-node/ultramodern-workspace/mf-validation/syntax.js +155 -0
  364. package/dist/esm-node/ultramodern-workspace/mf-validation/types.js +1 -0
  365. package/dist/esm-node/ultramodern-workspace/mf-validation/validate.js +58 -0
  366. package/dist/esm-node/ultramodern-workspace/mf-validation.js +3 -516
  367. package/dist/esm-node/ultramodern-workspace/module-federation/config.js +290 -0
  368. package/dist/esm-node/ultramodern-workspace/module-federation/index.js +5 -0
  369. package/dist/esm-node/ultramodern-workspace/module-federation/reexport-module.js +68 -0
  370. package/dist/esm-node/ultramodern-workspace/module-federation/remote-refs.js +66 -0
  371. package/dist/esm-node/ultramodern-workspace/module-federation/shared-config.js +13 -0
  372. package/dist/esm-node/ultramodern-workspace/module-federation.js +2 -458
  373. package/dist/esm-node/ultramodern-workspace/naming.js +2 -2
  374. package/dist/esm-node/ultramodern-workspace/overlay-baseline-guard.js +351 -0
  375. package/dist/esm-node/ultramodern-workspace/overlays.js +28 -5
  376. package/dist/esm-node/ultramodern-workspace/package-json.js +48 -283
  377. package/dist/esm-node/ultramodern-workspace/package-source.js +7 -1
  378. package/dist/esm-node/ultramodern-workspace/policy.js +784 -10
  379. package/dist/esm-node/ultramodern-workspace/public-surface.js +2 -2
  380. package/dist/esm-node/ultramodern-workspace/routes.js +9 -143
  381. package/dist/esm-node/ultramodern-workspace/shared-patches.js +4 -0
  382. package/dist/esm-node/ultramodern-workspace/shells.js +93 -0
  383. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
  384. package/dist/esm-node/ultramodern-workspace/tsconfigs.js +138 -0
  385. package/dist/esm-node/ultramodern-workspace/types.js +16 -1
  386. package/dist/esm-node/ultramodern-workspace/versions.js +35 -26
  387. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +125 -0
  388. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +115 -85
  389. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +605 -0
  390. package/dist/esm-node/ultramodern-workspace/write-app.js +106 -0
  391. package/dist/esm-node/ultramodern-workspace/write-workspace.js +47 -66
  392. package/dist/esm-node/ultramodern-workspace/zerops.js +71 -0
  393. package/dist/types/cli/flags.d.ts +35 -0
  394. package/dist/types/cli/help.d.ts +2 -0
  395. package/dist/types/cli/package-source.d.ts +15 -0
  396. package/dist/types/cli/project-setup.d.ts +8 -0
  397. package/dist/types/cli/prompts.d.ts +4 -0
  398. package/dist/types/locale/en.d.ts +3 -4
  399. package/dist/types/locale/index.d.ts +6 -8
  400. package/dist/types/locale/zh.d.ts +3 -4
  401. package/dist/types/ultramodern-package-source.d.ts +0 -11
  402. package/dist/types/ultramodern-release-cohort.d.ts +30 -0
  403. package/dist/types/ultramodern-tooling/commands/cloudflare-output-verify.d.ts +2 -0
  404. package/dist/types/ultramodern-tooling/commands/context.d.ts +11 -0
  405. package/dist/types/ultramodern-tooling/commands/mf-types.d.ts +2 -0
  406. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.d.ts +12 -0
  407. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.d.ts +4 -0
  408. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.d.ts +1 -0
  409. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.d.ts +3 -0
  410. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.d.ts +5 -0
  411. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.d.ts +3 -0
  412. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.d.ts +2 -0
  413. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.d.ts +4 -0
  414. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.d.ts +3 -0
  415. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.d.ts +3 -0
  416. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.d.ts +7 -0
  417. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.d.ts +3 -0
  418. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.d.ts +3 -0
  419. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/help.d.ts +1 -0
  420. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/install.d.ts +2 -0
  421. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/io.d.ts +20 -0
  422. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.d.ts +9 -0
  423. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-source.d.ts +3 -0
  424. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.d.ts +27 -0
  425. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.d.ts +26 -0
  426. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.d.ts +10 -0
  427. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.d.ts +4 -0
  428. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect.d.ts +2 -0
  429. package/dist/types/ultramodern-tooling/commands/options.d.ts +2 -0
  430. package/dist/types/ultramodern-tooling/commands/routes-generate.d.ts +2 -0
  431. package/dist/types/ultramodern-tooling/commands/skills.d.ts +2 -0
  432. package/dist/types/ultramodern-tooling/commands/sync-delivery-unit.d.ts +1 -0
  433. package/dist/types/ultramodern-tooling/commands/validate.d.ts +2 -0
  434. package/dist/types/ultramodern-tooling/commands.d.ts +1 -1
  435. package/dist/types/ultramodern-tooling/config/constants.d.ts +3 -0
  436. package/dist/types/ultramodern-tooling/config/index.d.ts +7 -0
  437. package/dist/types/ultramodern-tooling/config/json.d.ts +2 -0
  438. package/dist/types/ultramodern-tooling/config/load.d.ts +2 -0
  439. package/dist/types/ultramodern-tooling/config/metadata.d.ts +4 -0
  440. package/dist/types/ultramodern-tooling/config/normalize.d.ts +22 -0
  441. package/dist/types/ultramodern-tooling/config/synthesize.d.ts +5 -0
  442. package/dist/types/ultramodern-tooling/config/types.d.ts +45 -0
  443. package/dist/types/ultramodern-tooling/config.d.ts +1 -43
  444. package/dist/types/ultramodern-workspace/add-shell.d.ts +14 -0
  445. package/dist/types/ultramodern-workspace/add-vertical/constants.d.ts +4 -0
  446. package/dist/types/ultramodern-workspace/add-vertical/execute.d.ts +9 -0
  447. package/dist/types/ultramodern-workspace/add-vertical/plan.d.ts +2 -0
  448. package/dist/types/ultramodern-workspace/add-vertical/preflight.d.ts +42 -0
  449. package/dist/types/ultramodern-workspace/add-vertical/shell-files.d.ts +6 -0
  450. package/dist/types/ultramodern-workspace/add-vertical/topology.d.ts +9 -0
  451. package/dist/types/ultramodern-workspace/add-vertical/transaction.d.ts +80 -0
  452. package/dist/types/ultramodern-workspace/add-vertical/workspace-state.d.ts +15 -0
  453. package/dist/types/ultramodern-workspace/add-vertical.d.ts +7 -39
  454. package/dist/types/ultramodern-workspace/api/checkout-cart.d.ts +33 -0
  455. package/dist/types/ultramodern-workspace/api/client.d.ts +6 -0
  456. package/dist/types/ultramodern-workspace/api/contracts.d.ts +15 -0
  457. package/dist/types/ultramodern-workspace/api/names.d.ts +41 -0
  458. package/dist/types/ultramodern-workspace/api/rpc.d.ts +41 -0
  459. package/dist/types/ultramodern-workspace/api/service.d.ts +6 -0
  460. package/dist/types/ultramodern-workspace/api/shared.d.ts +10 -0
  461. package/dist/types/ultramodern-workspace/api.d.ts +6 -73
  462. package/dist/types/ultramodern-workspace/app-files.d.ts +4 -10
  463. package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
  464. package/dist/types/ultramodern-workspace/bridge-config/cli.d.ts +3 -0
  465. package/dist/types/ultramodern-workspace/bridge-config/defaults.d.ts +1 -0
  466. package/dist/types/ultramodern-workspace/bridge-config/gates.d.ts +3 -0
  467. package/dist/types/ultramodern-workspace/bridge-config/index.d.ts +4 -0
  468. package/dist/types/ultramodern-workspace/bridge-config/normalize.d.ts +2 -0
  469. package/dist/types/ultramodern-workspace/bridge-config/schema.d.ts +61 -0
  470. package/dist/types/ultramodern-workspace/bridge-config/shared.d.ts +12 -0
  471. package/dist/types/ultramodern-workspace/bridge-config/validation.d.ts +3 -0
  472. package/dist/types/ultramodern-workspace/bridge-config/workspace-packages.d.ts +3 -0
  473. package/dist/types/ultramodern-workspace/bridge-config.d.ts +1 -64
  474. package/dist/types/ultramodern-workspace/codesmith.d.ts +12 -6
  475. package/dist/types/ultramodern-workspace/contracts.d.ts +3 -3
  476. package/dist/types/ultramodern-workspace/delivery-unit-schema/types.d.ts +291 -0
  477. package/dist/types/ultramodern-workspace/delivery-unit-schema/up-projection.d.ts +189 -0
  478. package/dist/types/ultramodern-workspace/delivery-unit-stamp.d.ts +3 -0
  479. package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
  480. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +8 -0
  481. package/dist/types/ultramodern-workspace/demo-components.d.ts +14 -3
  482. package/dist/types/ultramodern-workspace/descriptors.d.ts +27 -3
  483. package/dist/types/ultramodern-workspace/effect-diagnostics.d.ts +1 -0
  484. package/dist/types/ultramodern-workspace/fs-io.d.ts +2 -8
  485. package/dist/types/ultramodern-workspace/generation-result.d.ts +3 -1
  486. package/dist/types/ultramodern-workspace/index.d.ts +6 -1
  487. package/dist/types/ultramodern-workspace/locales.d.ts +1 -133
  488. package/dist/types/ultramodern-workspace/mf-validation/constants.d.ts +5 -0
  489. package/dist/types/ultramodern-workspace/mf-validation/discovery.d.ts +2 -0
  490. package/dist/types/ultramodern-workspace/mf-validation/inspect.d.ts +2 -0
  491. package/dist/types/ultramodern-workspace/mf-validation/object-literal.d.ts +4 -0
  492. package/dist/types/ultramodern-workspace/mf-validation/path-utils.d.ts +11 -0
  493. package/dist/types/ultramodern-workspace/mf-validation/syntax.d.ts +5 -0
  494. package/dist/types/ultramodern-workspace/mf-validation/types.d.ts +33 -0
  495. package/dist/types/ultramodern-workspace/mf-validation/validate.d.ts +4 -0
  496. package/dist/types/ultramodern-workspace/mf-validation.d.ts +4 -27
  497. package/dist/types/ultramodern-workspace/module-federation/config.d.ts +5 -0
  498. package/dist/types/ultramodern-workspace/module-federation/index.d.ts +4 -0
  499. package/dist/types/ultramodern-workspace/module-federation/reexport-module.d.ts +4 -0
  500. package/dist/types/ultramodern-workspace/module-federation/remote-refs.d.ts +3 -0
  501. package/dist/types/ultramodern-workspace/module-federation/shared-config.d.ts +2 -0
  502. package/dist/types/ultramodern-workspace/module-federation.d.ts +2 -17
  503. package/dist/types/ultramodern-workspace/naming.d.ts +0 -2
  504. package/dist/types/ultramodern-workspace/overlay-baseline-guard.d.ts +57 -0
  505. package/dist/types/ultramodern-workspace/package-json.d.ts +2 -17
  506. package/dist/types/ultramodern-workspace/package-source.d.ts +1 -0
  507. package/dist/types/ultramodern-workspace/policy.d.ts +254 -47
  508. package/dist/types/ultramodern-workspace/public-api.d.ts +1 -1
  509. package/dist/types/ultramodern-workspace/public-surface.d.ts +5 -15
  510. package/dist/types/ultramodern-workspace/routes.d.ts +0 -9
  511. package/dist/types/ultramodern-workspace/shared-patches.d.ts +1 -0
  512. package/dist/types/ultramodern-workspace/shells.d.ts +51 -0
  513. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
  514. package/dist/types/ultramodern-workspace/tsconfigs.d.ts +13 -0
  515. package/dist/types/ultramodern-workspace/types.d.ts +134 -3
  516. package/dist/types/ultramodern-workspace/versions.d.ts +35 -26
  517. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +63 -0
  518. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +15 -5
  519. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +243 -0
  520. package/dist/types/ultramodern-workspace/write-app.d.ts +4 -0
  521. package/dist/types/ultramodern-workspace/write-workspace.d.ts +3 -3
  522. package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
  523. package/package.json +15 -13
  524. package/template-workspace/.gitignore.handlebars +8 -0
  525. package/template-workspace/.modernjs/release-cohort.json +208 -0
  526. package/template-workspace/AGENTS.md.handlebars +16 -89
  527. package/template-workspace/README.md.handlebars +17 -16
  528. package/template-workspace/patches/@module-federation__bridge-react@2.8.0.patch +185 -0
  529. package/template-workspace/patches/@module-federation__modern-js-v3@2.8.0.patch +259 -0
  530. package/template-workspace/patches/@tanstack__router-core@1.171.14.patch +123 -0
  531. package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +452 -0
  532. package/template-workspace/patches/effect-schema-error-type-id.patch +18 -0
  533. package/template-workspace/pnpm-workspace.yaml.handlebars +8 -25
  534. package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
  535. package/templates/app/shell-frame.tsx +5 -4
  536. package/templates/workspace/apps/modern.config.shared-module-federation.ts.handlebars +37 -0
  537. package/templates/workspace/apps/modern.config.ts.handlebars +191 -0
  538. package/templates/workspace/apps/shared/src/routes/ultramodern-jsonld.ts.handlebars +119 -0
  539. package/templates/workspace/apps/shared/src/routes/ultramodern-route-metadata.ts.handlebars +21 -0
  540. package/templates/workspace/apps/shell-super-app/src/routes/[lang]/page.tsx.handlebars +49 -0
  541. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.helpers.tsx.handlebars +27 -0
  542. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.imports.tsx.handlebars +4 -0
  543. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.tsx.handlebars +44 -0
  544. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.worker.helpers.tsx.handlebars +17 -0
  545. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.worker.imports.tsx.handlebars +1 -0
  546. package/templates/workspace/packages/shared-design-tokens/tokens.css.handlebars +7 -0
  547. package/templates/workspace/tailwind.config.ts.handlebars +3 -0
  548. package/templates/workspace/verticals/server/checkout-cart-handlers.ts.handlebars +49 -0
  549. package/templates/workspace/verticals/server/checkout-cart-state.ts.handlebars +23 -0
  550. package/templates/workspace/verticals/shared/api.checkout-cart-contract-fields.ts.handlebars +1 -0
  551. package/templates/workspace/verticals/shared/api.checkout-cart-endpoints.ts.handlebars +23 -0
  552. package/templates/workspace/verticals/shared/api.checkout-cart-operation-contexts.ts.handlebars +29 -0
  553. package/templates/workspace/verticals/shared/api.checkout-cart-schemas.ts.handlebars +48 -0
  554. package/templates/workspace/verticals/src/api/checkout-cart-client-exports.ts.handlebars +70 -0
  555. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +4 -4
  556. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
  557. package/templates/workspace-scripts/generate-node-backend-federation.mjs +379 -0
  558. package/templates/workspace-scripts/generate-public-surface-assets.mjs +5 -5
  559. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +447 -0
  560. package/templates/workspace-scripts/proof-cloudflare-version.mjs +237 -9
  561. package/templates/workspace-scripts/proof-node-backend-federation.mjs +1223 -0
  562. package/templates/workspace-scripts/proof-workerd-ssr.mts +815 -0
  563. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
  564. package/templates/workspace-scripts/ultramodern-typecheck.mjs +6 -46
  565. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +2436 -258
  566. package/template-workspace/patches/@tanstack__router-core@1.171.13.patch +0 -51
@@ -3,33 +3,55 @@ import crypto from 'node:crypto';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
 
6
+ // Generated by modern-js-create with an immutable expected proof contract.
6
7
  const root = process.cwd();
7
- const packageScope = '{{packageScope}}';
8
- const expectedNodeVersion = '{{nodeVersion}}';
9
- const tailwindEnabled = {{tailwindEnabledJson}};
10
- const fullStackVerticals = {{fullStackVerticalsJson}};
11
- const shellNamespace = {{shellNamespaceJson}};
12
- const oldRemotePaths = {{oldRemotePathsJson}};
13
- const expectedBuildScript = {{expectedBuildScriptJson}};
14
- const expectedCloudflareBuildScript = {{expectedCloudflareBuildScriptJson}};
15
- const expectedCloudflareDeployScript = {{expectedCloudflareDeployScriptJson}};
16
- const expectedCloudflareSecurity = {{expectedCloudflareSecurityJson}};
17
- const publicSurfaceManagedSourceAssetPaths = {{publicSurfaceManagedSourceAssetPathsJson}};
18
- const compactConfigPath = '.modernjs/ultramodern.json';
19
- const retiredMetadataPaths = [
20
- '.modernjs/ultramodern-generated-contract.json',
21
- '.modernjs/ultramodern-package-source.json',
22
- '.modernjs/ultramodern-workspace-template-manifest.json',
23
- ];
24
- const modernPackageCohort = [
25
- '@modern-js/create',
26
- '@modern-js/code-tools',
27
- '@modern-js/app-tools',
28
- '@modern-js/plugin-bff',
29
- '@modern-js/plugin-i18n',
30
- '@modern-js/plugin-tanstack',
31
- '@modern-js/runtime',
32
- ];
8
+ const workspaceValidationContract = {{workspaceValidationContractJson}};
9
+ const packageScope = workspaceValidationContract.packageScope;
10
+ const expectedNodeVersion = workspaceValidationContract.versions.node;
11
+ const expectedEffectVersion = workspaceValidationContract.versions.effect;
12
+ const expectedModuleFederationVersion =
13
+ workspaceValidationContract.versions.moduleFederation;
14
+ const expectedCloudflareCompatibilityDate =
15
+ workspaceValidationContract.versions.cloudflareCompatibilityDate;
16
+ const tailwindEnabled = workspaceValidationContract.tailwindEnabled;
17
+ const fullStackVerticals = workspaceValidationContract.fullStackVerticals;
18
+ // Backend-federation and Zerops runtime surfaces only exist when the workspace
19
+ // exposes API-bearing verticals. Shell-only workspaces skip their
20
+ // materialization during migrate, so the contract must not require them.
21
+ const hasBackendSurfaces = fullStackVerticals.some(
22
+ vertical => vertical.emitsApi,
23
+ );
24
+ // Every vertical (ui-only and horizontal-remote included) is a delivery unit
25
+ // and deploys via Zerops; only the BACKEND proof/generation surfaces depend on
26
+ // an API-bearing unit existing (split gating).
27
+ const hasDeliveryUnits = fullStackVerticals.length > 0;
28
+ const shellNamespace = workspaceValidationContract.shellNamespace;
29
+ const oldRemotePaths = workspaceValidationContract.oldRemotePaths;
30
+ const expectedBuildScript = workspaceValidationContract.scripts.build;
31
+ const expectedCloudflareBuildScript =
32
+ workspaceValidationContract.scripts.cloudflareBuild;
33
+ const expectedCloudflareDeployScript =
34
+ workspaceValidationContract.scripts.cloudflareDeploy;
35
+ const expectedCloudflareSecurity =
36
+ workspaceValidationContract.cloudflareSecurity;
37
+ const publicSurfaceManagedSourceAssetPaths =
38
+ workspaceValidationContract.publicSurfaceManagedSourceAssetPaths;
39
+ const shellRouteMetaPaths = workspaceValidationContract.shellRouteMetaPaths;
40
+ const compactConfigPath =
41
+ workspaceValidationContract.metadata.compactConfig.path;
42
+ const retiredMetadataPaths =
43
+ workspaceValidationContract.legacy.retiredMetadataPaths;
44
+ const modernPackageCohort =
45
+ workspaceValidationContract.cohort.modernPackages;
46
+ const expectedAdditionalShellIds =
47
+ workspaceValidationContract.cohort.additionalShellIds ?? [];
48
+ const expectedAdditionalShells =
49
+ workspaceValidationContract.additionalShells ?? [];
50
+ const expectedPrimaryShellVerticalIds =
51
+ workspaceValidationContract.topology?.referenceTopology?.shell
52
+ ?.verticalRefs ?? workspaceValidationContract.cohort.verticalIds;
53
+ const expectedReleaseCohort =
54
+ workspaceValidationContract.cohort.releaseCohort;
33
55
  const expectedModernPackageSpecifier = packageName => {
34
56
  if (packageSource.strategy === 'workspace') {
35
57
  return 'workspace:*';
@@ -53,8 +75,64 @@ const assertExists = relativePath => {
53
75
  const assertNotExists = relativePath => {
54
76
  assert(!fs.existsSync(path.join(root, relativePath)), `Unexpected ${relativePath}`);
55
77
  };
78
+ const assertAnyOf = relativePaths => {
79
+ assert(
80
+ relativePaths.some(relativePath =>
81
+ fs.existsSync(path.join(root, relativePath)),
82
+ ),
83
+ `Missing one of: ${relativePaths.join(', ')}`,
84
+ );
85
+ };
86
+ const requiredShellWorkerCompositionPath = shellPath => {
87
+ const workerCompositionPath = `${shellPath}/src/routes/vertical-components.worker.tsx`;
88
+ if (fs.existsSync(path.join(root, workerCompositionPath))) {
89
+ return workerCompositionPath;
90
+ }
91
+
92
+ const browserComposition = readText(
93
+ `${shellPath}/src/routes/vertical-components.tsx`,
94
+ );
95
+ const frameworkGeneratedComposition =
96
+ browserComposition.includes('const createRemoteComponent =') &&
97
+ browserComposition.includes('export const VerticalShowcase =');
98
+ return frameworkGeneratedComposition
99
+ ? workerCompositionPath
100
+ : `${shellPath}/src/federated-components.worker.tsx`;
101
+ };
102
+ const canonicalizeJson = value => {
103
+ if (Array.isArray(value)) {
104
+ const entries = value.map(canonicalizeJson);
105
+ const keyedEntries = entries.every(
106
+ entry =>
107
+ entry !== null &&
108
+ typeof entry === 'object' &&
109
+ !Array.isArray(entry) &&
110
+ typeof entry.id === 'string',
111
+ );
112
+ if (keyedEntries) {
113
+ const ids = entries.map(entry => entry.id);
114
+ if (new Set(ids).size === ids.length) {
115
+ return entries.toSorted((left, right) => left.id.localeCompare(right.id));
116
+ }
117
+ }
118
+ return entries;
119
+ }
120
+ if (value !== null && typeof value === 'object') {
121
+ return Object.fromEntries(
122
+ Object.keys(value)
123
+ .toSorted()
124
+ .map(key => [key, canonicalizeJson(value[key])]),
125
+ );
126
+ }
127
+ return value;
128
+ };
129
+ const sameJson = (actual, expected) =>
130
+ JSON.stringify(canonicalizeJson(actual)) ===
131
+ JSON.stringify(canonicalizeJson(expected));
56
132
  const formatJson = value =>
57
- value === undefined ? 'undefined' : JSON.stringify(value);
133
+ value === undefined ? 'undefined' : JSON.stringify(canonicalizeJson(value));
134
+ const quoteYamlString = value => `'${String(value).replace(/'/gu, "''")}'`;
135
+ const quoteShellValue = value => `'${String(value).replace(/'/gu, "'\\''")}'`;
58
136
  const selfCheckFailure = (contract, message, fixArea) =>
59
137
  `MicroVertical contract self-check failed: ${contract}. ${message}. Fix area: ${fixArea}.`;
60
138
  const assertSelfCheck = (condition, contract, message, fixArea) => {
@@ -62,7 +140,7 @@ const assertSelfCheck = (condition, contract, message, fixArea) => {
62
140
  };
63
141
  const assertSameJson = (actual, expected, contract, fixArea) => {
64
142
  assertSelfCheck(
65
- JSON.stringify(actual) === JSON.stringify(expected),
143
+ sameJson(actual, expected),
66
144
  contract,
67
145
  `Expected ${formatJson(expected)}, found ${formatJson(actual)}`,
68
146
  fixArea,
@@ -84,6 +162,590 @@ const assertArray = (value, contract, fixArea) => {
84
162
  fixArea,
85
163
  );
86
164
  };
165
+ const assertUniqueStrings = (values, contract) => {
166
+ assert(Array.isArray(values), `${contract} must be an array`);
167
+ const seen = new Set();
168
+ for (const value of values) {
169
+ assert(
170
+ typeof value === 'string' && value.length > 0,
171
+ `${contract} must contain non-empty strings`,
172
+ );
173
+ assert(!seen.has(value), `Duplicate value "${value}" in ${contract}`);
174
+ seen.add(value);
175
+ }
176
+ };
177
+ const assertUniqueIdEntries = (entries, contract) => {
178
+ assert(Array.isArray(entries), `${contract} must be an array`);
179
+ const seen = new Set();
180
+ for (const entry of entries) {
181
+ const id = entry?.id;
182
+ assert(
183
+ typeof id === 'string' && id.length > 0,
184
+ `${contract} entries must have non-empty string ids`,
185
+ );
186
+ assert(!seen.has(id), `Duplicate id "${id}" in ${contract}`);
187
+ seen.add(id);
188
+ }
189
+ };
190
+ const assertSameIdCohort = (entries, expectedIds, contract, fixArea) => {
191
+ assertUniqueIdEntries(entries, contract);
192
+ assertSameJson(
193
+ entries.map(entry => entry.id).toSorted(),
194
+ [...expectedIds].toSorted(),
195
+ `${contract} cohort`,
196
+ fixArea,
197
+ );
198
+ };
199
+ const assertWorkspaceValidationContract = contract => {
200
+ assert(
201
+ contract !== null &&
202
+ typeof contract === 'object' &&
203
+ !Array.isArray(contract),
204
+ 'Workspace validation contract must be a JSON object',
205
+ );
206
+ assert(
207
+ contract.schemaVersion === 1,
208
+ `Unsupported workspace validation contract schemaVersion ${formatJson(contract.schemaVersion)}; expected 1`,
209
+ );
210
+ assert(
211
+ contract.kind === 'modernjs.ultramodern-workspace-validation-contract',
212
+ `Unsupported workspace validation contract kind ${formatJson(contract.kind)}`,
213
+ );
214
+
215
+ const metadataEntries = Object.entries(contract.metadata ?? {});
216
+ assert(
217
+ metadataEntries.length === (contract.cohort?.releaseCohort ? 5 : 4),
218
+ 'Workspace validation contract must declare every structured metadata input',
219
+ );
220
+ for (const [name, metadata] of metadataEntries) {
221
+ assert(
222
+ typeof metadata?.path === 'string' && metadata.path.length > 0,
223
+ `Workspace validation contract metadata.${name}.path is required`,
224
+ );
225
+ assert(
226
+ metadata.schemaVersion === 1,
227
+ `Unsupported expected metadata schemaVersion ${formatJson(metadata.schemaVersion)} for ${name}`,
228
+ );
229
+ }
230
+
231
+ assertUniqueStrings(
232
+ contract.cohort?.modernPackages,
233
+ 'workspace validation contract Modern package cohort',
234
+ );
235
+ assertUniqueStrings(
236
+ contract.cohort?.appIds,
237
+ 'workspace validation contract app cohort',
238
+ );
239
+ assertUniqueStrings(
240
+ contract.cohort?.additionalShellIds ?? [],
241
+ 'workspace validation contract additional-shell cohort',
242
+ );
243
+ for (const [field, label] of [
244
+ [
245
+ 'additionalShellOwnerIds',
246
+ 'workspace validation contract additional-shell owner cohort',
247
+ ],
248
+ [
249
+ 'additionalShellDeliveryUnitIds',
250
+ 'workspace validation contract additional-shell delivery-unit cohort',
251
+ ],
252
+ [
253
+ 'additionalShellDegradedStateIds',
254
+ 'workspace validation contract additional-shell degraded-state cohort',
255
+ ],
256
+ [
257
+ 'additionalShellBuildMarkerIds',
258
+ 'workspace validation contract additional-shell build-marker cohort',
259
+ ],
260
+ ]) {
261
+ assertUniqueStrings(contract.cohort?.[field] ?? [], label);
262
+ }
263
+ assertUniqueStrings(
264
+ contract.cohort?.backendAppIds,
265
+ 'workspace validation contract backend app cohort',
266
+ );
267
+ assertUniqueStrings(
268
+ contract.cohort?.verticalIds,
269
+ 'workspace validation contract vertical cohort',
270
+ );
271
+ assertUniqueStrings(
272
+ contract.cohort?.sharedPackageIds,
273
+ 'workspace validation contract shared package cohort',
274
+ );
275
+ assertUniqueStrings(
276
+ contract.cohort?.ownerIds,
277
+ 'workspace validation contract owner cohort',
278
+ );
279
+ assertUniqueIdEntries(
280
+ contract.cohort?.packageManifests,
281
+ 'workspace validation contract package manifests',
282
+ );
283
+ assertUniqueStrings(
284
+ (contract.cohort?.packageManifests ?? []).map(manifest => manifest.path),
285
+ 'workspace validation contract package manifest paths',
286
+ );
287
+
288
+ const generatedSurfacePolicy = contract.generatedSurfacePolicy;
289
+ assert(
290
+ generatedSurfacePolicy?.schemaVersion === 1,
291
+ `Unsupported generated surface policy schemaVersion ${formatJson(generatedSurfacePolicy?.schemaVersion)}; expected 1`,
292
+ );
293
+ assertUniqueIdEntries(
294
+ generatedSurfacePolicy?.rules,
295
+ 'workspace validation contract generated surface policy rules',
296
+ );
297
+ for (const rule of generatedSurfacePolicy.rules) {
298
+ assertUniqueStrings(
299
+ (rule.paths ?? []).map(entry => entry.path),
300
+ `generated surface policy ${rule.id} paths`,
301
+ );
302
+ assert(
303
+ Array.isArray(rule.paths) && rule.paths.length > 0,
304
+ `generated surface policy ${rule.id} must target generated paths`,
305
+ );
306
+ for (const target of rule.paths) {
307
+ assert(
308
+ target?.kind === 'file' || target?.kind === 'directory',
309
+ `generated surface policy ${rule.id} has an invalid target kind`,
310
+ );
311
+ assert(
312
+ typeof target.path === 'string' && target.path.length > 0,
313
+ `generated surface policy ${rule.id} target path is required`,
314
+ );
315
+ if (target.kind === 'directory') {
316
+ assertUniqueStrings(
317
+ target.extensions,
318
+ `generated surface policy ${rule.id} directory extensions`,
319
+ );
320
+ assertUniqueStrings(
321
+ target.excludePaths ?? [],
322
+ `generated surface policy ${rule.id} directory exclusions`,
323
+ );
324
+ }
325
+ }
326
+ assertUniqueIdEntries(
327
+ rule.patterns,
328
+ `generated surface policy ${rule.id} patterns`,
329
+ );
330
+ for (const pattern of rule.patterns) {
331
+ if (pattern.structuralMatcher !== undefined) {
332
+ assert(
333
+ pattern.expression === undefined && pattern.flags === undefined,
334
+ `generated surface policy ${rule.id}.${pattern.id} must use exactly one matcher`,
335
+ );
336
+ assert(
337
+ pattern.structuralMatcher?.kind === 'jsx-attribute',
338
+ `generated surface policy ${rule.id}.${pattern.id} has an unsupported structural matcher`,
339
+ );
340
+ assert(
341
+ typeof pattern.structuralMatcher.elementName === 'string' &&
342
+ pattern.structuralMatcher.elementName.length > 0,
343
+ `generated surface policy ${rule.id}.${pattern.id} structural elementName is required`,
344
+ );
345
+ assert(
346
+ typeof pattern.structuralMatcher.attributeName === 'string' &&
347
+ pattern.structuralMatcher.attributeName.length > 0,
348
+ `generated surface policy ${rule.id}.${pattern.id} structural attributeName is required`,
349
+ );
350
+ } else {
351
+ assert(
352
+ typeof pattern.expression === 'string' && pattern.expression.length > 0,
353
+ `generated surface policy ${rule.id}.${pattern.id} expression is required`,
354
+ );
355
+ assert(
356
+ pattern.flags === 'u',
357
+ `generated surface policy ${rule.id}.${pattern.id} must use deterministic Unicode matching`,
358
+ );
359
+ new RegExp(pattern.expression, pattern.flags);
360
+ }
361
+ assert(
362
+ typeof pattern.diagnostic === 'string' && pattern.diagnostic.length > 0,
363
+ `generated surface policy ${rule.id}.${pattern.id} diagnostic is required`,
364
+ );
365
+ assert(
366
+ typeof pattern.fixArea === 'string' && pattern.fixArea.length > 0,
367
+ `generated surface policy ${rule.id}.${pattern.id} fixArea is required`,
368
+ );
369
+ }
370
+ }
371
+ };
372
+ const generatedSurfacePolicyFiles = target => {
373
+ const absolutePath = path.join(root, target.path);
374
+ if (target.kind === 'file') {
375
+ assert(fs.existsSync(absolutePath), `Missing generated surface policy file ${target.path}`);
376
+ return [target.path];
377
+ }
378
+
379
+ assert(fs.existsSync(absolutePath), `Missing generated surface policy directory ${target.path}`);
380
+ const files = [];
381
+ const queue = [absolutePath];
382
+ while (queue.length > 0) {
383
+ const current = queue.shift();
384
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
385
+ const absoluteEntryPath = path.join(current, entry.name);
386
+ if (entry.isDirectory()) {
387
+ queue.push(absoluteEntryPath);
388
+ } else if (target.extensions.includes(path.extname(entry.name))) {
389
+ files.push(path.relative(root, absoluteEntryPath).split(path.sep).join('/'));
390
+ }
391
+ }
392
+ }
393
+ const excludedPaths = new Set(target.excludePaths ?? []);
394
+ return files.filter(file => !excludedPaths.has(file)).toSorted();
395
+ };
396
+ const isJsxNameCharacter = character =>
397
+ typeof character === 'string' && /[\w:.-]/u.test(character);
398
+ const skipQuotedSource = (source, start) => {
399
+ const quote = source[start];
400
+ let cursor = start + 1;
401
+ while (cursor < source.length) {
402
+ if (source[cursor] === '\\') {
403
+ cursor += 2;
404
+ } else if (source[cursor] === quote) {
405
+ return cursor + 1;
406
+ } else {
407
+ cursor += 1;
408
+ }
409
+ }
410
+ return cursor;
411
+ };
412
+ const skipSourceComment = (source, start) => {
413
+ if (source[start + 1] === '/') {
414
+ const lineEnd = source.indexOf('\n', start + 2);
415
+ return lineEnd === -1 ? source.length : lineEnd + 1;
416
+ }
417
+ if (source[start + 1] === '*') {
418
+ const commentEnd = source.indexOf('*/', start + 2);
419
+ return commentEnd === -1 ? source.length : commentEnd + 2;
420
+ }
421
+ return start;
422
+ };
423
+ const findJsxAttribute = (source, matcher) => {
424
+ const opening = `<${matcher.elementName}`;
425
+ let elementIndex = source.indexOf(opening);
426
+ while (elementIndex !== -1) {
427
+ const elementBoundary = source[elementIndex + opening.length];
428
+ if (!isJsxNameCharacter(elementBoundary)) {
429
+ let cursor = elementIndex + opening.length;
430
+ let expressionDepth = 0;
431
+ while (cursor < source.length) {
432
+ const character = source[cursor];
433
+ if (character === "'" || character === '"' || character === '`') {
434
+ cursor = skipQuotedSource(source, cursor);
435
+ continue;
436
+ }
437
+ if (character === '/') {
438
+ const afterComment = skipSourceComment(source, cursor);
439
+ if (afterComment !== cursor) {
440
+ cursor = afterComment;
441
+ continue;
442
+ }
443
+ }
444
+ if (character === '{') {
445
+ expressionDepth += 1;
446
+ cursor += 1;
447
+ continue;
448
+ }
449
+ if (character === '}') {
450
+ expressionDepth = Math.max(0, expressionDepth - 1);
451
+ cursor += 1;
452
+ continue;
453
+ }
454
+ if (character === '>' && expressionDepth === 0) {
455
+ break;
456
+ }
457
+ if (
458
+ expressionDepth === 0 &&
459
+ source.startsWith(matcher.attributeName, cursor) &&
460
+ !isJsxNameCharacter(source[cursor - 1]) &&
461
+ !isJsxNameCharacter(source[cursor + matcher.attributeName.length])
462
+ ) {
463
+ let equalsIndex = cursor + matcher.attributeName.length;
464
+ while (/\s/u.test(source[equalsIndex] ?? '')) {
465
+ equalsIndex += 1;
466
+ }
467
+ if (source[equalsIndex] === '=') {
468
+ return { index: cursor };
469
+ }
470
+ }
471
+ cursor += 1;
472
+ }
473
+ }
474
+ elementIndex = source.indexOf(opening, elementIndex + opening.length);
475
+ }
476
+ return null;
477
+ };
478
+ const findGeneratedSurfacePolicyMatch = (source, pattern) =>
479
+ pattern.structuralMatcher?.kind === 'jsx-attribute'
480
+ ? findJsxAttribute(source, pattern.structuralMatcher)
481
+ : new RegExp(pattern.expression, pattern.flags).exec(source);
482
+ const assertGeneratedSurfacePolicy = () => {
483
+ for (const rule of workspaceValidationContract.generatedSurfacePolicy.rules) {
484
+ const files = rule.paths.flatMap(generatedSurfacePolicyFiles).toSorted();
485
+ for (const relativePath of files) {
486
+ const source = readText(relativePath);
487
+ for (const pattern of rule.patterns) {
488
+ const match = findGeneratedSurfacePolicyMatch(source, pattern);
489
+ assertSelfCheck(
490
+ match === null,
491
+ `generated surface policy ${rule.id}.${pattern.id}`,
492
+ `${pattern.diagnostic} Found forbidden source at ${relativePath}:${match?.index ?? 0}`,
493
+ pattern.fixArea,
494
+ );
495
+ }
496
+ }
497
+ }
498
+ if ((workspaceValidationContract.cohort?.additionalShellIds ?? []).length > 0) {
499
+ for (const field of [
500
+ 'additionalShellOwnerIds',
501
+ 'additionalShellDeliveryUnitIds',
502
+ 'additionalShellDegradedStateIds',
503
+ 'additionalShellBuildMarkerIds',
504
+ ]) {
505
+ assertSameJson(
506
+ workspaceValidationContract.cohort?.[field],
507
+ workspaceValidationContract.cohort.additionalShellIds,
508
+ `workspace validation contract ${field}`,
509
+ 'restore every generated additional-shell cohort',
510
+ );
511
+ }
512
+ assertSameIdCohort(
513
+ workspaceValidationContract.cohort?.additionalShellManifests,
514
+ workspaceValidationContract.cohort.additionalShellIds,
515
+ 'workspace validation contract additional-shell manifests',
516
+ 'restore every generated additional-shell package manifest',
517
+ );
518
+ assertSameIdCohort(
519
+ workspaceValidationContract.additionalShells,
520
+ workspaceValidationContract.cohort.additionalShellIds,
521
+ 'workspace validation contract additional-shell records',
522
+ 'restore every generated additional-shell contract record',
523
+ );
524
+ } else {
525
+ assert(
526
+ workspaceValidationContract.cohort?.additionalShellManifests === undefined,
527
+ 'Single-shell workspace must not declare additional-shell manifests',
528
+ );
529
+ assert(
530
+ workspaceValidationContract.additionalShells === undefined,
531
+ 'Single-shell workspace must not declare additional-shell records',
532
+ );
533
+ for (const field of [
534
+ 'additionalShellOwnerIds',
535
+ 'additionalShellDeliveryUnitIds',
536
+ 'additionalShellDegradedStateIds',
537
+ 'additionalShellBuildMarkerIds',
538
+ ]) {
539
+ assert(
540
+ workspaceValidationContract.cohort?.[field] === undefined,
541
+ `Single-shell workspace must not declare ${field}`,
542
+ );
543
+ }
544
+ }
545
+ };
546
+ const compactConfigPolicyView = config => ({
547
+ schemaVersion: config.schemaVersion,
548
+ profile: config.profile,
549
+ workspace: config.workspace,
550
+ features: config.features,
551
+ deploy: config.deploy,
552
+ moduleFederation: config.moduleFederation,
553
+ backendFederation: config.backendFederation,
554
+ agentSkills: config.agentSkills,
555
+ tooling: config.tooling,
556
+ });
557
+ const assertStructuredWorkspaceMetadata = ({
558
+ ultramodernConfig,
559
+ topology,
560
+ ownership,
561
+ overlay,
562
+ }) => {
563
+ const observedMetadata = [
564
+ {
565
+ contract: workspaceValidationContract.metadata.compactConfig,
566
+ value: ultramodernConfig,
567
+ },
568
+ {
569
+ contract: workspaceValidationContract.metadata.referenceTopology,
570
+ value: topology,
571
+ },
572
+ {
573
+ contract: workspaceValidationContract.metadata.ownership,
574
+ value: ownership,
575
+ },
576
+ {
577
+ contract: workspaceValidationContract.metadata.developmentOverlay,
578
+ value: overlay,
579
+ },
580
+ ];
581
+
582
+ for (const entry of observedMetadata) {
583
+ assert(
584
+ entry.value !== null &&
585
+ typeof entry.value === 'object' &&
586
+ !Array.isArray(entry.value),
587
+ `${entry.contract.path} must contain a JSON object`,
588
+ );
589
+ assert(
590
+ Number.isInteger(entry.value.schemaVersion),
591
+ `${entry.contract.path} must declare an integer schemaVersion`,
592
+ );
593
+ }
594
+
595
+ const observedSchemaVersions = new Set(
596
+ observedMetadata.map(entry => entry.value.schemaVersion),
597
+ );
598
+ assert(
599
+ observedSchemaVersions.size === 1,
600
+ `Mixed workspace metadata schema versions: ${observedMetadata
601
+ .map(entry => `${entry.contract.path}=${entry.value.schemaVersion}`)
602
+ .join(', ')}`,
603
+ );
604
+ for (const entry of observedMetadata) {
605
+ assert(
606
+ entry.value.schemaVersion === entry.contract.schemaVersion,
607
+ `Unsupported workspace metadata schemaVersion ${entry.value.schemaVersion} at ${entry.contract.path}; expected ${entry.contract.schemaVersion}`,
608
+ );
609
+ }
610
+
611
+ assertObject(
612
+ ultramodernConfig.packageSource,
613
+ `${compactConfigPath} packageSource`,
614
+ 'restore generated compact package-source metadata',
615
+ );
616
+ for (const field of
617
+ workspaceValidationContract.legacy.forbiddenCompactConfigFields) {
618
+ assert(
619
+ !Object.hasOwn(ultramodernConfig, field),
620
+ `Stale legacy field ${compactConfigPath}.${field} is forbidden`,
621
+ );
622
+ }
623
+ for (const field of
624
+ workspaceValidationContract.legacy.forbiddenPackageSourceFields) {
625
+ assert(
626
+ !Object.hasOwn(ultramodernConfig.packageSource, field),
627
+ `Stale legacy field ${compactConfigPath}.packageSource.${field} is forbidden`,
628
+ );
629
+ }
630
+ for (const field of workspaceValidationContract.legacy.forbiddenTopologyFields) {
631
+ assert(
632
+ !Object.hasOwn(topology, field),
633
+ `Stale legacy field ${workspaceValidationContract.metadata.referenceTopology.path}.${field} is forbidden`,
634
+ );
635
+ }
636
+
637
+ assertSameIdCohort(
638
+ ultramodernConfig.topology?.apps,
639
+ workspaceValidationContract.cohort.appIds,
640
+ `${compactConfigPath} topology.apps`,
641
+ 'restore the complete generated app cohort',
642
+ );
643
+ assertSameIdCohort(
644
+ ultramodernConfig.moduleFederation?.apps,
645
+ workspaceValidationContract.cohort.appIds,
646
+ `${compactConfigPath} moduleFederation.apps`,
647
+ 'restore the complete generated Module Federation app cohort',
648
+ );
649
+ assertSameIdCohort(
650
+ ultramodernConfig.backendFederation?.apps,
651
+ workspaceValidationContract.cohort.backendAppIds,
652
+ `${compactConfigPath} backendFederation.apps`,
653
+ 'restore the complete generated backend app cohort',
654
+ );
655
+ assertSameIdCohort(
656
+ topology.verticals,
657
+ workspaceValidationContract.cohort.verticalIds,
658
+ `${workspaceValidationContract.metadata.referenceTopology.path} verticals`,
659
+ 'restore the complete generated vertical cohort',
660
+ );
661
+ assertSameIdCohort(
662
+ topology.sharedPackages,
663
+ workspaceValidationContract.cohort.sharedPackageIds,
664
+ `${workspaceValidationContract.metadata.referenceTopology.path} sharedPackages`,
665
+ 'restore the complete generated shared package cohort',
666
+ );
667
+ assertSameIdCohort(
668
+ topology.shell?.moduleFederation?.remotes,
669
+ workspaceValidationContract.topology.referenceTopology.shell.moduleFederation.remotes.map(
670
+ remote => remote.id,
671
+ ),
672
+ `${workspaceValidationContract.metadata.referenceTopology.path} shell.moduleFederation.remotes`,
673
+ 'restore the complete generated shell remote cohort',
674
+ );
675
+ assertSameIdCohort(
676
+ ownership.owners,
677
+ workspaceValidationContract.cohort.ownerIds,
678
+ `${workspaceValidationContract.metadata.ownership.path} owners`,
679
+ 'restore the complete generated ownership cohort',
680
+ );
681
+
682
+ for (const manifest of workspaceValidationContract.cohort.packageManifests) {
683
+ assertExists(manifest.path);
684
+ const packageJson = readJson(manifest.path);
685
+ assert(
686
+ packageJson.name === manifest.packageName,
687
+ `${manifest.path} package name must be ${manifest.packageName}`,
688
+ );
689
+ if (manifest.role === 'shell' || manifest.role === 'vertical') {
690
+ assert(
691
+ packageJson.modernjs?.appId === manifest.id,
692
+ `${manifest.path} modernjs.appId must be ${manifest.id}`,
693
+ );
694
+ }
695
+ }
696
+ if (expectedReleaseCohort) {
697
+ const releaseCohortContract = workspaceValidationContract.metadata.releaseCohort;
698
+ assertSelfCheck(
699
+ releaseCohortContract?.path === '.modernjs/release-cohort.json',
700
+ 'authenticated release cohort projection',
701
+ 'Expected release-cohort metadata path is missing or invalid',
702
+ '.modernjs/release-cohort.json',
703
+ );
704
+ assertSameJson(
705
+ readJson(releaseCohortContract.path),
706
+ expectedReleaseCohort,
707
+ 'authenticated release cohort projection',
708
+ releaseCohortContract.path,
709
+ );
710
+ }
711
+ };
712
+ const assertStructuredWorkspaceMetadataSemantics = ({
713
+ ultramodernConfig,
714
+ topology,
715
+ ownership,
716
+ overlay,
717
+ }) => {
718
+ assertSameJson(
719
+ compactConfigPolicyView(ultramodernConfig),
720
+ workspaceValidationContract.policy.compactConfig,
721
+ `${compactConfigPath} policy`,
722
+ 'restore generated compact workspace policy metadata',
723
+ );
724
+ assertSameJson(
725
+ ultramodernConfig.topology,
726
+ workspaceValidationContract.topology.compactConfig,
727
+ `${compactConfigPath} topology`,
728
+ 'restore the complete generated compact topology cohort',
729
+ );
730
+ assertSameJson(
731
+ topology,
732
+ workspaceValidationContract.topology.referenceTopology,
733
+ workspaceValidationContract.metadata.referenceTopology.path,
734
+ 'restore the complete generated reference topology',
735
+ );
736
+ assertSameJson(
737
+ ownership,
738
+ workspaceValidationContract.topology.ownership,
739
+ workspaceValidationContract.metadata.ownership.path,
740
+ 'restore the complete generated ownership topology',
741
+ );
742
+ assertSameJson(
743
+ overlay,
744
+ workspaceValidationContract.topology.developmentOverlay,
745
+ workspaceValidationContract.metadata.developmentOverlay.path,
746
+ 'restore the complete generated development topology',
747
+ );
748
+ };
87
749
  const findById = (entries, id) =>
88
750
  Array.isArray(entries) ? entries.find(entry => entry?.id === id) : undefined;
89
751
  const generatedContractLabel = compactConfigPath;
@@ -136,6 +798,40 @@ const buildMarkerFor = app =>
136
798
  .update(`${packageScope}:${app.packageSuffix}:${app.id}:0.1.0`)
137
799
  .digest('hex')
138
800
  .slice(0, 16);
801
+ const deliveryUnitIdentityFixArea =
802
+ 'regenerate vertical identity from delivery-unit record; do not hand-edit surface markers';
803
+ const deliveryUnitBlock = record => ({
804
+ schemaVersion: record?.schemaVersion,
805
+ kind: record?.kind,
806
+ unitId: record?.unitId,
807
+ packageName: record?.packageName,
808
+ version: record?.version,
809
+ buildMarker: record?.buildMarker,
810
+ sourceRevision: record?.sourceRevision,
811
+ });
812
+ const expectedCompactAppFor = id =>
813
+ workspaceValidationContract.topology.compactConfig?.apps?.find(
814
+ entry => entry?.id === id,
815
+ );
816
+ const expectedDeliveryUnitFor = vertical => {
817
+ const expectedApp = expectedCompactAppFor(vertical.id);
818
+ return (
819
+ expectedApp?.backendFederation?.deliveryUnit ??
820
+ expectedApp?.deliveryUnit ??
821
+ vertical.deliveryUnit
822
+ );
823
+ };
824
+ const assertBuildFacadeExport = (source, exportName, sourcePath, contract) => {
825
+ const exportPattern = new RegExp(
826
+ `export const ${exportName} = ${sourcePath.replace(/\./gu, '\\.')};`,
827
+ );
828
+ assertSelfCheck(
829
+ exportPattern.test(source),
830
+ contract,
831
+ `${exportName} must re-export the canonical ultramodern-build.json artifact rather than hand-forking fields`,
832
+ deliveryUnitIdentityFixArea,
833
+ );
834
+ };
139
835
  const normalizeCompactApp = rawApp => {
140
836
  const id = String(rawApp.id);
141
837
  const kind = rawApp.kind === 'vertical' ? 'vertical' : 'shell';
@@ -175,6 +871,10 @@ const normalizeCompactApp = rawApp => {
175
871
  consumer => typeof consumer === 'string',
176
872
  )
177
873
  : ['shell-super-app', id],
874
+ // Preserve the API protocol so the synthesized generated contract can
875
+ // branch between the REST (`shared/api.ts` + `./api/client`) and RPC
876
+ // (`shared/rpc.ts` + `./api/rpc-client`) surfaces.
877
+ protocol: rawApp.api.protocol === 'rpc' ? 'rpc' : 'rest',
178
878
  }
179
879
  : undefined;
180
880
 
@@ -210,6 +910,14 @@ const normalizeCompactApp = rawApp => {
210
910
  verticalRefs: Array.isArray(moduleFederation.verticalRefs)
211
911
  ? moduleFederation.verticalRefs.filter(ref => typeof ref === 'string')
212
912
  : [],
913
+ deliveryUnit:
914
+ rawApp.deliveryUnit && typeof rawApp.deliveryUnit === 'object'
915
+ ? rawApp.deliveryUnit
916
+ : undefined,
917
+ backendFederation:
918
+ rawApp.backendFederation && typeof rawApp.backendFederation === 'object'
919
+ ? rawApp.backendFederation
920
+ : undefined,
213
921
  api,
214
922
  };
215
923
  };
@@ -287,7 +995,7 @@ const createPublicSurface = app => {
287
995
  metadataExport: './src/routes/ultramodern-route-metadata',
288
996
  generator: 'scripts/generate-public-surface-assets.mts',
289
997
  outputRoot: 'dist/public',
290
- cloudflareOutputRoot: '.output/public',
998
+ cloudflareBuildOutputRoot: 'dist-cloudflare/public',
291
999
  privateRoutePolicy: 'omit-from-generated-public-surface',
292
1000
  files:
293
1001
  publicRoutes.length > 0
@@ -394,7 +1102,10 @@ const createCloudflareRoutes = app => ({
394
1102
  ssr: '/en',
395
1103
  mfManifest: '/mf-manifest.json',
396
1104
  locale: `/locales/en/${appNamespace(app)}.json`,
397
- ...(app.api
1105
+ // Mirrors policy.ts:76 — the Cloudflare proof carries a REST readiness route
1106
+ // only for `rest` units. An `rpc` unit exposes its `/rpc` route instead and
1107
+ // must not emit (or require) a REST readiness path.
1108
+ ...(app.api && app.api.protocol === 'rest'
398
1109
  ? {
399
1110
  apiReadiness: `${app.api.prefix}/${app.api.stem}/readiness`,
400
1111
  }
@@ -404,7 +1115,7 @@ const createCloudflareDeploy = app => ({
404
1115
  target: 'cloudflare',
405
1116
  workerName: expectedWorkerName(app.packageSuffix),
406
1117
  publicUrlEnv: `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(app.id)}`,
407
- compatibilityDate: '{{cloudflareCompatibilityDate}}',
1118
+ compatibilityDate: expectedCloudflareCompatibilityDate,
408
1119
  compatibilityFlags: ['nodejs_compat', 'global_fetch_strictly_public'],
409
1120
  assetsBinding: 'ASSETS',
410
1121
  routes: createCloudflareRoutes(app),
@@ -486,7 +1197,7 @@ const createAppConfigContract = app => ({
486
1197
  'zephyrRspackPlugin',
487
1198
  ],
488
1199
  dev: {
489
- assetPrefix: '/',
1200
+ assetPrefix: app.kind === 'shell' ? '/' : 'app-public-origin',
490
1201
  },
491
1202
  output: {
492
1203
  assetPrefix: {
@@ -585,7 +1296,7 @@ const createAppContract = (app, apps) => ({
585
1296
  target: 'cloudflare',
586
1297
  cloudflare: createCloudflareDeploy(app),
587
1298
  worker: {
588
- compatibilityDate: '{{cloudflareCompatibilityDate}}',
1299
+ compatibilityDate: expectedCloudflareCompatibilityDate,
589
1300
  name: expectedWorkerName(app.packageSuffix),
590
1301
  security: expectedCloudflareSecurity,
591
1302
  ssr: true,
@@ -593,7 +1304,7 @@ const createAppContract = (app, apps) => ({
593
1304
  },
594
1305
  ssr: app.moduleFederationSsr
595
1306
  ? {
596
- mode: 'string',
1307
+ mode: 'stream',
597
1308
  moduleFederationAppSSR: true,
598
1309
  }
599
1310
  : undefined,
@@ -625,37 +1336,55 @@ const createAppContract = (app, apps) => ({
625
1336
  : {}),
626
1337
  exposes: app.exposes,
627
1338
  dts: {
628
- compilerInstance: 'tsgo',
1339
+ compilerInstance: 'effect-tsgo',
629
1340
  displayErrorInTerminal: true,
630
1341
  tsConfigPath: './tsconfig.mf-types.json',
631
1342
  },
632
1343
  browserSafeExposesOnly: true,
633
1344
  },
634
- marker: {
635
- appId: app.id,
636
- packageName: app.package ?? packageNameFor(packageScope, app.packageSuffix),
637
- version: '0.1.0',
638
- build: buildMarkerFor(app),
639
- deployProfile: 'cloudflare-ssr-mf-effect-v1',
640
- uiSurface: 'ui',
641
- ...(app.api ? { apiSurface: 'api' } : {}),
1345
+ marker: {
1346
+ appId: app.id,
1347
+ packageName: app.package ?? packageNameFor(packageScope, app.packageSuffix),
1348
+ version: '0.1.0',
1349
+ build: app.deliveryUnit?.buildMarker ?? buildMarkerFor(app),
1350
+ deployProfile: 'cloudflare-ssr-mf-effect-v1',
1351
+ uiSurface: 'ui',
1352
+ ...(app.api ? { apiSurface: 'api' } : {}),
642
1353
  },
643
1354
  ...(app.api
644
1355
  ? {
645
- api: {
646
- runtime: 'effect',
647
- import: '@modern-js/plugin-bff/effect-edge',
648
- prefix: app.api.prefix,
649
- openapi: '/openapi.json',
650
- strictEffectApproach: true,
651
- workerEntry: 'worker/__modern_bff_effect.js',
652
- contract: './api',
653
- client: './api/client',
654
- readiness: createEffectReadiness(app),
655
- requestContext: createEffectRequestContext(),
656
- domainOperations: createEffectDomainOperations(app),
657
- ...createEffectOperationContract(app),
658
- },
1356
+ // An `rpc` unit exposes the Effect RpcGroup contract/client and the
1357
+ // `/rpc` route instead of the REST `shared/api.ts` + `./api/client`
1358
+ // surface, so it carries no OpenAPI/readiness/domain-operation metadata.
1359
+ api:
1360
+ (app.api.protocol ?? 'rest') === 'rpc'
1361
+ ? {
1362
+ runtime: 'effect',
1363
+ import: '@modern-js/plugin-bff/effect-edge',
1364
+ prefix: app.api.prefix,
1365
+ protocol: 'rpc',
1366
+ strictEffectApproach: true,
1367
+ workerEntry: 'worker/__modern_bff_effect.js',
1368
+ contract: './api',
1369
+ client: './api/rpc-client',
1370
+ group: toCamelCase(app.api.stem),
1371
+ rpc: { path: '/rpc', serialization: 'json' },
1372
+ rpcPath: `${app.api.prefix}/rpc`,
1373
+ }
1374
+ : {
1375
+ runtime: 'effect',
1376
+ import: '@modern-js/plugin-bff/effect-edge',
1377
+ prefix: app.api.prefix,
1378
+ openapi: '/openapi.json',
1379
+ strictEffectApproach: true,
1380
+ workerEntry: 'worker/__modern_bff_effect.js',
1381
+ contract: './api',
1382
+ client: './api/client',
1383
+ readiness: createEffectReadiness(app),
1384
+ requestContext: createEffectRequestContext(),
1385
+ domainOperations: createEffectDomainOperations(app),
1386
+ ...createEffectOperationContract(app),
1387
+ },
659
1388
  }
660
1389
  : {}),
661
1390
  });
@@ -720,13 +1449,26 @@ const createModernPackageAliases = packageSourceConfig => {
720
1449
  ]),
721
1450
  );
722
1451
  };
723
- const synthesizePackageSourceFromCompact = config => {
724
- const source = config.packageSource ?? {};
725
- const strategy = source.strategy === 'install' ? 'install' : 'workspace';
1452
+ const createPackageSourceView = config => {
1453
+ const source = config.packageSource;
1454
+ assert(
1455
+ source !== null && typeof source === 'object' && !Array.isArray(source),
1456
+ `${compactConfigPath} packageSource must be a JSON object`,
1457
+ );
1458
+ assert(
1459
+ source.strategy === 'workspace' || source.strategy === 'install',
1460
+ `${compactConfigPath} packageSource.strategy must be workspace or install`,
1461
+ );
1462
+ if (source.strategy === 'install') {
1463
+ assert(
1464
+ typeof source.modernPackageVersion === 'string' &&
1465
+ source.modernPackageVersion.length > 0,
1466
+ `${compactConfigPath} install package source must declare modernPackageVersion`,
1467
+ );
1468
+ }
1469
+ const strategy = source.strategy;
726
1470
  const specifier =
727
- strategy === 'install' && typeof source.modernPackageVersion === 'string'
728
- ? source.modernPackageVersion
729
- : 'workspace:*';
1471
+ strategy === 'install' ? source.modernPackageVersion : 'workspace:*';
730
1472
  const aliases = createModernPackageAliases(source);
731
1473
  return {
732
1474
  schemaVersion: 1,
@@ -762,16 +1504,124 @@ const synthesizeGeneratedContractFromCompact = config => {
762
1504
  apps: apps.map(app => createAppContract(app, apps)),
763
1505
  };
764
1506
  };
765
- const readPackageSourceView = config => {
766
- return synthesizePackageSourceFromCompact(config);
767
- };
768
1507
  const readGeneratedContractView = config => {
769
1508
  return synthesizeGeneratedContractFromCompact(config);
770
1509
  };
771
1510
  const expectedManifestUrl = vertical =>
772
1511
  `http://localhost:${vertical.port}/mf-manifest.json`;
773
1512
  const expectedApiUrl = vertical =>
774
- `http://localhost:${vertical.port}${vertical.apiPrefix}`;
1513
+ `http://localhost:${vertical.port}${vertical.apiPrefix}${
1514
+ vertical.apiProtocol === 'rpc' ? '/rpc' : ''
1515
+ }`;
1516
+ const expectedBackendFederationName = vertical => `${vertical.mfName}Backend`;
1517
+ const expectedBackendManifestUrl = vertical =>
1518
+ `http://localhost:${vertical.port}/backend-mf-manifest.json`;
1519
+ const expectedBackendContainerEntry = vertical =>
1520
+ `http://localhost:${vertical.port}/backendRemoteEntry.cjs`;
1521
+ const expectedBackendManifestEnv = vertical =>
1522
+ `VERTICAL_${toEnvSegment(vertical.domain ?? vertical.id)}_BACKEND_MF_MANIFEST`;
1523
+ const expectedPublicUrlEnv = vertical =>
1524
+ `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(vertical.id)}`;
1525
+ const expectedCloudflareWorkerName = vertical =>
1526
+ toKebabCase(`${packageScope}-${vertical.packageSuffix ?? vertical.id}`).slice(
1527
+ 0,
1528
+ 63,
1529
+ );
1530
+ const backendFederationSubset = backendFederation => ({
1531
+ role: backendFederation?.role,
1532
+ name: backendFederation?.name,
1533
+ runtimeFramework: backendFederation?.runtimeFramework,
1534
+ strictEffectApproach: backendFederation?.strictEffectApproach,
1535
+ exposeRuntime: backendFederation?.exposes?.['./effect-api']?.runtime,
1536
+ exposeReadiness: backendFederation?.exposes?.['./effect-api']?.readiness,
1537
+ versionBoundary: {
1538
+ invariant: backendFederation?.versionBoundary?.invariant,
1539
+ uiManifestUrl: backendFederation?.versionBoundary?.ui?.manifestUrl,
1540
+ apiReadiness: backendFederation?.versionBoundary?.api?.readiness,
1541
+ },
1542
+ cloudflare: {
1543
+ kind: backendFederation?.executionSurfaces?.cloudflare?.kind,
1544
+ workerName: backendFederation?.executionSurfaces?.cloudflare?.workerName,
1545
+ publicUrlEnv:
1546
+ backendFederation?.executionSurfaces?.cloudflare?.publicUrlEnv,
1547
+ zephyrRuntime:
1548
+ backendFederation?.executionSurfaces?.cloudflare?.zephyr?.runtime,
1549
+ },
1550
+ node: {
1551
+ kind: backendFederation?.executionSurfaces?.node?.kind,
1552
+ remoteName: backendFederation?.executionSurfaces?.node?.remoteName,
1553
+ manifestEnv: backendFederation?.executionSurfaces?.node?.manifestEnv,
1554
+ manifestUrl: backendFederation?.executionSurfaces?.node?.manifestUrl,
1555
+ containerEntry:
1556
+ backendFederation?.executionSurfaces?.node?.containerEntry,
1557
+ remoteType: backendFederation?.executionSurfaces?.node?.remoteType,
1558
+ expose: backendFederation?.executionSurfaces?.node?.expose,
1559
+ },
1560
+ compatibility: {
1561
+ contractVersion: backendFederation?.compatibility?.contractVersion,
1562
+ },
1563
+ topLevelManifestUrl: backendFederation?.manifestUrl,
1564
+ topLevelContainerEntry: backendFederation?.containerEntry,
1565
+ });
1566
+ const expectedBackendFederationSubset = vertical => ({
1567
+ role: 'microvertical-server',
1568
+ name: expectedBackendFederationName(vertical),
1569
+ runtimeFramework: 'effect',
1570
+ strictEffectApproach: true,
1571
+ exposeRuntime: `${vertical.path}/api/index.ts`,
1572
+ // The RPC surface exposes no REST readiness endpoint, so its backend
1573
+ // federation contract omits the readiness probes (G7a).
1574
+ exposeReadiness:
1575
+ vertical.apiProtocol === 'rpc'
1576
+ ? undefined
1577
+ : `${vertical.apiPrefix}/${vertical.stem}/readiness`,
1578
+ versionBoundary: {
1579
+ invariant: 'web-and-api-same-build',
1580
+ uiManifestUrl: expectedManifestUrl(vertical),
1581
+ apiReadiness:
1582
+ vertical.apiProtocol === 'rpc'
1583
+ ? undefined
1584
+ : `${vertical.apiPrefix}/${vertical.stem}/readiness`,
1585
+ },
1586
+ cloudflare: {
1587
+ kind: 'cloudflare-worker-snapshot',
1588
+ workerName: expectedCloudflareWorkerName(vertical),
1589
+ publicUrlEnv: expectedPublicUrlEnv(vertical),
1590
+ zephyrRuntime: 'ssr-worker',
1591
+ },
1592
+ node: {
1593
+ kind: 'node-mf-runtime',
1594
+ remoteName: expectedBackendFederationName(vertical),
1595
+ manifestEnv: expectedBackendManifestEnv(vertical),
1596
+ manifestUrl: expectedBackendManifestUrl(vertical),
1597
+ containerEntry: expectedBackendContainerEntry(vertical),
1598
+ remoteType: 'commonjs-module',
1599
+ expose: './effect-api',
1600
+ },
1601
+ compatibility: {
1602
+ contractVersion: 'microvertical-server-effect-v1',
1603
+ },
1604
+ topLevelManifestUrl: undefined,
1605
+ topLevelContainerEntry: undefined,
1606
+ });
1607
+ const serverExecutionSubset = serverExecution => ({
1608
+ apiBaseUrl: serverExecution?.apiBaseUrl,
1609
+ versionBoundary: serverExecution?.versionBoundary,
1610
+ cloudflareKind: serverExecution?.cloudflare?.kind,
1611
+ cloudflareWorkerName: serverExecution?.cloudflare?.workerName,
1612
+ nodeKind: serverExecution?.node?.kind,
1613
+ nodeManifestUrl: serverExecution?.node?.manifestUrl,
1614
+ nodeContainerEntry: serverExecution?.node?.containerEntry,
1615
+ });
1616
+ const expectedServerExecutionSubset = vertical => ({
1617
+ apiBaseUrl: expectedApiUrl(vertical),
1618
+ versionBoundary: 'web-and-api-same-build',
1619
+ cloudflareKind: 'cloudflare-worker-snapshot',
1620
+ cloudflareWorkerName: expectedCloudflareWorkerName(vertical),
1621
+ nodeKind: 'node-mf-runtime',
1622
+ nodeManifestUrl: expectedBackendManifestUrl(vertical),
1623
+ nodeContainerEntry: expectedBackendContainerEntry(vertical),
1624
+ });
775
1625
  const remoteContractSubset = remote => ({
776
1626
  id: remote?.id,
777
1627
  name: remote?.name,
@@ -792,25 +1642,78 @@ const requiredMicroVerticalPaths = vertical => [
792
1642
  `${vertical.path}/tsconfig.json`,
793
1643
  `${vertical.path}/tsconfig.mf-types.json`,
794
1644
  `${vertical.path}/modern.config.ts`,
795
- `${vertical.path}/module-federation.config.ts`,
796
- `${vertical.path}/api/index.ts`,
797
- `${vertical.path}/shared/api.ts`,
798
- `${vertical.path}/src/api/${vertical.stem}-client.ts`,
799
1645
  `${vertical.path}/src/modern-app-env.d.ts`,
800
1646
  `${vertical.path}/src/modern.runtime.ts`,
801
- `${vertical.path}/src/federation-entry.tsx`,
802
- ...vertical.componentPaths,
803
1647
  `${vertical.path}/locales/en/translation.json`,
804
1648
  `${vertical.path}/locales/en/${vertical.namespace}.json`,
805
1649
  `${vertical.path}/locales/cs/translation.json`,
806
1650
  `${vertical.path}/locales/cs/${vertical.namespace}.json`,
807
- `${vertical.path}/src/routes/index.css`,
808
- `${vertical.path}/src/routes/layout.tsx`,
809
- `${vertical.path}/src/routes/ultramodern-route-head.tsx`,
810
- `${vertical.path}/src/routes/ultramodern-route-metadata.ts`,
811
- `${vertical.path}/src/routes/[lang]/page.tsx`,
812
- ...vertical.routePagePaths,
813
- ...vertical.routeMetaPaths,
1651
+ ...(vertical.emitsUi
1652
+ ? [
1653
+ `${vertical.path}/module-federation.config.ts`,
1654
+ `${vertical.path}/src/federation-entry.tsx`,
1655
+ ...vertical.componentPaths,
1656
+ `${vertical.path}/src/routes/index.css`,
1657
+ `${vertical.path}/src/routes/layout.tsx`,
1658
+ `${vertical.path}/src/routes/ultramodern-route-head.tsx`,
1659
+ `${vertical.path}/src/routes/ultramodern-route-metadata.ts`,
1660
+ `${vertical.path}/src/routes/[lang]/page.tsx`,
1661
+ ...(vertical.exposes.includes('./Widget')
1662
+ ? [
1663
+ `${vertical.path}/src/routes/[lang]/_mf/fragment/widget/page.tsx`,
1664
+ ]
1665
+ : []),
1666
+ ...vertical.routePagePaths,
1667
+ ...vertical.routeMetaPaths,
1668
+ ]
1669
+ : []),
1670
+ ...(vertical.emitsApi
1671
+ ? [
1672
+ `${vertical.path}/backend-federation.config.ts`,
1673
+ `${vertical.path}/api/effect-api.ts`,
1674
+ `${vertical.path}/api/index.ts`,
1675
+ `${vertical.path}/${vertical.apiContractPath}`,
1676
+ `${vertical.path}/${vertical.apiClientPath}`,
1677
+ ]
1678
+ : []),
1679
+ ];
1680
+ // UI/MF artifacts an `api-only` unit must NOT emit (headless invariant), and
1681
+ // API/BFF artifacts a `ui-only`/Horizontal Remote unit must NOT emit.
1682
+ const forbiddenMicroVerticalPaths = vertical => [
1683
+ ...(vertical.emitsUi
1684
+ ? []
1685
+ : [
1686
+ `${vertical.path}/module-federation.config.ts`,
1687
+ `${vertical.path}/src/federation-entry.tsx`,
1688
+ // The federated demo `./Widget` component (descriptors.ts:127 ->
1689
+ // remoteComponentOutputPath) is a UI-only artifact; a headless api-only
1690
+ // unit exposes no browser component and must not ship it.
1691
+ `${vertical.path}/src/components/${vertical.domain ?? vertical.id}-widget.tsx`,
1692
+ `${vertical.path}/src/routes/layout.tsx`,
1693
+ `${vertical.path}/src/routes/[lang]/page.tsx`,
1694
+ `${vertical.path}/src/routes/[lang]/_mf/fragment/widget/page.tsx`,
1695
+ // A headless api-only unit renders no browser surface, so it must not
1696
+ // ship route components, the colocated route-metadata/head modules, or
1697
+ // the colocated `[lang]/route.meta.ts` route-meta file.
1698
+ `${vertical.path}/src/routes/[lang]/route.meta.ts`,
1699
+ `${vertical.path}/src/routes/ultramodern-route-head.tsx`,
1700
+ `${vertical.path}/src/routes/ultramodern-route-metadata.ts`,
1701
+ `${vertical.path}/src/routes/index.css`,
1702
+ ]),
1703
+ ...(vertical.emitsApi
1704
+ ? []
1705
+ : [
1706
+ `${vertical.path}/shared/api.ts`,
1707
+ // A ui-only/Horizontal Remote unit carries no API contract in either
1708
+ // protocol (neither the REST `shared/api.ts` nor the RPC `shared/rpc.ts`)
1709
+ // and no generated API client.
1710
+ `${vertical.path}/shared/rpc.ts`,
1711
+ `${vertical.path}/backend-federation.config.ts`,
1712
+ `${vertical.path}/api/index.ts`,
1713
+ `${vertical.path}/api/effect-api.ts`,
1714
+ `${vertical.path}/src/api/${vertical.domain ?? vertical.id}-client.ts`,
1715
+ `${vertical.path}/src/api/${vertical.domain ?? vertical.id}-rpc-client.ts`,
1716
+ ]),
814
1717
  ];
815
1718
  const assertRequiredVerticalFile = vertical => relativePath => {
816
1719
  assertSelfCheck(
@@ -820,6 +1723,14 @@ const assertRequiredVerticalFile = vertical => relativePath => {
820
1723
  'restore the generated MicroVertical files or rerun the MicroVertical generator',
821
1724
  );
822
1725
  };
1726
+ const assertForbiddenVerticalFile = vertical => relativePath => {
1727
+ assertSelfCheck(
1728
+ !fs.existsSync(path.join(root, relativePath)),
1729
+ `forbidden files for ${vertical.id}`,
1730
+ `Unexpected ${relativePath} for a ${vertical.surfaceProfile} unit`,
1731
+ `remove ${relativePath}; a ${vertical.surfaceProfile} unit does not emit this surface`,
1732
+ );
1733
+ };
823
1734
  const assertMicroVerticalContractGraph = ({
824
1735
  generatedContract,
825
1736
  topology,
@@ -829,8 +1740,16 @@ const assertMicroVerticalContractGraph = ({
829
1740
  }) => {
830
1741
  const expectedVerticalIds = fullStackVerticals.map(vertical => vertical.id);
831
1742
  const expectedAppIds = ['shell-super-app', ...expectedVerticalIds];
832
- const expectedShellRemotes = fullStackVerticals.map(
833
- expectedRemoteContractSubset,
1743
+ const expectedShellVerticalIds = expectedPrimaryShellVerticalIds;
1744
+ const expectedShellRemotes = expectedShellVerticalIds.flatMap(
1745
+ verticalId => {
1746
+ const vertical = fullStackVerticals.find(
1747
+ candidate => candidate.id === verticalId,
1748
+ );
1749
+ return vertical === undefined
1750
+ ? []
1751
+ : [expectedRemoteContractSubset(vertical)];
1752
+ },
834
1753
  );
835
1754
 
836
1755
  assertObject(
@@ -881,7 +1800,7 @@ const assertMicroVerticalContractGraph = ({
881
1800
 
882
1801
  assertSameJson(
883
1802
  topology.shell.verticalRefs ?? [],
884
- expectedVerticalIds,
1803
+ expectedShellVerticalIds,
885
1804
  'topology/reference-topology.json shell.verticalRefs',
886
1805
  'restore generated topology shell references',
887
1806
  );
@@ -912,7 +1831,7 @@ const assertMicroVerticalContractGraph = ({
912
1831
  );
913
1832
  assertSameJson(
914
1833
  shellContract.moduleFederation?.verticalRefs ?? [],
915
- expectedVerticalIds,
1834
+ expectedShellVerticalIds,
916
1835
  `${generatedContractLabel} shell moduleFederation.verticalRefs`,
917
1836
  'regenerate the generated shell Module Federation contract',
918
1837
  );
@@ -925,11 +1844,11 @@ const assertMicroVerticalContractGraph = ({
925
1844
  assertSameJson(
926
1845
  shellContract.ssr,
927
1846
  {
928
- mode: 'string',
1847
+ mode: 'stream',
929
1848
  moduleFederationAppSSR: true,
930
1849
  },
931
1850
  `${generatedContractLabel} shell SSR contract`,
932
- 'restore generated string SSR Module Federation settings',
1851
+ 'restore generated streaming SSR Module Federation settings',
933
1852
  );
934
1853
 
935
1854
  for (const vertical of fullStackVerticals) {
@@ -964,10 +1883,14 @@ const assertMicroVerticalContractGraph = ({
964
1883
  remoteContractSubset,
965
1884
  ),
966
1885
  },
967
- api: {
968
- prefix: topologyEntry.api?.bff?.prefix,
969
- serverEntry: topologyEntry.api?.serverEntry,
970
- },
1886
+ ...(vertical.emitsApi
1887
+ ? {
1888
+ api: {
1889
+ prefix: topologyEntry.api?.bff?.prefix,
1890
+ serverEntry: topologyEntry.api?.serverEntry,
1891
+ },
1892
+ }
1893
+ : {}),
971
1894
  },
972
1895
  {
973
1896
  kind: 'vertical',
@@ -980,15 +1903,71 @@ const assertMicroVerticalContractGraph = ({
980
1903
  verticalRefs: expectedRefs,
981
1904
  remotes: expectedRemoteSubsetsForRefs(expectedRefs),
982
1905
  },
983
- api: {
984
- prefix: vertical.apiPrefix,
985
- serverEntry: `${vertical.path}/api/index.ts`,
986
- },
1906
+ ...(vertical.emitsApi
1907
+ ? {
1908
+ api: {
1909
+ prefix: vertical.apiPrefix,
1910
+ serverEntry: `${vertical.path}/api/index.ts`,
1911
+ },
1912
+ }
1913
+ : {}),
987
1914
  },
988
1915
  `topology/reference-topology.json verticals.${vertical.id}`,
989
1916
  'restore generated topology vertical entries',
990
1917
  );
991
-
1918
+ if (vertical.emitsApi) {
1919
+ assertSameJson(
1920
+ backendFederationSubset(topologyEntry.backendFederation),
1921
+ expectedBackendFederationSubset(vertical),
1922
+ `topology/reference-topology.json verticals.${vertical.id}.backendFederation`,
1923
+ 'restore generated MicroVertical server execution contract',
1924
+ );
1925
+ }
1926
+
1927
+ if (vertical.deliveryUnit) {
1928
+ const compactApp = findById(
1929
+ ultramodernConfig.topology?.apps,
1930
+ vertical.id,
1931
+ );
1932
+ const expectedDeliveryUnit = deliveryUnitBlock(
1933
+ expectedDeliveryUnitFor(vertical),
1934
+ );
1935
+ assertSameJson(
1936
+ deliveryUnitBlock(compactApp?.deliveryUnit),
1937
+ expectedDeliveryUnit,
1938
+ `${generatedContractLabel} topology.apps.${vertical.id}.deliveryUnit`,
1939
+ deliveryUnitIdentityFixArea,
1940
+ );
1941
+ if (vertical.emitsApi) {
1942
+ assertSameJson(
1943
+ deliveryUnitBlock(compactApp?.backendFederation?.deliveryUnit),
1944
+ expectedDeliveryUnit,
1945
+ `${generatedContractLabel} topology.apps.${vertical.id}.backendFederation.deliveryUnit`,
1946
+ deliveryUnitIdentityFixArea,
1947
+ );
1948
+ }
1949
+ assertSameJson(
1950
+ deliveryUnitBlock(topologyEntry.deliveryUnit),
1951
+ expectedDeliveryUnit,
1952
+ `topology/reference-topology.json verticals.${vertical.id}.deliveryUnit`,
1953
+ deliveryUnitIdentityFixArea,
1954
+ );
1955
+ if (vertical.emitsApi) {
1956
+ assertSameJson(
1957
+ deliveryUnitBlock(topologyEntry.backendFederation?.deliveryUnit),
1958
+ expectedDeliveryUnit,
1959
+ `topology/reference-topology.json verticals.${vertical.id}.backendFederation.deliveryUnit`,
1960
+ deliveryUnitIdentityFixArea,
1961
+ );
1962
+ assertSelfCheck(
1963
+ topologyEntry.backendFederation?.versionBoundary?.identityRoot === 'deliveryUnit',
1964
+ `topology/reference-topology.json verticals.${vertical.id}.backendFederation.versionBoundary.identityRoot`,
1965
+ `Expected "deliveryUnit", found ${formatJson(topologyEntry.backendFederation?.versionBoundary?.identityRoot)}`,
1966
+ deliveryUnitIdentityFixArea,
1967
+ );
1968
+ }
1969
+ }
1970
+
992
1971
  assertObject(
993
1972
  ownershipEntry,
994
1973
  `topology/ownership.json owners.${vertical.id}`,
@@ -1013,31 +1992,67 @@ const assertMicroVerticalContractGraph = ({
1013
1992
  `topology/local-overlays/development.json ports.${vertical.id}`,
1014
1993
  'restore generated local development port overlay',
1015
1994
  );
1016
- assertSameJson(
1017
- overlay.manifests[vertical.id],
1018
- expectedManifestUrl(vertical),
1019
- `topology/local-overlays/development.json manifests.${vertical.id}`,
1020
- 'restore generated local Module Federation manifest overlay',
1021
- );
1022
- assertSameJson(
1023
- overlay.apis[vertical.id],
1024
- expectedApiUrl(vertical),
1025
- `topology/local-overlays/development.json apis.${vertical.id}`,
1026
- 'restore generated local API overlay',
1027
- );
1995
+ if (vertical.emitsUi) {
1996
+ assertSameJson(
1997
+ overlay.manifests[vertical.id],
1998
+ expectedManifestUrl(vertical),
1999
+ `topology/local-overlays/development.json manifests.${vertical.id}`,
2000
+ 'restore generated local Module Federation manifest overlay',
2001
+ );
2002
+ }
2003
+ if (vertical.emitsApi) {
2004
+ assertSameJson(
2005
+ overlay.apis[vertical.id],
2006
+ expectedApiUrl(vertical),
2007
+ `topology/local-overlays/development.json apis.${vertical.id}`,
2008
+ 'restore generated local API overlay',
2009
+ );
2010
+ assertSameJson(
2011
+ serverExecutionSubset(overlay.serverExecution?.[vertical.id]),
2012
+ expectedServerExecutionSubset(vertical),
2013
+ `topology/local-overlays/development.json serverExecution.${vertical.id}`,
2014
+ 'restore generated local MicroVertical server execution overlay',
2015
+ );
2016
+ }
1028
2017
 
1029
- assertSameJson(
1030
- shellPackage.dependencies?.[vertical.packageName],
1031
- 'workspace:*',
1032
- `apps/shell-super-app/package.json dependencies.${vertical.packageName}`,
1033
- 'restore shell dependency for the MicroVertical API consumer',
1034
- );
1035
- assertSameJson(
1036
- shellPackage['zephyr:dependencies']?.[vertical.zephyrAlias],
1037
- `${vertical.packageName}@workspace:*`,
1038
- `apps/shell-super-app/package.json zephyr:dependencies.${vertical.zephyrAlias}`,
1039
- 'restore shell Zephyr dependency metadata for the MicroVertical',
1040
- );
2018
+
2019
+ // Plain workspace dependencies and Zephyr metadata are gated
2020
+ // INDEPENDENTLY (G2a/G28): every shell depends on each API-emitting
2021
+ // vertical (its vertical-clients.ts re-exports the client) regardless of
2022
+ // UI composition, while Zephyr metadata follows UI composition refs only.
2023
+ const shellPackagesForDependencyChecks = [
2024
+ {
2025
+ id: 'shell-super-app',
2026
+ path: 'apps/shell-super-app',
2027
+ pkg: shellPackage,
2028
+ uiRefs: expectedShellVerticalIds,
2029
+ },
2030
+ ...expectedAdditionalShells.map(additionalShell => ({
2031
+ id: additionalShell.id,
2032
+ path: additionalShell.path,
2033
+ pkg: readJson(`${additionalShell.path}/package.json`),
2034
+ uiRefs: additionalShell.verticalRefs ?? [],
2035
+ })),
2036
+ ];
2037
+ for (const shellEntry of shellPackagesForDependencyChecks) {
2038
+ const composed = shellEntry.uiRefs.includes(vertical.id);
2039
+ if (vertical.emitsApi || composed) {
2040
+ assertSameJson(
2041
+ shellEntry.pkg.dependencies?.[vertical.packageName],
2042
+ 'workspace:*',
2043
+ `${shellEntry.path}/package.json dependencies.${vertical.packageName}`,
2044
+ 'restore shell dependency for the MicroVertical consumer',
2045
+ );
2046
+ }
2047
+ if (composed) {
2048
+ assertSameJson(
2049
+ shellEntry.pkg['zephyr:dependencies']?.[vertical.zephyrAlias],
2050
+ `${vertical.packageName}@workspace:*`,
2051
+ `${shellEntry.path}/package.json zephyr:dependencies.${vertical.zephyrAlias}`,
2052
+ 'restore shell Zephyr dependency metadata for the MicroVertical',
2053
+ );
2054
+ }
2055
+ }
1041
2056
 
1042
2057
  assertObject(
1043
2058
  contractEntry,
@@ -1057,11 +2072,15 @@ const assertMicroVerticalContractGraph = ({
1057
2072
  remoteContractSubset,
1058
2073
  ),
1059
2074
  },
1060
- api: {
1061
- prefix: contractEntry.api?.prefix,
1062
- contract: contractEntry.api?.contract,
1063
- client: contractEntry.api?.client,
1064
- },
2075
+ ...(vertical.emitsApi
2076
+ ? {
2077
+ api: {
2078
+ prefix: contractEntry.api?.prefix,
2079
+ contract: contractEntry.api?.contract,
2080
+ client: contractEntry.api?.client,
2081
+ },
2082
+ }
2083
+ : {}),
1065
2084
  ssr: contractEntry.ssr,
1066
2085
  },
1067
2086
  {
@@ -1074,13 +2093,19 @@ const assertMicroVerticalContractGraph = ({
1074
2093
  verticalRefs: expectedRefs,
1075
2094
  remotes: expectedRemoteSubsetsForRefs(expectedRefs),
1076
2095
  },
1077
- api: {
1078
- prefix: vertical.apiPrefix,
1079
- contract: './api',
1080
- client: './api/client',
1081
- },
2096
+ ...(vertical.emitsApi
2097
+ ? {
2098
+ api: {
2099
+ prefix: vertical.apiPrefix,
2100
+ // Protocol-aware exports: `rest` -> `./api`/`./api/client`,
2101
+ // `rpc` -> `./api`/`./api/rpc-client`.
2102
+ contract: vertical.apiContractExport,
2103
+ client: vertical.apiClientExport,
2104
+ },
2105
+ }
2106
+ : {}),
1082
2107
  ssr: {
1083
- mode: 'string',
2108
+ mode: 'stream',
1084
2109
  moduleFederationAppSSR: true,
1085
2110
  },
1086
2111
  },
@@ -1116,19 +2141,34 @@ const assertProjectReferenceEmitConfig = (tsConfig, packagePath) => {
1116
2141
  );
1117
2142
  };
1118
2143
  const assertTsConfigReferenceGraph = () => {
2144
+ const baseTsConfig = readJson('tsconfig.base.json');
1119
2145
  const rootTsConfig = readJson('tsconfig.json');
1120
2146
  const shellTsConfig = readJson('apps/shell-super-app/tsconfig.json');
1121
2147
  const shellMfTypesTsConfig = readJson(
1122
2148
  'apps/shell-super-app/tsconfig.mf-types.json',
1123
2149
  );
2150
+ const additionalShellPaths = (
2151
+ workspaceValidationContract.structuralShellPolicy?.shells ?? []
2152
+ )
2153
+ .filter(shell => shell.id !== 'shell-super-app')
2154
+ .map(shell => shell.packageDir);
1124
2155
  const expectedRootReferences = [
1125
2156
  ...sharedPackagePaths,
1126
2157
  'apps/shell-super-app',
1127
2158
  ...fullStackVerticals.map(vertical => vertical.path),
2159
+ ...additionalShellPaths,
1128
2160
  ].map(referencePath => ({ path: referencePath }));
1129
2161
  const expectedShellReferences = [
1130
2162
  ...sharedPackagePaths,
1131
- ...fullStackVerticals.map(vertical => vertical.path),
2163
+ ...(topology.shell?.verticalRefs ?? [])
2164
+ .map(verticalRef =>
2165
+ fullStackVerticals.find(candidate => candidate.id === verticalRef),
2166
+ )
2167
+ .filter(Boolean)
2168
+ // The shell only project-references verticals whose API client types it
2169
+ // imports; UI-only remotes are federated at runtime, not type-referenced.
2170
+ .filter(vertical => vertical.emitsApi)
2171
+ .map(vertical => vertical.path),
1132
2172
  ].map(referencePath =>
1133
2173
  referenceFrom('apps/shell-super-app', referencePath),
1134
2174
  );
@@ -1151,6 +2191,13 @@ const assertTsConfigReferenceGraph = () => {
1151
2191
  'apps/shell-super-app/tsconfig.json references',
1152
2192
  'restore the generated shell project-reference graph',
1153
2193
  );
2194
+ assert(baseTsConfig.compilerOptions?.skipLibCheck !== true, 'tsconfig.base.json must not use skipLibCheck');
2195
+ assertSameJson(
2196
+ shellTsConfig.include ?? [],
2197
+ ['src', 'locales/**/*.json', 'package.json', 'shared'],
2198
+ 'apps/shell-super-app/tsconfig.json include',
2199
+ 'restore the generated shell typecheck boundary',
2200
+ );
1154
2201
  assertProjectReferenceEmitConfig(
1155
2202
  shellTsConfig,
1156
2203
  'apps/shell-super-app',
@@ -1158,7 +2205,7 @@ const assertTsConfigReferenceGraph = () => {
1158
2205
  assertSameJson(
1159
2206
  shellMfTypesTsConfig,
1160
2207
  {
1161
- extends: './tsconfig.json',
2208
+ extends: '../../tsconfig.base.json',
1162
2209
  include: ['src/modern-app-env.d.ts'],
1163
2210
  },
1164
2211
  'apps/shell-super-app/tsconfig.mf-types.json',
@@ -1191,23 +2238,77 @@ const assertTsConfigReferenceGraph = () => {
1191
2238
  `${vertical.path}/tsconfig.json references`,
1192
2239
  'restore the generated MicroVertical project-reference graph',
1193
2240
  );
2241
+ assertSameJson(
2242
+ verticalTsConfig.include ?? [],
2243
+ [
2244
+ 'src',
2245
+ 'locales/**/*.json',
2246
+ 'package.json',
2247
+ 'shared',
2248
+ ...(vertical.emitsApi ? ['api'] : []),
2249
+ ],
2250
+ `${vertical.path}/tsconfig.json include`,
2251
+ 'restore the generated MicroVertical typecheck boundary',
2252
+ );
1194
2253
  assertProjectReferenceEmitConfig(verticalTsConfig, vertical.path);
1195
2254
  assertSameJson(
1196
2255
  verticalMfTypesTsConfig,
1197
2256
  {
1198
- extends: './tsconfig.json',
1199
- include: [
1200
- 'src/federation-entry.tsx',
1201
- ...vertical.componentPaths.map(componentPath =>
1202
- componentPath.replace(`${vertical.path}/`, ''),
1203
- ),
1204
- 'src/modern-app-env.d.ts',
1205
- ],
2257
+ extends: '../../tsconfig.base.json',
2258
+ // A headless (api-only) unit exposes no Module Federation surface, so
2259
+ // its DTS boundary only covers the ambient env declarations (G2a).
2260
+ include: vertical.emitsUi
2261
+ ? [
2262
+ 'src/federation-entry.tsx',
2263
+ ...vertical.componentPaths.map(componentPath =>
2264
+ componentPath.replace(`${vertical.path}/`, ''),
2265
+ ),
2266
+ 'src/modern-app-env.d.ts',
2267
+ ]
2268
+ : ['src/modern-app-env.d.ts'],
1206
2269
  },
1207
2270
  `${vertical.path}/tsconfig.mf-types.json`,
1208
2271
  'restore the generated MicroVertical Module Federation DTS boundary',
1209
2272
  );
1210
2273
  }
2274
+
2275
+ for (const shell of expectedAdditionalShells) {
2276
+ const shellTsConfig = readJson(`${shell.path}/tsconfig.json`);
2277
+ const shellMfTypesTsConfig = readJson(
2278
+ `${shell.path}/tsconfig.mf-types.json`,
2279
+ );
2280
+ const expectedShellReferences = [
2281
+ ...sharedPackagePaths,
2282
+ ...(shell.verticalRefs ?? [])
2283
+ .map(verticalRef =>
2284
+ fullStackVerticals.find(candidate => candidate.id === verticalRef),
2285
+ )
2286
+ .filter(Boolean)
2287
+ .map(referencedVertical => referencedVertical.path),
2288
+ ].map(referencePath => referenceFrom(shell.path, referencePath));
2289
+ assertSameJson(
2290
+ shellTsConfig.references ?? [],
2291
+ expectedShellReferences,
2292
+ `${shell.path}/tsconfig.json references`,
2293
+ 'restore the generated additional-shell project-reference graph',
2294
+ );
2295
+ assertSameJson(
2296
+ shellTsConfig.include ?? [],
2297
+ ['src', 'locales/**/*.json', 'package.json', 'shared'],
2298
+ `${shell.path}/tsconfig.json include`,
2299
+ 'restore the generated additional-shell typecheck boundary',
2300
+ );
2301
+ assertProjectReferenceEmitConfig(shellTsConfig, shell.path);
2302
+ assertSameJson(
2303
+ shellMfTypesTsConfig,
2304
+ {
2305
+ extends: '../../tsconfig.base.json',
2306
+ include: ['src/modern-app-env.d.ts'],
2307
+ },
2308
+ `${shell.path}/tsconfig.mf-types.json`,
2309
+ 'restore the generated additional-shell Module Federation DTS boundary',
2310
+ );
2311
+ }
1211
2312
  };
1212
2313
  const packageJsonFiles = startDir => {
1213
2314
  const files = [];
@@ -1216,7 +2317,7 @@ const packageJsonFiles = startDir => {
1216
2317
  const current = queue.shift();
1217
2318
  for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
1218
2319
  if (
1219
- ['.git', '.output', 'dist', 'node_modules', 'repos'].includes(
2320
+ ['.git', '.output', '.zerops', 'dist', 'node_modules', 'repos'].includes(
1220
2321
  entry.name,
1221
2322
  )
1222
2323
  ) {
@@ -1235,35 +2336,52 @@ const packageJsonFiles = startDir => {
1235
2336
  const modernDependencyNames = packageJson =>
1236
2337
  [
1237
2338
  ...new Set(
1238
- ['dependencies', 'devDependencies']
2339
+ [
2340
+ 'dependencies',
2341
+ 'devDependencies',
2342
+ 'optionalDependencies',
2343
+ 'peerDependencies',
2344
+ ]
1239
2345
  .flatMap(section => Object.keys(packageJson[section] ?? {}))
1240
2346
  .filter(packageName => packageName.startsWith('@modern-js/')),
1241
2347
  ),
1242
2348
  ];
1243
2349
  const assertModernPackageCohort = () => {
1244
- const modernPackageNames = packageSource.modernPackages?.packages;
1245
- assert(
1246
- Array.isArray(modernPackageNames) && modernPackageNames.length > 0,
1247
- 'Package source metadata must list the Modern package cohort',
1248
- );
1249
- for (const packageName of modernPackageNames) {
1250
- assert(
1251
- typeof packageName === 'string' && packageName.startsWith('@modern-js/'),
1252
- `Package source metadata contains invalid Modern package name ${packageName}`,
1253
- );
1254
- }
1255
-
2350
+ const modernPackageNames = workspaceValidationContract.cohort.modernPackages;
1256
2351
  const modernPackageNameSet = new Set(modernPackageNames);
2352
+ const observedModernPackageNames = new Set();
2353
+ const observedAppIds = [];
2354
+ // Additional shells (G28) are their own Delivery Units registered in the
2355
+ // additive `config.shells` collection and gated by the structural thin-shell
2356
+ // policy; they are deliberately kept out of the strict topology.apps cohort,
2357
+ // so their package manifests are excluded from the app-id cohort walk here.
2358
+ const additionalShellAppIds = new Set(
2359
+ (workspaceValidationContract.structuralShellPolicy?.shells ?? [])
2360
+ .map(shell => shell.id)
2361
+ .filter(id => id !== 'shell-super-app'),
2362
+ );
1257
2363
  for (const packageJsonPath of packageJsonFiles(root)) {
1258
2364
  const relativePath = path.relative(root, packageJsonPath).split(path.sep).join('/');
1259
2365
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
2366
+ if (
2367
+ typeof packageJson.modernjs?.appId === 'string' &&
2368
+ !additionalShellAppIds.has(packageJson.modernjs.appId)
2369
+ ) {
2370
+ observedAppIds.push(packageJson.modernjs.appId);
2371
+ }
1260
2372
  for (const packageName of modernDependencyNames(packageJson)) {
2373
+ observedModernPackageNames.add(packageName);
1261
2374
  assert(
1262
2375
  modernPackageNameSet.has(packageName),
1263
2376
  `${relativePath} declares ${packageName} outside package source metadata`,
1264
2377
  );
1265
2378
  }
1266
- for (const section of ['dependencies', 'devDependencies']) {
2379
+ for (const section of [
2380
+ 'dependencies',
2381
+ 'devDependencies',
2382
+ 'optionalDependencies',
2383
+ 'peerDependencies',
2384
+ ]) {
1267
2385
  for (const packageName of modernPackageNames) {
1268
2386
  const actual = packageJson[section]?.[packageName];
1269
2387
  if (actual !== undefined) {
@@ -1275,6 +2393,25 @@ const assertModernPackageCohort = () => {
1275
2393
  }
1276
2394
  }
1277
2395
  }
2396
+
2397
+ for (const packageName of modernPackageNames) {
2398
+ assert(
2399
+ observedModernPackageNames.has(packageName),
2400
+ `Modern package cohort is missing ${packageName}`,
2401
+ );
2402
+ }
2403
+ assertUniqueStrings(observedAppIds, 'generated app package manifests');
2404
+ // The app-id cohort is a SET: observedAppIds is discovered by a
2405
+ // filesystem-ordered package.json walk, while cohort.appIds is in generation
2406
+ // insertion order. Compare order-insensitively (every other cohort check uses
2407
+ // the same sorted comparison via assertSameIdCohort) so a multi-vertical
2408
+ // workspace whose insertion order differs from filesystem order still passes.
2409
+ assertSameJson(
2410
+ [...observedAppIds].toSorted(),
2411
+ [...workspaceValidationContract.cohort.appIds].toSorted(),
2412
+ 'generated app package manifest cohort',
2413
+ 'restore every generated app package manifest',
2414
+ );
1278
2415
  };
1279
2416
  const assertPublicSurfaceAssets = (appPath, publicRoutes) => {
1280
2417
  for (const relativePath of publicSurfaceManagedSourceAssetPaths) {
@@ -1286,7 +2423,8 @@ const assertPublicSurfaceContract = (appId, publicSurface) => {
1286
2423
  assert(publicSurface?.artifactLifecycle === 'build-and-deploy-output', `${appId} public surface artifacts must be build/deploy outputs`);
1287
2424
  assert(publicSurface?.generator === 'scripts/generate-public-surface-assets.mts', `${appId} public surface generator script is incorrect`);
1288
2425
  assert(publicSurface?.outputRoot === 'dist/public', `${appId} public surface dist outputRoot is incorrect`);
1289
- assert(publicSurface?.cloudflareOutputRoot === '.output/public', `${appId} public surface Cloudflare outputRoot is incorrect`);
2426
+ assert(publicSurface?.cloudflareBuildOutputRoot === 'dist-cloudflare/public', `${appId} public surface Cloudflare build outputRoot is incorrect`);
2427
+ assert(!('cloudflareOutputRoot' in (publicSurface ?? {})), `${appId} public surface must not target final .output directly`);
1290
2428
  assert(!('staticRoot' in (publicSurface ?? {})), `${appId} public surface must not point at source config/public`);
1291
2429
  assert((publicSurface?.files ?? []).includes('robots.txt'), `${appId} public surface must always emit robots.txt`);
1292
2430
  assert(publicSurface?.contentExpansion?.authoring === 'route-owned-esm-provider', `${appId} public content expansion authoring is incorrect`);
@@ -1349,21 +2487,41 @@ const extractAssetPrefixExpression = modernConfig => {
1349
2487
  assert(match?.groups?.expression, 'modern.config.ts must assign assetPrefix');
1350
2488
  return match.groups.expression;
1351
2489
  };
1352
- const assertBuildCacheBase = (appId, modernConfig) => {
2490
+ const assertTargetIsolatedBuildArtifacts = (appId, modernConfig) => {
1353
2491
  assert(
1354
- modernConfig.includes("const buildCacheTarget = cloudflareDeployEnabled ? 'cloudflare' : 'web';") &&
1355
- modernConfig.includes('const buildCacheDirectory = `node_modules/.cache/rspack-${appId}-${buildCacheTarget}`;') &&
2492
+ modernConfig.includes("const buildTarget = cloudflareDeployEnabled ? 'cloudflare' : 'web';") &&
2493
+ modernConfig.includes("const buildOutputRoot = cloudflareDeployEnabled ? 'dist-cloudflare' : 'dist';") &&
2494
+ modernConfig.includes('const buildTempDirectory = `node_modules/.modern-js-${appId}-${buildTarget}`;') &&
2495
+ modernConfig.includes('const buildCacheDirectory = `node_modules/.cache/rspack-${appId}-${buildTarget}`;') &&
2496
+ modernConfig.includes('root: buildOutputRoot,') &&
2497
+ modernConfig.includes('tempDir: buildTempDirectory,') &&
2498
+ modernConfig.includes('cacheDigest: [appId, buildTarget],') &&
1356
2499
  modernConfig.includes('cacheDirectory: buildCacheDirectory,'),
1357
- `${appId} must provide a per-app/per-target Rspack cache base directory`,
2500
+ `${appId} must isolate build output, Modern temp files, and Rspack cache by app and build target`,
1358
2501
  );
1359
2502
  };
1360
2503
  const assertCloudflareBuildSkipsDeployRebuild = (appId, packageJson) => {
2504
+ const cloudflareBuild = packageJson.scripts?.['cloudflare:build'] ?? '';
2505
+ const buildCommand = 'MODERNJS_DEPLOY=cloudflare modern build';
2506
+ const publicSurfaceCommand = `--app ${appId} --target cloudflare-dist`;
2507
+ const deployCommand =
2508
+ 'MODERNJS_DEPLOY=cloudflare modern deploy --skip-build';
1361
2509
  assert(
1362
- packageJson.scripts?.['cloudflare:build']?.includes(
1363
- 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy --skip-build',
1364
- ),
2510
+ cloudflareBuild.includes(deployCommand),
1365
2511
  `${appId} cloudflare:build must deploy with --skip-build after the explicit Cloudflare build`,
1366
2512
  );
2513
+ assert(
2514
+ cloudflareBuild.includes(publicSurfaceCommand) &&
2515
+ cloudflareBuild.indexOf(buildCommand) <
2516
+ cloudflareBuild.indexOf(publicSurfaceCommand) &&
2517
+ cloudflareBuild.indexOf(publicSurfaceCommand) <
2518
+ cloudflareBuild.indexOf(deployCommand),
2519
+ `${appId} cloudflare:build must generate public-surface assets in the explicit Cloudflare build output before deploy`,
2520
+ );
2521
+ assert(
2522
+ !/--target (?:dist|cloudflare)(?=\s|$)/u.test(cloudflareBuild),
2523
+ `${appId} cloudflare:build must not target the Node dist or final Cloudflare output directly`,
2524
+ );
1367
2525
  };
1368
2526
  const stripYamlInlineComment = value => {
1369
2527
  let quote = undefined;
@@ -1478,6 +2636,9 @@ const requiredPaths = [
1478
2636
  '.gitignore',
1479
2637
  'package.json',
1480
2638
  'pnpm-workspace.yaml',
2639
+ `patches/@module-federation__modern-js-v3@${expectedModuleFederationVersion}.patch`,
2640
+ `patches/@module-federation__bridge-react@${expectedModuleFederationVersion}.patch`,
2641
+ 'patches/effect-schema-error-type-id.patch',
1481
2642
  'tsconfig.json',
1482
2643
  'tsconfig.base.json',
1483
2644
  'oxlint.config.ts',
@@ -1485,8 +2646,6 @@ const requiredPaths = [
1485
2646
  '.github/renovate.json',
1486
2647
  '.github/workflows/ultramodern-workspace-gates.yml',
1487
2648
  '.agents/agent-reference-repos.json',
1488
- '.codex/skills-lock.json',
1489
- '.codex/rstackjs-agent-skills-LICENSE',
1490
2649
  'topology/reference-topology.json',
1491
2650
  'topology/ownership.json',
1492
2651
  'topology/local-overlays/development.json',
@@ -1494,13 +2653,18 @@ const requiredPaths = [
1494
2653
  'scripts/bootstrap-agent-skills.mts',
1495
2654
  'scripts/check-ultramodern-api-boundaries.mts',
1496
2655
  'scripts/check-ultramodern-i18n-boundaries.mts',
2656
+ ...(hasBackendSurfaces ? ['scripts/generate-node-backend-federation.mts'] : []),
1497
2657
  'scripts/generate-public-surface-assets.mts',
2658
+ 'scripts/generate-tanstack-routes.mts',
1498
2659
  'scripts/proof-cloudflare-version.mts',
2660
+ ...(hasDeliveryUnits ? ['scripts/proof-workerd-ssr.mts'] : []),
2661
+ ...(hasBackendSurfaces ? ['scripts/proof-node-backend-federation.mts'] : []),
1499
2662
  'scripts/setup-agent-reference-repos.mts',
1500
2663
  'scripts/ultramodern-performance-readiness.config.mjs',
1501
2664
  'scripts/ultramodern-performance-readiness.mts',
1502
2665
  'scripts/ultramodern-typecheck.mts',
1503
2666
  'scripts/validate-ultramodern-workspace.mts',
2667
+ 'scripts/verify-cloudflare-output.mts',
1504
2668
  'apps/shell-super-app/package.json',
1505
2669
  'apps/shell-super-app/tsconfig.json',
1506
2670
  'apps/shell-super-app/tsconfig.mf-types.json',
@@ -1515,10 +2679,13 @@ const requiredPaths = [
1515
2679
  `apps/shell-super-app/locales/cs/${shellNamespace}.json`,
1516
2680
  'apps/shell-super-app/src/routes/index.css',
1517
2681
  'apps/shell-super-app/src/routes/layout.tsx',
2682
+ 'apps/shell-super-app/src/routes/shell-frame.tsx',
1518
2683
  'apps/shell-super-app/src/routes/ultramodern-route-head.tsx',
1519
2684
  'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
2685
+ 'apps/shell-super-app/src/routes/vertical-components.tsx',
2686
+ requiredShellWorkerCompositionPath('apps/shell-super-app'),
1520
2687
  'apps/shell-super-app/src/routes/[lang]/page.tsx',
1521
- ...{{shellRouteMetaPathsJson}},
2688
+ ...shellRouteMetaPaths,
1522
2689
  'packages/shared-contracts/package.json',
1523
2690
  'packages/shared-contracts/src/index.ts',
1524
2691
  'packages/shared-contracts/tsconfig.json',
@@ -1534,12 +2701,10 @@ for (const vertical of fullStackVerticals) {
1534
2701
 
1535
2702
  if (tailwindEnabled) {
1536
2703
  requiredPaths.push(
1537
- 'apps/shell-super-app/postcss.config.mjs',
1538
2704
  'apps/shell-super-app/tailwind.config.ts',
1539
- ...fullStackVerticals.flatMap(vertical => [
1540
- `${vertical.path}/postcss.config.mjs`,
1541
- `${vertical.path}/tailwind.config.ts`,
1542
- ]),
2705
+ ...fullStackVerticals
2706
+ .filter(vertical => vertical.emitsUi)
2707
+ .flatMap(vertical => [`${vertical.path}/tailwind.config.ts`]),
1543
2708
  );
1544
2709
  }
1545
2710
 
@@ -1549,10 +2714,86 @@ for (const vertical of fullStackVerticals) {
1549
2714
  requiredMicroVerticalPaths(vertical).forEach(
1550
2715
  assertRequiredVerticalFile(vertical),
1551
2716
  );
2717
+ // Reject profile-foreign surfaces planted into a unit (e.g. a browser MF
2718
+ // config in a headless api-only unit, or an API contract in a UI-only unit).
2719
+ forbiddenMicroVerticalPaths(vertical).forEach(
2720
+ assertForbiddenVerticalFile(vertical),
2721
+ );
2722
+ }
2723
+ for (const shell of expectedAdditionalShells) {
2724
+ requiredPaths.push(
2725
+ `${shell.path}/package.json`,
2726
+ `${shell.path}/tsconfig.json`,
2727
+ `${shell.path}/tsconfig.mf-types.json`,
2728
+ `${shell.path}/modern.config.ts`,
2729
+ `${shell.path}/module-federation.config.ts`,
2730
+ `${shell.path}/src/modern-app-env.d.ts`,
2731
+ `${shell.path}/src/modern.runtime.ts`,
2732
+ `${shell.path}/src/api/vertical-clients.ts`,
2733
+ `${shell.path}/locales/en/translation.json`,
2734
+ `${shell.path}/locales/en/${shellNamespace}.json`,
2735
+ `${shell.path}/locales/cs/translation.json`,
2736
+ `${shell.path}/locales/cs/${shellNamespace}.json`,
2737
+ `${shell.path}/src/routes/index.css`,
2738
+ `${shell.path}/src/routes/layout.tsx`,
2739
+ `${shell.path}/src/routes/shell-frame.tsx`,
2740
+ `${shell.path}/src/routes/vertical-components.tsx`,
2741
+ requiredShellWorkerCompositionPath(shell.path),
2742
+ `${shell.path}/src/routes/ultramodern-route-head.tsx`,
2743
+ `${shell.path}/src/routes/ultramodern-route-metadata.ts`,
2744
+ `${shell.path}/src/routes/[lang]/page.tsx`,
2745
+ ...shellRouteMetaPaths.map(relativePath =>
2746
+ relativePath.replace(/^apps\/shell-super-app/u, shell.path),
2747
+ ),
2748
+ );
2749
+ if (tailwindEnabled) {
2750
+ requiredPaths.push(`${shell.path}/tailwind.config.ts`);
2751
+ }
1552
2752
  }
1553
2753
  for (const requiredPath of requiredPaths) {
1554
2754
  assertExists(requiredPath);
1555
2755
  }
2756
+ // Agent skills metadata may live under .agents/ (agents-standard layout) or
2757
+ // .codex/ (legacy scaffold default).
2758
+ assertAnyOf(['.agents/skills-lock.json', '.codex/skills-lock.json']);
2759
+ assertAnyOf([
2760
+ '.agents/rstackjs-agent-skills-LICENSE',
2761
+ '.codex/rstackjs-agent-skills-LICENSE',
2762
+ ]);
2763
+ const pnpmWorkspace = readText('pnpm-workspace.yaml');
2764
+ assert(
2765
+ pnpmWorkspace.includes(`'@effect/opentelemetry': ${expectedEffectVersion}`),
2766
+ 'pnpm-workspace.yaml must override @effect/opentelemetry to the generated Effect cohort',
2767
+ );
2768
+ assert(
2769
+ pnpmWorkspace.includes(`effect: ${expectedEffectVersion}`),
2770
+ 'pnpm-workspace.yaml must override effect to the generated Effect cohort',
2771
+ );
2772
+ assert(
2773
+ pnpmWorkspace.includes(
2774
+ `'@module-federation/modern-js-v3@${expectedModuleFederationVersion}': patches/@module-federation__modern-js-v3@${expectedModuleFederationVersion}.patch`,
2775
+ ),
2776
+ 'pnpm-workspace.yaml must patch the generated Module Federation Modern.js integration cohort',
2777
+ );
2778
+ assert(
2779
+ pnpmWorkspace.includes(
2780
+ `'@module-federation/bridge-react@${expectedModuleFederationVersion}': patches/@module-federation__bridge-react@${expectedModuleFederationVersion}.patch`,
2781
+ ),
2782
+ 'pnpm-workspace.yaml must patch the generated Module Federation React bridge cohort',
2783
+ );
2784
+ assert(
2785
+ [
2786
+ `'effect@${expectedEffectVersion}'`,
2787
+ `effect@${expectedEffectVersion}`,
2788
+ ].some(effectPatchKey =>
2789
+ pnpmWorkspace.includes(
2790
+ `${effectPatchKey}: patches/effect-schema-error-type-id.patch`,
2791
+ ),
2792
+ ),
2793
+ 'pnpm-workspace.yaml must patch the generated Effect declaration cohort',
2794
+ );
2795
+ assertWorkspaceValidationContract(workspaceValidationContract);
2796
+ assertGeneratedSurfacePolicy();
1556
2797
  for (const oldRemotePath of oldRemotePaths) {
1557
2798
  assertNotExists(oldRemotePath);
1558
2799
  }
@@ -1561,13 +2802,23 @@ for (const retiredMetadataPath of retiredMetadataPaths) {
1561
2802
  }
1562
2803
  const rootPackage = readJson('package.json');
1563
2804
  const ultramodernConfig = readJson(compactConfigPath);
2805
+ const topology = readJson(
2806
+ workspaceValidationContract.metadata.referenceTopology.path,
2807
+ );
2808
+ const ownership = readJson(workspaceValidationContract.metadata.ownership.path);
2809
+ const overlay = readJson(
2810
+ workspaceValidationContract.metadata.developmentOverlay.path,
2811
+ );
2812
+ assertStructuredWorkspaceMetadata({
2813
+ ultramodernConfig,
2814
+ topology,
2815
+ ownership,
2816
+ overlay,
2817
+ });
1564
2818
  const bridgeConfig =
1565
2819
  ultramodernConfig?.bridge?.enabled === true ? ultramodernConfig.bridge : undefined;
1566
- const packageSource = readPackageSourceView(ultramodernConfig);
2820
+ const packageSource = createPackageSourceView(ultramodernConfig);
1567
2821
  const generatedContract = readGeneratedContractView(ultramodernConfig);
1568
- const topology = readJson('topology/reference-topology.json');
1569
- const ownership = readJson('topology/ownership.json');
1570
- const overlay = readJson('topology/local-overlays/development.json');
1571
2822
  const shellPackage = readJson('apps/shell-super-app/package.json');
1572
2823
 
1573
2824
  assertMicroVerticalContractGraph({
@@ -1577,6 +2828,12 @@ assertMicroVerticalContractGraph({
1577
2828
  overlay,
1578
2829
  shellPackage,
1579
2830
  });
2831
+ assertStructuredWorkspaceMetadataSemantics({
2832
+ ultramodernConfig,
2833
+ topology,
2834
+ ownership,
2835
+ overlay,
2836
+ });
1580
2837
  assertTsConfigReferenceGraph();
1581
2838
 
1582
2839
  assert(rootPackage.private === true, 'Root package must be private');
@@ -1611,6 +2868,594 @@ assert(rootPackage.modernjs?.packageSource?.strategy === packageSource.strategy,
1611
2868
  assert(packageSource.strategy === 'workspace' || packageSource.strategy === 'install', 'Package source strategy must be workspace or install');
1612
2869
  assert(packageSource.strategy === 'install' || packageSource.modernPackages?.specifier === 'workspace:*', 'Workspace package source must be explicitly backed by workspace:*');
1613
2870
  assertModernPackageCohort();
2871
+ const assertStructuralShellPolicy = () => {
2872
+ const policy = workspaceValidationContract.structuralShellPolicy;
2873
+ const compositionPolicy =
2874
+ workspaceValidationContract.federatedCompositionSourcePolicy;
2875
+ if (!policy && !compositionPolicy) {
2876
+ return;
2877
+ }
2878
+ const collectSourceFiles = absoluteDir => {
2879
+ const files = [];
2880
+ const queue = [absoluteDir];
2881
+ while (queue.length > 0) {
2882
+ const current = queue.shift();
2883
+ let entries;
2884
+ try {
2885
+ entries = fs.readdirSync(current, { withFileTypes: true });
2886
+ } catch {
2887
+ continue;
2888
+ }
2889
+ for (const entry of entries) {
2890
+ const absoluteEntry = path.join(current, entry.name);
2891
+ if (entry.isDirectory()) {
2892
+ queue.push(absoluteEntry);
2893
+ } else if (/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/u.test(entry.name)) {
2894
+ files.push(absoluteEntry);
2895
+ }
2896
+ }
2897
+ }
2898
+ return files.toSorted();
2899
+ };
2900
+ // Strip comments before scanning imports so the structural gate reasons
2901
+ // about executable source only. A specifier that lives inside a comment
2902
+ // (`// import x from '../../../verticals/catalog/src/internal'` or a /* */
2903
+ // block) must not trip the gate, and — conversely — a real import carrying a
2904
+ // magic comment (`import(/* webpackChunkName */ '...')`) must still be
2905
+ // detected once the comment is removed. The scanner is string-literal aware,
2906
+ // so a `//` inside a string (e.g. `'http://example.com'`) is preserved and
2907
+ // never mistaken for a comment start. It is also regex-literal aware, so a
2908
+ // quote inside a regex (`const q = /['"]/;`) never opens a phantom string
2909
+ // that would swallow a later real comment, and template-interpolation aware,
2910
+ // so a comment inside `${ … }` is stripped like any other code. Regex-vs-
2911
+ // division is resolved from the previous significant token; `<`/`>` are
2912
+ // deliberately excluded so JSX closing tags (`</div>`) are never mistaken for
2913
+ // regex literals. Dependency-free by construction.
2914
+ const stripSourceComments = code => {
2915
+ const isIdentifierChar = ch => ch !== '' && /[A-Za-z0-9_$]/u.test(ch);
2916
+ // Punctuation after which a `/` opens a regex literal (not a division op).
2917
+ // `<`/`>` are intentionally omitted to keep JSX closing/self-closing tags
2918
+ // (`</div>`, `<br/>`) parsing as code rather than as regex.
2919
+ const regexPrecedingPunct = new Set([
2920
+ '(', ',', '=', '[', '{', ';', ':', '!', '&', '|', '?', '+', '-', '*',
2921
+ '%', '^', '~',
2922
+ ]);
2923
+ // Keywords after which a `/` opens a regex literal.
2924
+ const regexPrecedingKeywords = new Set([
2925
+ 'return', 'typeof', 'instanceof', 'in', 'of', 'new', 'delete', 'void',
2926
+ 'do', 'else', 'yield', 'await', 'case',
2927
+ ]);
2928
+ let result = '';
2929
+ let state = 'code';
2930
+ // Last significant (non-whitespace) code character emitted and the trailing
2931
+ // identifier word — together they decide regex-vs-division for a `/`.
2932
+ let lastSignificant = '';
2933
+ let currentWord = '';
2934
+ let regexInClass = false;
2935
+ // Stack of open `${ … }` interpolations; each entry is the `{ }` nesting
2936
+ // depth reached inside that interpolation so the matching close brace pops
2937
+ // back to template text. Comments inside an interpolation are code.
2938
+ const interpolations = [];
2939
+ const regexCanFollow = () => {
2940
+ if (lastSignificant === '') {
2941
+ return true;
2942
+ }
2943
+ if (regexPrecedingPunct.has(lastSignificant)) {
2944
+ return true;
2945
+ }
2946
+ if (isIdentifierChar(lastSignificant)) {
2947
+ return regexPrecedingKeywords.has(currentWord);
2948
+ }
2949
+ return false;
2950
+ };
2951
+ const emitCodeChar = ch => {
2952
+ result += ch;
2953
+ if (/\s/u.test(ch)) {
2954
+ return;
2955
+ }
2956
+ if (isIdentifierChar(ch)) {
2957
+ currentWord += ch;
2958
+ } else {
2959
+ currentWord = '';
2960
+ }
2961
+ lastSignificant = ch;
2962
+ };
2963
+ for (let index = 0; index < code.length; index += 1) {
2964
+ const char = code[index];
2965
+ const next = index + 1 < code.length ? code[index + 1] : '';
2966
+ if (state === 'code') {
2967
+ if (char === '/' && next === '/') {
2968
+ state = 'line';
2969
+ index += 1;
2970
+ } else if (char === '/' && next === '*') {
2971
+ state = 'block';
2972
+ result += ' ';
2973
+ index += 1;
2974
+ } else if (char === '/' && regexCanFollow()) {
2975
+ state = 'regex';
2976
+ regexInClass = false;
2977
+ emitCodeChar(char);
2978
+ } else if (char === "'") {
2979
+ state = 'single';
2980
+ result += char;
2981
+ } else if (char === '"') {
2982
+ state = 'double';
2983
+ result += char;
2984
+ } else if (char === '`') {
2985
+ state = 'template';
2986
+ result += char;
2987
+ } else if (
2988
+ char === '}' &&
2989
+ interpolations.length > 0 &&
2990
+ interpolations[interpolations.length - 1] === 0
2991
+ ) {
2992
+ // The closing brace of the current `${ … }` interpolation: resume the
2993
+ // enclosing template literal text.
2994
+ interpolations.pop();
2995
+ state = 'template';
2996
+ result += char;
2997
+ lastSignificant = char;
2998
+ currentWord = '';
2999
+ } else {
3000
+ if (interpolations.length > 0) {
3001
+ if (char === '{') {
3002
+ interpolations[interpolations.length - 1] += 1;
3003
+ } else if (char === '}') {
3004
+ interpolations[interpolations.length - 1] -= 1;
3005
+ }
3006
+ }
3007
+ emitCodeChar(char);
3008
+ }
3009
+ continue;
3010
+ }
3011
+ if (state === 'line') {
3012
+ if (char === '\n') {
3013
+ state = 'code';
3014
+ result += char;
3015
+ }
3016
+ continue;
3017
+ }
3018
+ if (state === 'block') {
3019
+ if (char === '*' && next === '/') {
3020
+ state = 'code';
3021
+ index += 1;
3022
+ } else if (char === '\n') {
3023
+ result += char;
3024
+ }
3025
+ continue;
3026
+ }
3027
+ if (state === 'regex') {
3028
+ // Regex body passes through as code. Honor escapes and character
3029
+ // classes (`[...]`), where `/` need not be escaped, so the literal ends
3030
+ // only at the true closing `/`.
3031
+ if (char === '\\') {
3032
+ result += char + next;
3033
+ index += 1;
3034
+ continue;
3035
+ }
3036
+ if (char === '[') {
3037
+ regexInClass = true;
3038
+ } else if (char === ']') {
3039
+ regexInClass = false;
3040
+ } else if (char === '/' && !regexInClass) {
3041
+ state = 'code';
3042
+ result += char;
3043
+ lastSignificant = char;
3044
+ currentWord = '';
3045
+ continue;
3046
+ }
3047
+ result += char;
3048
+ continue;
3049
+ }
3050
+ if (state === 'template') {
3051
+ if (char === '\\') {
3052
+ result += char + next;
3053
+ index += 1;
3054
+ continue;
3055
+ }
3056
+ if (char === '`') {
3057
+ state = 'code';
3058
+ result += char;
3059
+ lastSignificant = char;
3060
+ currentWord = '';
3061
+ continue;
3062
+ }
3063
+ if (char === '$' && next === '{') {
3064
+ // Enter a `${ … }` interpolation: its body is code, so re-enter the
3065
+ // full scanner (nesting-aware) to strip comments inside it.
3066
+ interpolations.push(0);
3067
+ state = 'code';
3068
+ result += '${';
3069
+ index += 1;
3070
+ lastSignificant = '{';
3071
+ currentWord = '';
3072
+ continue;
3073
+ }
3074
+ result += char;
3075
+ continue;
3076
+ }
3077
+ // Inside a single- or double-quoted string literal: preserve every
3078
+ // character, honor escapes so a closing quote is not misread, and never
3079
+ // treat `//` or `/*` as comments.
3080
+ if (char === '\\') {
3081
+ result += char + next;
3082
+ index += 1;
3083
+ continue;
3084
+ }
3085
+ if (
3086
+ (state === 'single' && char === "'") ||
3087
+ (state === 'double' && char === '"')
3088
+ ) {
3089
+ state = 'code';
3090
+ result += char;
3091
+ lastSignificant = char;
3092
+ currentWord = '';
3093
+ continue;
3094
+ }
3095
+ result += char;
3096
+ }
3097
+ return result;
3098
+ };
3099
+ for (const shell of policy?.shells ?? []) {
3100
+ for (const forbidden of policy?.forbiddenPathClasses ?? []) {
3101
+ assert(
3102
+ !fs.existsSync(path.join(root, shell.packageDir, forbidden.path)),
3103
+ selfCheckFailure(
3104
+ `structural thin-shell ${forbidden.id}`,
3105
+ `${forbidden.diagnostic} Forbidden artifact ${shell.packageDir}/${forbidden.path} exists`,
3106
+ `remove forbidden thin-shell artifact ${shell.packageDir}/${forbidden.path}`,
3107
+ ),
3108
+ );
3109
+ }
3110
+ const srcAbsolute = path.join(root, shell.srcDir);
3111
+ if (!fs.existsSync(srcAbsolute)) {
3112
+ continue;
3113
+ }
3114
+ for (const file of collectSourceFiles(srcAbsolute)) {
3115
+ const source = stripSourceComments(fs.readFileSync(file, 'utf-8'));
3116
+ const relative = path.relative(root, file).split(path.sep).join('/');
3117
+ for (const pattern of policy?.forbiddenImportPatterns ?? []) {
3118
+ const match = new RegExp(pattern.expression, pattern.flags).exec(source);
3119
+ assert(
3120
+ match === null,
3121
+ selfCheckFailure(
3122
+ `structural thin-shell ${pattern.id}`,
3123
+ `${pattern.diagnostic} Found forbidden import at ${relative}:${match?.index ?? 0}`,
3124
+ 'consume only published surfaces from the thin shell',
3125
+ ),
3126
+ );
3127
+ }
3128
+ }
3129
+ }
3130
+
3131
+ const runtimeModuleSpecifiers = source => {
3132
+ const specifiers = [];
3133
+ const matchAll = expression => {
3134
+ for (const match of source.matchAll(expression)) {
3135
+ specifiers.push(match.at(-1));
3136
+ }
3137
+ };
3138
+ for (const match of source.matchAll(/\b(?:import|export)\s+([^;'"`]+?)\s+from\s*['"]([^'"]+)['"]/gu)) {
3139
+ if (!match[1].trimStart().startsWith('type ')) {
3140
+ specifiers.push(match[2]);
3141
+ }
3142
+ }
3143
+ matchAll(/\bimport\s*['"]([^'"]+)['"]/gu);
3144
+ matchAll(/\bimport\s*\(\s*['"]([^'"]+)['"]/gu);
3145
+ matchAll(/\brequire\s*\(\s*['"]([^'"]+)['"]/gu);
3146
+ return specifiers;
3147
+ };
3148
+ const remoteImplementationFor = (specifier, remotes) =>
3149
+ remotes.find(remote => {
3150
+ const packageSubpath = specifier.startsWith(`${remote.packageName}/`)
3151
+ ? specifier.slice(remote.packageName.length + 1)
3152
+ : undefined;
3153
+ if (
3154
+ specifier === remote.packageName ||
3155
+ (packageSubpath !== undefined && !packageSubpath.startsWith('api'))
3156
+ ) {
3157
+ return true;
3158
+ }
3159
+ const normalizedSpecifier = specifier.replaceAll('\\', '/');
3160
+ return normalizedSpecifier.includes(`${remote.directory}/`);
3161
+ });
3162
+
3163
+ for (const host of compositionPolicy?.hosts ?? []) {
3164
+ const srcAbsolute = path.join(root, host.srcDir);
3165
+ if (!fs.existsSync(srcAbsolute)) {
3166
+ continue;
3167
+ }
3168
+ for (const file of collectSourceFiles(srcAbsolute)) {
3169
+ const source = stripSourceComments(fs.readFileSync(file, 'utf-8'));
3170
+ const relative = path.relative(root, file).split(path.sep).join('/');
3171
+ for (const pattern of compositionPolicy.forbiddenSourcePatterns ?? []) {
3172
+ const match = new RegExp(pattern.expression, pattern.flags).exec(source);
3173
+ assert(
3174
+ match === null,
3175
+ selfCheckFailure(
3176
+ `federated composition ${pattern.id}`,
3177
+ `${pattern.diagnostic} Found forbidden source at ${relative}:${match?.index ?? 0}`,
3178
+ 'compose remote rendering through framework Module Federation primitives',
3179
+ ),
3180
+ );
3181
+ }
3182
+ // Declaration files cannot execute. Ambient federation declarations may
3183
+ // re-export a workspace component solely to preserve its public prop
3184
+ // type, so treating their specifiers as runtime implementation imports
3185
+ // produces a false positive.
3186
+ if (/\.d\.(?:ts|mts|cts)$/u.test(relative)) {
3187
+ continue;
3188
+ }
3189
+ for (const specifier of runtimeModuleSpecifiers(source)) {
3190
+ const remote = remoteImplementationFor(specifier, host.remotes ?? []);
3191
+ assert(
3192
+ remote === undefined,
3193
+ selfCheckFailure(
3194
+ 'federated composition remote-runtime-package-import',
3195
+ `Host ${host.id} imports remote render implementation ${specifier} from ${remote?.id} at ${relative}`,
3196
+ 'use import type for contracts or compose the implementation through Module Federation',
3197
+ ),
3198
+ );
3199
+ }
3200
+ }
3201
+ }
3202
+ };
3203
+ assertStructuralShellPolicy();
3204
+ const assertAdditionalShellCohort = () => {
3205
+ const primaryShellConfig = findById(
3206
+ ultramodernConfig.topology?.apps,
3207
+ 'shell-super-app',
3208
+ );
3209
+ const overlayPorts = overlay.ports ?? {};
3210
+ const configuredPorts = [
3211
+ ...Object.entries(overlayPorts).map(([id, port]) => ({ id, port })),
3212
+ ...(!Object.hasOwn(overlayPorts, 'shell-super-app')
3213
+ ? [{ id: 'shell-super-app', port: primaryShellConfig?.port }]
3214
+ : []),
3215
+ ...expectedAdditionalShells.map(shell => ({
3216
+ id: shell.id,
3217
+ port: shell.port,
3218
+ })),
3219
+ ];
3220
+ const portsByValue = new Map();
3221
+ for (const { id, port } of configuredPorts) {
3222
+ assert(
3223
+ typeof port === 'number' && Number.isFinite(port),
3224
+ `Configured development port for ${id} must be finite`,
3225
+ );
3226
+ const previous = portsByValue.get(port);
3227
+ assert(
3228
+ previous === undefined,
3229
+ `Duplicate configured development port ${port} for ${previous} and ${id}`,
3230
+ );
3231
+ portsByValue.set(port, id);
3232
+ }
3233
+
3234
+ if (expectedAdditionalShellIds.length === 0) {
3235
+ assert(
3236
+ ultramodernConfig.shells === undefined,
3237
+ 'Single-shell workspace must not declare config.shells',
3238
+ );
3239
+ return;
3240
+ }
3241
+
3242
+ assertSameIdCohort(
3243
+ ultramodernConfig.shells,
3244
+ expectedAdditionalShellIds,
3245
+ `${compactConfigPath} shells`,
3246
+ 'restore every configured additional shell',
3247
+ );
3248
+ const configuredShellRecords = ultramodernConfig.shells;
3249
+ const configuredShellById = new Map(
3250
+ configuredShellRecords.map(shell => [shell.id, shell]),
3251
+ );
3252
+ assertUniqueStrings(
3253
+ [
3254
+ primaryShellConfig?.moduleFederation?.name,
3255
+ ...expectedAdditionalShells.map(shell => shell.mfName),
3256
+ ],
3257
+ 'configured shell Module Federation identities',
3258
+ );
3259
+ assertUniqueStrings(
3260
+ expectedAdditionalShells.map(
3261
+ shell => shell.deliveryUnit?.buildMarker,
3262
+ ),
3263
+ 'configured shell build markers',
3264
+ );
3265
+
3266
+ const configuredOrigins = [...portsByValue.keys()]
3267
+ .toSorted((left, right) => left - right)
3268
+ .map(port => `http://localhost:${port}`);
3269
+
3270
+ for (const shell of expectedAdditionalShells) {
3271
+ const configShell = configuredShellById.get(shell.id);
3272
+ assertObject(
3273
+ configShell,
3274
+ `${compactConfigPath} shells.${shell.id}`,
3275
+ 'restore the generated additional-shell config record',
3276
+ );
3277
+ assertSameJson(
3278
+ {
3279
+ id: configShell.id,
3280
+ name: configShell.name,
3281
+ kind: configShell.kind,
3282
+ package: configShell.package,
3283
+ path: configShell.path,
3284
+ port: configShell.port,
3285
+ portEnv: configShell.portEnv,
3286
+ mfName: configShell.mfName,
3287
+ verticalRefs: configShell.verticalRefs,
3288
+ owner: configShell.owner,
3289
+ deliveryUnit: configShell.deliveryUnit,
3290
+ moduleFederation: configShell.moduleFederation,
3291
+ },
3292
+ {
3293
+ id: shell.id,
3294
+ name: shell.id.replace(/^shell-/u, ''),
3295
+ kind: 'shell',
3296
+ package: shell.packageName,
3297
+ path: shell.path,
3298
+ port: shell.port,
3299
+ portEnv: shell.portEnv,
3300
+ mfName: shell.mfName,
3301
+ verticalRefs: shell.verticalRefs,
3302
+ owner: shell.owner,
3303
+ deliveryUnit: shell.deliveryUnit,
3304
+ moduleFederation: shell.moduleFederation,
3305
+ },
3306
+ `${compactConfigPath} shells.${shell.id}`,
3307
+ 'restore the complete additional-shell config record',
3308
+ );
3309
+ assert(
3310
+ !Object.hasOwn(overlay.ports ?? {}, shell.id),
3311
+ `${shell.id} port must stay in config.shells, not the development overlay`,
3312
+ );
3313
+ const owner = configShell.owner;
3314
+ assertObject(
3315
+ owner,
3316
+ `${compactConfigPath} shells.${shell.id}.owner`,
3317
+ 'record exactly one owner for every configured Delivery Unit',
3318
+ );
3319
+ assert(
3320
+ ['team', 'agent', 'agent-team'].includes(owner.kind) &&
3321
+ typeof owner.id === 'string' &&
3322
+ owner.id.length > 0,
3323
+ `${compactConfigPath} shells.${shell.id}.owner must identify one accountable owner`,
3324
+ );
3325
+ assertSameJson(
3326
+ owner,
3327
+ shell.owner,
3328
+ `${compactConfigPath} shells.${shell.id}.owner`,
3329
+ 'restore the generated additional-shell owner attribution',
3330
+ );
3331
+ assertObject(
3332
+ configShell.deliveryUnit,
3333
+ `${compactConfigPath} shells.${shell.id}.deliveryUnit`,
3334
+ 'restore the generated additional-shell Delivery Unit identity',
3335
+ );
3336
+ assert(
3337
+ typeof configShell.deliveryUnit.unitId === 'string' &&
3338
+ configShell.deliveryUnit.unitId.length > 0 &&
3339
+ typeof configShell.deliveryUnit.buildMarker === 'string' &&
3340
+ configShell.deliveryUnit.buildMarker.length > 0,
3341
+ `${compactConfigPath} shells.${shell.id}.deliveryUnit must carry unitId and buildMarker`,
3342
+ );
3343
+
3344
+ const packagePath = `${shell.path}/package.json`;
3345
+ const packageJson = readJson(packagePath);
3346
+ assert(packageJson.name === shell.packageName, `${shell.id} package name is incorrect`);
3347
+ assert(packageJson.modernjs?.appId === shell.id, `${shell.id} package modernjs.appId is incorrect`);
3348
+ assert(packageJson.modernjs?.role === 'shell', `${shell.id} package modernjs.role must be shell`);
3349
+ assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', `${shell.id} must expose cloudflare:deploy`);
3350
+ assert(packageJson.scripts?.['cloudflare:proof']?.includes(`--app ${shell.id}`), `${shell.id} must expose cloudflare:proof`);
3351
+
3352
+ const buildArtifact = readJson(`${shell.path}/shared/ultramodern-build.json`);
3353
+ const buildSource = readText(`${shell.path}/shared/ultramodern-build.ts`);
3354
+ assert(buildArtifact.deliveryUnit?.appId === shell.id, `${shell.id} build artifact appId is incorrect`);
3355
+ assert(buildArtifact.deliveryUnit?.buildMarker === shell.deliveryUnit?.buildMarker, `${shell.id} build marker is not participating in the build artifact`);
3356
+ assertSameJson(
3357
+ deliveryUnitBlock(configShell.deliveryUnit),
3358
+ deliveryUnitBlock(shell.deliveryUnit),
3359
+ `${compactConfigPath} shells.${shell.id}.deliveryUnit`,
3360
+ deliveryUnitIdentityFixArea,
3361
+ );
3362
+ assertSameJson(
3363
+ deliveryUnitBlock(buildArtifact.deliveryUnit),
3364
+ deliveryUnitBlock(shell.deliveryUnit),
3365
+ `${shell.path}/shared/ultramodern-build.json deliveryUnit`,
3366
+ deliveryUnitIdentityFixArea,
3367
+ );
3368
+ assert(
3369
+ buildSource.includes('export const ultramodernDeliveryUnit = ultramodernBuildArtifact.deliveryUnit;'),
3370
+ `${shell.path}/shared/ultramodern-build.ts must expose the shell delivery-unit identity`,
3371
+ );
3372
+ assertBuildFacadeExport(
3373
+ buildSource,
3374
+ 'ultramodernUiMarker',
3375
+ 'ultramodernBuildArtifact.surfaces.ui',
3376
+ `${shell.path}/shared/ultramodern-build.ts ultramodernUiMarker`,
3377
+ );
3378
+ assert(
3379
+ shell.degradedState?.appId === shell.id &&
3380
+ shell.degradedState?.status === 'degraded',
3381
+ `${shell.id} degraded-state contract must identify its own shell`,
3382
+ );
3383
+
3384
+ const modernConfig = readText(`${shell.path}/modern.config.ts`);
3385
+ const moduleFederationConfig = readText(
3386
+ `${shell.path}/module-federation.config.ts`,
3387
+ );
3388
+ const runtimeConfig = readText(`${shell.path}/src/modern.runtime.ts`);
3389
+ const styles = readText(`${shell.path}/src/routes/index.css`);
3390
+ const shellFrame = readText(`${shell.path}/src/routes/shell-frame.tsx`);
3391
+ const routePage = readText(`${shell.path}/src/routes/[lang]/page.tsx`);
3392
+ const remoteComponents = readText(
3393
+ `${shell.path}/src/routes/vertical-components.tsx`,
3394
+ );
3395
+ const workerRemoteComponents = readText(
3396
+ requiredShellWorkerCompositionPath(shell.path),
3397
+ );
3398
+ assert(modernConfig.includes(`const appId = '${shell.id}';`), `${shell.id} modern.config.ts appId is incorrect`);
3399
+ assert(modernConfig.includes(`const port = Number(getBuildConfigEnvironment('${shell.portEnv}') ?? ${shell.port});`), `${shell.id} modern.config.ts port is incorrect`);
3400
+ assert(modernConfig.includes(`uniqueName('${shell.mfName}')`), `${shell.id} modern.config.ts Rspack identity is incorrect`);
3401
+ assert(moduleFederationConfig.includes(`name: '${shell.mfName}'`), `${shell.id} Module Federation container name is incorrect`);
3402
+ assert(new RegExp(`appId:\\s*['"]${shell.id}['"]`, 'u').test(runtimeConfig), `${shell.id} runtime boundary metadata must identify its own shell`);
3403
+ assert(routePage.includes('ShellFrame') && routePage.includes('VerticalShowcase'), `${shell.id} route page must use its own shell composition host`);
3404
+ assert(remoteComponents.includes(`data-modern-boundary-id="${shell.mfName}"`), `${shell.id} remote composition boundary must use its own MF identity`);
3405
+ assert(workerRemoteComponents.includes('DistributedSsrBoundary'), `${shell.id} Worker SSR must use distributed fragment boundaries`);
3406
+ assert(!workerRemoteComponents.includes('@module-federation') && !workerRemoteComponents.includes('import('), `${shell.id} Worker SSR must not include native Module Federation runtime or remote imports`);
3407
+ if (tailwindEnabled) {
3408
+ assert(styles.includes(`prefix(${shell.tailwindPrefix})`), `${shell.id} styles must use its shell-specific Tailwind prefix`);
3409
+ }
3410
+ assert(shellFrame.includes(`${shell.tailwindPrefix}:`), `${shell.id} shell-frame must use its shell-specific Tailwind prefix`);
3411
+ if ((shell.verticalRefs ?? []).length > 0) {
3412
+ assert(
3413
+ shell.degradedState.required === true,
3414
+ `${shell.id} degraded-state contract must be required for remote consumption`,
3415
+ );
3416
+ assert(remoteComponents.includes(`${shell.tailwindPrefix}:text-red-900`), `${shell.id} degraded fallback must report its own shell identity`);
3417
+ assert(remoteComponents.includes('fallback: <RemoteUnavailable />'), `${shell.id} consumption points must have a degraded fallback`);
3418
+ assert(!remoteComponents.includes('data-modern-boundary-id="shellSuperApp"'), `${shell.id} degraded fallback must not report the primary shell`);
3419
+ }
3420
+ }
3421
+
3422
+ for (const appPath of [
3423
+ 'apps/shell-super-app',
3424
+ ...expectedAdditionalShells.map(shell => shell.path),
3425
+ ...fullStackVerticals.map(vertical => vertical.path),
3426
+ ]) {
3427
+ const modernConfig = readText(`${appPath}/modern.config.ts`);
3428
+ for (const origin of configuredOrigins) {
3429
+ assert(
3430
+ modernConfig.includes(`'${origin}'`),
3431
+ `${appPath} MF dev CORS must allow configured origin ${origin}`,
3432
+ );
3433
+ }
3434
+ assert(modernConfig.includes('credentials: false'), `${appPath} MF asset CORS must disable credentials`);
3435
+ assert(!modernConfig.includes('credentials: true'), `${appPath} MF asset CORS must not enable credentials`);
3436
+ assert(!/origin:\s*(?:true|\*|['"]\*['"])/u.test(modernConfig), `${appPath} MF dev CORS must not reflect arbitrary origins`);
3437
+ }
3438
+
3439
+ // Zerops artifacts exist whenever the workspace has delivery units at all
3440
+ // (ui-only and horizontal-remote units deploy too); a shell-only workspace
3441
+ // must not carry one.
3442
+ if (!hasDeliveryUnits) {
3443
+ assertNotExists('zerops.yaml');
3444
+ } else {
3445
+ assertExists('zerops.yaml');
3446
+ const zeropsYaml = readText('zerops.yaml');
3447
+ assert(
3448
+ zeropsYaml.includes(`setup: ${quoteYamlString('shell-super-app')}`),
3449
+ 'shell-super-app must have a Zerops service',
3450
+ );
3451
+ for (const shell of expectedAdditionalShells) {
3452
+ assert(zeropsYaml.includes(`setup: ${quoteYamlString(shell.id)}`), `${shell.id} must have a Zerops service`);
3453
+ assert(zeropsYaml.includes(`start: cd ${quoteShellValue(`.zerops/runtime/${shell.id}`)} && npm run serve`), `${shell.id} Zerops service start command is missing`);
3454
+ assert(zeropsYaml.includes(` ${shell.portEnv}: ${quoteYamlString(String(shell.port))}`), `${shell.id} Zerops service port environment is missing`);
3455
+ }
3456
+ }
3457
+ };
3458
+ assertAdditionalShellCohort();
1614
3459
  assert(
1615
3460
  rootPackage.devDependencies?.['@modern-js/create'] ===
1616
3461
  expectedModernPackageSpecifier('@modern-js/create'),
@@ -1621,6 +3466,11 @@ assert(
1621
3466
  expectedModernPackageSpecifier('@modern-js/code-tools'),
1622
3467
  'Root must depend on @modern-js/code-tools through package source metadata',
1623
3468
  );
3469
+ assert(
3470
+ rootPackage.devDependencies?.['@modern-js/plugin-bff'] ===
3471
+ expectedModernPackageSpecifier('@modern-js/plugin-bff'),
3472
+ 'Root must depend on @modern-js/plugin-bff for Node backend federation proof',
3473
+ );
1624
3474
  if (packageSource.strategy === 'install') {
1625
3475
  const installSpecifier = packageSource.modernPackages?.specifier;
1626
3476
  assert(
@@ -1680,20 +3530,90 @@ if (bridgeConfig) {
1680
3530
  }
1681
3531
  assert(rootPackage.scripts?.['bridge:check'], 'Bridge workspaces must expose bridge:check');
1682
3532
  } else {
1683
- assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mts --build tsconfig.json', 'Root typecheck must run TS-Go build mode across project references');
3533
+ assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mts --project tsconfig.json', 'Root typecheck must run TS-Go across the root project reference graph');
1684
3534
  }
1685
3535
  assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultramodern-workspace.mts', 'Root must expose contract:check');
1686
3536
  assert(rootPackage.scripts?.['api:check'] === 'node ./scripts/check-ultramodern-api-boundaries.mts', 'Root must expose api:check');
1687
3537
  assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mts', 'Root must expose i18n:boundaries');
1688
3538
  assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mts', 'Root must expose default-on performance readiness diagnostics');
3539
+ if (hasBackendSurfaces) {
3540
+ assert(rootPackage.scripts?.['node:backend-federation:generate'] === 'node ./scripts/generate-node-backend-federation.mts', 'Root must expose local Node backend federation artifact generation');
3541
+ assert(
3542
+ rootPackage.scripts?.['node:proof'] ===
3543
+ 'node ./scripts/proof-node-backend-federation.mts',
3544
+ 'Root must expose read-only node:proof for already-built backend federation Effect modules',
3545
+ );
3546
+ } else {
3547
+ assert(rootPackage.scripts?.['node:backend-federation:generate'] === undefined, 'Root must not expose backend federation generation without an API surface');
3548
+ assert(rootPackage.scripts?.['node:proof'] === undefined, 'Root must not expose node:proof without an API surface');
3549
+ }
3550
+ if (hasDeliveryUnits) {
3551
+ assert(rootPackage.scripts?.['zerops:materialize'] === 'node ./scripts/materialize-zerops-runtime.mjs', 'Root must expose Zerops runtime materialization script');
3552
+ assert(rootPackage.scripts?.['cloudflare:ssr-proof'] === 'node ./scripts/proof-workerd-ssr.mts', 'Root must expose workerd distributed SSR composition proof');
3553
+ assert(rootPackage.scripts?.['cloudflare:build']?.endsWith('&& pnpm cloudflare:ssr-proof'), 'Root Cloudflare build must finish with workerd distributed SSR composition proof');
3554
+ } else {
3555
+ assert(rootPackage.scripts?.['zerops:materialize'] === undefined, 'Root must not expose Zerops materialization in a shell-only workspace');
3556
+ assert(rootPackage.scripts?.['cloudflare:ssr-proof'] === undefined, 'Root must not expose workerd SSR proof in a shell-only workspace');
3557
+ }
3558
+ assertNotExists('scripts/generate-node-backend-federation.mjs');
3559
+ assertNotExists('scripts/proof-node-backend-federation.mjs');
3560
+ assertNotExists('scripts/verify-cloudflare-output.mjs');
3561
+ assertNotExists('scripts/generate-tanstack-routes.mjs');
1689
3562
  assert(
1690
- bridgeConfig
1691
- ? rootPackage.scripts?.check?.includes('pnpm api:check') &&
1692
- rootPackage.scripts.check.endsWith('&& pnpm performance:readiness && pnpm bridge:check')
1693
- : rootPackage.scripts?.check?.includes('pnpm api:check') &&
1694
- rootPackage.scripts.check.endsWith('&& pnpm performance:readiness'),
1695
- 'Root check must run default-on performance readiness diagnostics and bridge gates when configured',
3563
+ rootPackage.scripts?.check?.includes('pnpm api:check') &&
3564
+ !rootPackage.scripts.check.includes('pnpm node:proof') &&
3565
+ rootPackage.scripts.check.endsWith(
3566
+ bridgeConfig
3567
+ ? '&& pnpm performance:readiness && pnpm bridge:check'
3568
+ : '&& pnpm performance:readiness',
3569
+ ),
3570
+ 'Root check must remain static while running default-on performance readiness diagnostics and bridge gates when configured',
1696
3571
  );
3572
+ if (hasDeliveryUnits) {
3573
+ const zeropsYaml = readText('zerops.yaml');
3574
+ assert(zeropsYaml.includes('zerops:'), 'Zerops manifest must include zerops services');
3575
+ assert(zeropsYaml.includes(`setup: ${quoteYamlString('shell-super-app')}`), 'Zerops manifest must include shell service');
3576
+ assert(zeropsYaml.includes(`base: ${quoteYamlString('nodejs@26')}`), 'Zerops manifest must use generated Node runtime major');
3577
+ assert(zeropsYaml.includes('deployFiles:'), 'Zerops manifest must deploy package-pruned runtime directories');
3578
+ assert(
3579
+ zeropsYaml.includes(`start: cd ${quoteShellValue('.zerops/runtime/shell-super-app')} && npm run serve`),
3580
+ 'Zerops shell service must start from materialized runtime package',
3581
+ );
3582
+ assert(
3583
+ zeropsYaml.includes('SHELL_SUPER_APP_PORT:') &&
3584
+ zeropsYaml.includes('ULTRAMODERN_ZEROPS_SERVICE:'),
3585
+ 'Zerops manifest must expose service identity and ports',
3586
+ );
3587
+ for (const vertical of fullStackVerticals) {
3588
+ assert(zeropsYaml.includes(`setup: ${quoteYamlString(vertical.id)}`), `${vertical.id} must have a Zerops service`);
3589
+ assert(
3590
+ zeropsYaml.includes(`~/.local/bin/mise exec -- pnpm run zerops:materialize -- --app ${quoteShellValue(vertical.id)} --package ${quoteShellValue(vertical.packageName)} --package-dir ${quoteShellValue(vertical.path)}`),
3591
+ `${vertical.id} Zerops service must materialize its runtime package`,
3592
+ );
3593
+ // Only REST-backed (or headless-less UI) units expose an HTTP readiness
3594
+ // probe; the RPC surface has no REST readiness endpoint (G7a).
3595
+ if (!vertical.emitsApi || vertical.apiProtocol === 'rest') {
3596
+ const zeropsReadinessPath = vertical.emitsApi
3597
+ ? `${vertical.apiPrefix}/${vertical.stem}/readiness`
3598
+ : '/';
3599
+ assert(
3600
+ zeropsYaml.includes(`path: ${quoteYamlString(zeropsReadinessPath)}`),
3601
+ `${vertical.id} BFF readiness path must use generated API prefix`,
3602
+ );
3603
+ }
3604
+ }
3605
+ const zeropsMaterializer = readText('scripts/materialize-zerops-runtime.mjs');
3606
+ assert(zeropsMaterializer.includes("MODERNJS_DEPLOY: 'node'"), 'Zerops materializer must use Modern.js Node deploy target');
3607
+ assert(/'deploy',\s*'--skip-build'/.test(zeropsMaterializer), 'Zerops materializer must pass --skip-build directly to Modern deploy');
3608
+ assert(zeropsMaterializer.includes('normalizeRuntimePackageDependencies'), 'Zerops materializer must normalize generated Modern package aliases before installing dependencies');
3609
+ assert(zeropsMaterializer.includes('officialPackageName'), 'Zerops materializer must add official Modern.js npm aliases for generated runtime imports');
3610
+ assert(zeropsMaterializer.includes('installRuntimeDependencies'), 'Zerops materializer must install dependencies outside the workspace copy runtime node_modules');
3611
+ assert(zeropsMaterializer.includes('copyWorkspacePackage'), 'Zerops materializer must preserve generated local workspace package dependencies');
3612
+ assert(zeropsMaterializer.includes('makeWorkspacePackageRuntimeSafe'), 'Zerops materializer must rewrite copied workspace packages to runtime-safe JavaScript exports');
3613
+ assert(zeropsMaterializer.includes("serve: runtimePackage.scripts?.serve ?? 'node index.js'"), 'Zerops materializer must preserve runtime serve script fallback');
3614
+ assert(/'install',\s*'--omit=dev'/.test(zeropsMaterializer), 'Zerops materializer must omit dev dependencies from runtime installs');
3615
+ assert(zeropsMaterializer.includes("'--legacy-peer-deps'"), 'Zerops materializer must tolerate generated-workspace peer dependency ranges in npm runtime install');
3616
+ }
1697
3617
  const performanceReadinessConfig = readText('scripts/ultramodern-performance-readiness.config.mjs');
1698
3618
  const assertToolWrapper = (scriptPath, command) => {
1699
3619
  const source = readText(scriptPath);
@@ -1705,7 +3625,6 @@ const assertToolWrapper = (scriptPath, command) => {
1705
3625
  assert(performanceReadinessConfig.includes('UltramodernPerformanceReadinessDiagnosticsConfig'), 'Performance readiness config must carry the typed opt-out surface');
1706
3626
  assert(performanceReadinessConfig.includes('enabled: true'), 'Performance readiness diagnostics must be default-on');
1707
3627
  assert(performanceReadinessConfig.includes("failOn: 'framework-invariant'"), 'Performance readiness diagnostics must only fail framework invariants by default');
1708
- assertToolWrapper('scripts/validate-ultramodern-workspace.mts', 'validate');
1709
3628
  assertToolWrapper('scripts/ultramodern-performance-readiness.mts', 'performance-readiness');
1710
3629
  const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mts');
1711
3630
  assertToolWrapper('scripts/ultramodern-typecheck.mts', 'typecheck');
@@ -1720,12 +3639,18 @@ assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloud
1720
3639
  assert(rootPackage.scripts?.['migrate:strict-effect'] === 'node ./scripts/migrate-strict-effect.mts', 'Root must expose migrate:strict-effect');
1721
3640
  assert(rootPackage.scripts?.['skills:install'] === 'node ./scripts/bootstrap-agent-skills.mts', 'Root must expose skills:install');
1722
3641
  assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent-skills.mts --check', 'Root must expose skills:check');
1723
- assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mts --postinstall", 'Root postinstall must format, run the default-on Codex skills bootstrap, and leave reference repository installs explicit');
3642
+ assert(rootPackage.scripts?.postinstall === "node ./scripts/bootstrap-agent-skills.mts --postinstall && oxfmt . '!repos/**'", 'Root postinstall must run the default-on Codex skills bootstrap, format installed skills, and leave reference repository installs explicit');
1724
3643
  assert(rootPackage.scripts?.['agents:refs:install'] === 'node ./scripts/setup-agent-reference-repos.mts', 'Root must expose agents:refs:install as the explicit reference repo installer');
1725
3644
  const agentSkillsBootstrap = readText('scripts/bootstrap-agent-skills.mts');
1726
3645
  assertToolWrapper('scripts/assert-mf-types.mts', 'mf-types');
3646
+ if (hasBackendSurfaces) {
3647
+ assertToolWrapper('scripts/generate-node-backend-federation.mts', 'backend-federation-generate');
3648
+ assertToolWrapper('scripts/proof-node-backend-federation.mts', 'backend-federation-proof');
3649
+ }
1727
3650
  assertToolWrapper('scripts/generate-public-surface-assets.mts', 'public-surface');
3651
+ assertToolWrapper('scripts/generate-tanstack-routes.mts', 'routes-generate');
1728
3652
  assertToolWrapper('scripts/proof-cloudflare-version.mts', 'cloudflare-proof');
3653
+ assertToolWrapper('scripts/verify-cloudflare-output.mts', 'cloudflare-output-verify');
1729
3654
  assertToolWrapper('scripts/migrate-strict-effect.mts', 'migrate-strict-effect');
1730
3655
  assertToolWrapper('scripts/bootstrap-agent-skills.mts', 'skills');
1731
3656
  assert(!agentSkillsBootstrap.includes("run('brew'") && !agentSkillsBootstrap.includes('runShell('), 'Agent skills bootstrap must never invoke system package managers');
@@ -1735,10 +3660,9 @@ assert(agentReferenceRepoSetup.includes("commitInstallerChanges('Initialize Ultr
1735
3660
  assert(agentReferenceRepoSetup.includes("commitInstallerChanges('Record agent reference repo manifest')"), 'Agent reference repo manifest commit must use the installer commit helper');
1736
3661
 
1737
3662
  const expectedAppIds = ['shell-super-app', ...fullStackVerticals.map(vertical => vertical.id)];
1738
- const expectedCloudflareCompatibilityDate = '{{cloudflareCompatibilityDate}}';
1739
3663
  const expectedCloudflareCompatibilityFlags = ['nodejs_compat', 'global_fetch_strictly_public'];
1740
3664
  assert(
1741
- JSON.stringify(generatedContract.apps?.map(app => app.id)) === JSON.stringify(expectedAppIds),
3665
+ sameJson(generatedContract.apps?.map(app => app.id), expectedAppIds),
1742
3666
  'Generated contract must contain shell plus the full-stack verticals',
1743
3667
  );
1744
3668
  assert(generatedContract.cssFederation?.sharedDesignTokens?.owner?.id === 'shared-design-tokens', 'CSS federation must declare shared design token ownership');
@@ -1763,38 +3687,57 @@ assert(generatedContract.performanceReadiness?.mode === 'diagnostic', 'Performan
1763
3687
  assert(generatedContract.performanceReadiness?.report?.script === 'scripts/ultramodern-performance-readiness.mts', 'Performance readiness contract must point at the generated script');
1764
3688
  assert(generatedContract.performanceReadiness?.report?.deterministic === true, 'Performance readiness reports must be deterministic');
1765
3689
  assert(generatedContract.performanceReadiness?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Performance readiness env opt-out is incorrect');
1766
- assert(JSON.stringify(generatedContract.performanceReadiness?.signals?.map(signal => signal.id)) === JSON.stringify(expectedPerformanceReadinessSignals), 'Performance readiness signal ids are incorrect');
3690
+ assert(sameJson(generatedContract.performanceReadiness?.signals?.map(signal => signal.id), expectedPerformanceReadinessSignals), 'Performance readiness signal ids are incorrect');
1767
3691
 
1768
3692
  const shellModernConfig = readText('apps/shell-super-app/modern.config.ts');
1769
3693
  const shellModuleFederationConfig = readText('apps/shell-super-app/module-federation.config.ts');
3694
+ const shellModernAppEnv = readText('apps/shell-super-app/src/modern-app-env.d.ts');
3695
+ const gitignore = readText('.gitignore');
1770
3696
  const shellRouteHead = readText('apps/shell-super-app/src/routes/ultramodern-route-head.tsx');
1771
3697
  const shellRouteMetadata = readText('apps/shell-super-app/src/routes/ultramodern-route-metadata.ts');
3698
+ assert(/^\.mf\/$/mu.test(gitignore), 'Generated .gitignore must ignore root Module Federation diagnostics');
3699
+ assert(/^\*\*\/\.mf\/$/mu.test(gitignore), 'Generated .gitignore must ignore per-app Module Federation diagnostics');
3700
+ assert(/^dist-cloudflare\/$/mu.test(gitignore), 'Generated .gitignore must ignore Cloudflare build output');
3701
+ assert(/^\.output\/$/mu.test(gitignore), 'Generated .gitignore must ignore root final deployment output');
3702
+ assert(/^\*\*\/\.output\/$/mu.test(gitignore), 'Generated .gitignore must ignore per-app final deployment output');
3703
+ assert(/^\*\*\/src\/modern-tanstack\/$/mu.test(gitignore), 'Generated .gitignore must ignore framework-owned TanStack router output');
3704
+ assert(/^\*\*\/\.tsgo\.\*\.resolved\.json$/mu.test(gitignore), 'Generated .gitignore must ignore transient TS-Go resolution output');
3705
+ assert(shellModernAppEnv.includes('/// <reference types="@modern-js/app-tools/types" />'), 'Shell app env must reference the framework-owned app ambient type bundle while remaining an ambient declaration file');
3706
+ assert(/declare const ULTRAMODERN_SITE_URL: string;/u.test(shellModernAppEnv), 'Shell app env must keep generated globals explicit in ambient scope');
3707
+ assert(!shellModernAppEnv.includes("declare module '*.svg'"), 'Shell app env must not redeclare framework-owned svg asset modules');
3708
+ assert(!shellModernAppEnv.includes("declare module '*.css'"), 'Shell app env must not redeclare framework-owned css asset modules');
1772
3709
  assert(shellRouteMetadata.includes('@generated by @modern-js/create'), 'Shell route metadata compatibility manifest must be marked generated');
1773
3710
  assert(shellRouteMetadata.includes("authoring: 'colocated-route-meta'"), 'Shell route metadata manifest must advertise colocated authoring');
1774
3711
  const expectedZephyrDependencies = Object.fromEntries(
1775
- fullStackVerticals.map(vertical => [
1776
- vertical.zephyrAlias,
1777
- `${vertical.packageName}@workspace:*`,
1778
- ]),
3712
+ expectedPrimaryShellVerticalIds.map(verticalId => {
3713
+ const vertical = fullStackVerticals.find(
3714
+ candidate => candidate.id === verticalId,
3715
+ );
3716
+ assert(vertical, `Missing primary-shell vertical ${verticalId}`);
3717
+ return [
3718
+ vertical.zephyrAlias,
3719
+ `${vertical.packageName}@workspace:*`,
3720
+ ];
3721
+ }),
1779
3722
  );
1780
3723
  assert(
1781
- JSON.stringify(shellPackage['zephyr:dependencies']) ===
1782
- JSON.stringify(expectedZephyrDependencies),
1783
- 'Shell Zephyr dependencies must reference every vertical package',
3724
+ sameJson(shellPackage['zephyr:dependencies'], expectedZephyrDependencies),
3725
+ 'Shell Zephyr dependencies must reference every primary-shell vertical package',
1784
3726
  );
1785
3727
  assert(shellPackage.devDependencies?.['@modern-js/app-tools'] === expectedModernPackageSpecifier('@modern-js/app-tools'), 'Shell app-tools dependency must match package source metadata');
1786
3728
  assert(shellPackage.dependencies?.['@modern-js/plugin-bff'] === expectedModernPackageSpecifier('@modern-js/plugin-bff'), 'Shell plugin-bff dependency must match package source metadata');
1787
3729
  assert(shellPackage.dependencies?.['@modern-js/plugin-i18n'] === expectedModernPackageSpecifier('@modern-js/plugin-i18n'), 'Shell plugin-i18n dependency must match package source metadata');
1788
3730
  assert(shellPackage.dependencies?.['@modern-js/plugin-tanstack'] === expectedModernPackageSpecifier('@modern-js/plugin-tanstack'), 'Shell plugin-tanstack dependency must match package source metadata');
1789
3731
  assert(shellPackage.dependencies?.['@modern-js/runtime'] === expectedModernPackageSpecifier('@modern-js/runtime'), 'Shell runtime dependency must match package source metadata');
1790
- assertBuildCacheBase('shell-super-app', shellModernConfig);
3732
+ assert(shellPackage.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', 'Shell must expose cloudflare:deploy');
3733
+ assertTargetIsolatedBuildArtifacts('shell-super-app', shellModernConfig);
1791
3734
  assertCloudflareBuildSkipsDeployRebuild('shell-super-app', shellPackage);
1792
3735
  const shellContract = generatedContract.apps?.find(app => app.id === 'shell-super-app');
1793
3736
  assert(shellContract?.deploy?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell Cloudflare workerName is incorrect');
1794
3737
  assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'Shell Cloudflare public URL env is incorrect');
1795
3738
  assert(shellContract?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell Cloudflare compatibilityDate is incorrect');
1796
- assert(JSON.stringify(shellContract?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
1797
- assert(JSON.stringify(shellContract?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
3739
+ assert(sameJson(shellContract?.deploy?.cloudflare?.compatibilityFlags, expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
3740
+ assert(sameJson(shellContract?.deploy?.cloudflare?.security, expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
1798
3741
  assertCloudflareQualityGates('shell-super-app', shellContract?.deploy?.cloudflare?.qualityGates);
1799
3742
  assert(shellContract?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell worker compatibilityDate is incorrect');
1800
3743
  assert(shellContract?.deploy?.worker?.name === expectedWorkerName('shell-super-app'), 'Shell worker name is incorrect');
@@ -1813,15 +3756,15 @@ assert(shellModernConfig.includes("assetPrefix: '/'"), 'Shell modern.config.ts m
1813
3756
  assert(shellModernConfig.includes('assetPrefix,'), 'Shell modern.config.ts must wire output.assetPrefix to the derived asset prefix');
1814
3757
  assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
1815
3758
  assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
1816
- assert(JSON.stringify(shellContract?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), 'Shell asset prefix env fallback order is incorrect');
3759
+ assert(sameJson(shellContract?.config?.output?.assetPrefix?.envFallbackOrder, ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), 'Shell asset prefix env fallback order is incorrect');
1817
3760
  assert(shellContract?.config?.output?.disableTsChecker === false, 'Shell must keep the framework TypeScript checker enabled');
1818
3761
  assert(shellContract?.config?.performance?.readinessDiagnostics?.default === 'enabled', 'Shell performance readiness diagnostics must be default-on');
1819
3762
  assert(shellContract?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', 'Shell performance readiness diagnostics must only fail framework invariants by default');
1820
3763
  assert(shellContract?.config?.performance?.readinessDiagnostics?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Shell performance readiness env opt-out is incorrect');
1821
- assert(JSON.stringify(shellContract?.config?.source?.siteUrl?.envFallbackOrder) === JSON.stringify(['MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN', 'SHELL_SUPER_APP_PORT']), 'Shell site URL env fallback order is incorrect');
3764
+ assert(sameJson(shellContract?.config?.source?.siteUrl?.envFallbackOrder, ['MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN', 'SHELL_SUPER_APP_PORT']), 'Shell site URL env fallback order is incorrect');
1822
3765
  assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
1823
3766
  assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
1824
- assert(shellContract?.moduleFederation?.dts?.compilerInstance === 'tsgo', 'Shell must keep mandatory DTS compiler');
3767
+ assert(shellContract?.moduleFederation?.dts?.compilerInstance === 'effect-tsgo', 'Shell must keep mandatory DTS compiler');
1825
3768
  assert(shellContract?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', 'Shell must keep dedicated Module Federation DTS tsconfig');
1826
3769
  assert(shellModuleFederationConfig.includes("tsConfigPath: './tsconfig.mf-types.json'"), 'Shell Module Federation config must use the dedicated DTS tsconfig');
1827
3770
  assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
@@ -1839,7 +3782,7 @@ assert(shellContract?.routes?.privateByDefault === true, 'Shell routes must be p
1839
3782
  assert(shellContract?.routes?.metadataAuthoring === 'colocated-route-meta', 'Shell route metadata authoring mode is incorrect');
1840
3783
  assert(shellContract?.routes?.generatedManifest === true, 'Shell route metadata manifest must be generated');
1841
3784
  assert(shellContract?.routes?.publicnessDefault === 'private-app-screen', 'Shell route publicness default is incorrect');
1842
- assert(JSON.stringify(shellContract?.routes?.publicRoutes ?? []) === '[]', 'Shell must not expose generated public routes by default');
3785
+ assert(sameJson(shellContract?.routes?.publicRoutes ?? [], []), 'Shell must not expose generated public routes by default');
1843
3786
  assertPublicHeadContract('shell-super-app', shellContract?.routes?.publicHead, shellRouteHead);
1844
3787
  assertPublicSurfaceContract('shell-super-app', shellContract?.routes?.publicSurface);
1845
3788
  assert(
@@ -1848,7 +3791,7 @@ assert(
1848
3791
  );
1849
3792
  assertPublicSurfaceAssets('apps/shell-super-app', shellContract?.routes?.publicRoutes ?? []);
1850
3793
  assert(
1851
- topology.shell?.verticalRefs?.join(',') === fullStackVerticals.map(vertical => vertical.id).join(','),
3794
+ topology.shell?.verticalRefs?.join(',') === expectedPrimaryShellVerticalIds.join(','),
1852
3795
  'Topology shell verticalRefs must match generated verticals',
1853
3796
  );
1854
3797
  assert(topology.verticals?.length === fullStackVerticals.length, 'Topology must contain only generated verticals');
@@ -1858,23 +3801,104 @@ assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, no
1858
3801
  for (const vertical of fullStackVerticals) {
1859
3802
  const packageJson = readJson(`${vertical.path}/package.json`);
1860
3803
  const modernConfig = readText(`${vertical.path}/modern.config.ts`);
1861
- const moduleFederationConfig = readText(`${vertical.path}/module-federation.config.ts`);
1862
- const routeHead = readText(`${vertical.path}/src/routes/ultramodern-route-head.tsx`);
1863
- const routeMetadata = readText(`${vertical.path}/src/routes/ultramodern-route-metadata.ts`);
1864
- assert(routeMetadata.includes('@generated by @modern-js/create'), `${vertical.id} route metadata compatibility manifest must be marked generated`);
1865
- assert(routeMetadata.includes("authoring: 'colocated-route-meta'"), `${vertical.id} route metadata manifest must advertise colocated authoring`);
3804
+ // The browser Module Federation config and colocated route surfaces only
3805
+ // exist for UI-emitting units; a headless api-only unit emits none (G2a/P4).
3806
+ const moduleFederationConfig = vertical.emitsUi
3807
+ ? readText(`${vertical.path}/module-federation.config.ts`)
3808
+ : '';
3809
+ const modernAppEnv = readText(`${vertical.path}/src/modern-app-env.d.ts`);
3810
+ const routeHead = vertical.emitsUi
3811
+ ? readText(`${vertical.path}/src/routes/ultramodern-route-head.tsx`)
3812
+ : '';
3813
+ const routeMetadata = vertical.emitsUi
3814
+ ? readText(`${vertical.path}/src/routes/ultramodern-route-metadata.ts`)
3815
+ : '';
3816
+ const ultramodernBuildSource = readText(`${vertical.path}/shared/ultramodern-build.ts`);
3817
+ const ultramodernBuildArtifact = readJson(`${vertical.path}/shared/ultramodern-build.json`);
3818
+ if (vertical.deliveryUnit) {
3819
+ const topologyEntry = findById(topology.verticals, vertical.id);
3820
+ const expectedDeliveryUnit = deliveryUnitBlock(
3821
+ expectedDeliveryUnitFor(vertical, topologyEntry),
3822
+ );
3823
+ const buildLabel = `${vertical.path}/shared/ultramodern-build.json deliveryUnit`;
3824
+ assertSelfCheck(
3825
+ ultramodernBuildSource.includes('export const ultramodernDeliveryUnit'),
3826
+ buildLabel,
3827
+ 'Missing ultramodernDeliveryUnit export',
3828
+ deliveryUnitIdentityFixArea,
3829
+ );
3830
+ const buildIdentity = ultramodernBuildArtifact.deliveryUnit ?? {};
3831
+ assertSelfCheck(
3832
+ buildIdentity.buildMarker === expectedDeliveryUnit.buildMarker,
3833
+ buildLabel,
3834
+ `Expected build "${expectedDeliveryUnit.buildMarker}", found ${formatJson(buildIdentity.buildMarker)}`,
3835
+ deliveryUnitIdentityFixArea,
3836
+ );
3837
+ assertSelfCheck(
3838
+ buildIdentity.unitId === expectedDeliveryUnit.unitId,
3839
+ buildLabel,
3840
+ `Expected unitId "${expectedDeliveryUnit.unitId}", found ${formatJson(buildIdentity.unitId)}`,
3841
+ deliveryUnitIdentityFixArea,
3842
+ );
3843
+ assertSelfCheck(
3844
+ buildIdentity.packageName === expectedDeliveryUnit.packageName,
3845
+ buildLabel,
3846
+ `Expected packageName "${expectedDeliveryUnit.packageName}", found ${formatJson(buildIdentity.packageName)}`,
3847
+ deliveryUnitIdentityFixArea,
3848
+ );
3849
+ assertSelfCheck(
3850
+ buildIdentity.version === expectedDeliveryUnit.version,
3851
+ buildLabel,
3852
+ `Expected version "${expectedDeliveryUnit.version}", found ${formatJson(buildIdentity.version)}`,
3853
+ deliveryUnitIdentityFixArea,
3854
+ );
3855
+ assertBuildFacadeExport(
3856
+ ultramodernBuildSource,
3857
+ 'ultramodernUiMarker',
3858
+ 'ultramodernBuildArtifact.surfaces.ui',
3859
+ `${vertical.path}/shared/ultramodern-build.ts ultramodernUiMarker`,
3860
+ );
3861
+ assertBuildFacadeExport(
3862
+ ultramodernBuildSource,
3863
+ 'ultramodernApiMarker',
3864
+ 'ultramodernBuildArtifact.surfaces.api',
3865
+ `${vertical.path}/shared/ultramodern-build.ts ultramodernApiMarker`,
3866
+ );
3867
+ }
3868
+ assert(modernAppEnv.includes('/// <reference types="@modern-js/app-tools/types" />'), `${vertical.id} app env must reference the framework-owned app ambient type bundle while remaining an ambient declaration file`);
3869
+ assert(/declare const ULTRAMODERN_SITE_URL: string;/u.test(modernAppEnv), `${vertical.id} app env must keep generated globals explicit in ambient scope`);
3870
+ assert(!modernAppEnv.includes("declare module '*.svg'"), `${vertical.id} app env must not redeclare framework-owned svg asset modules`);
3871
+ assert(!modernAppEnv.includes("declare module '*.css'"), `${vertical.id} app env must not redeclare framework-owned css asset modules`);
3872
+ if (vertical.emitsUi) {
3873
+ assert(routeMetadata.includes('@generated by @modern-js/create'), `${vertical.id} route metadata compatibility manifest must be marked generated`);
3874
+ assert(routeMetadata.includes("authoring: 'colocated-route-meta'"), `${vertical.id} route metadata manifest must advertise colocated authoring`);
3875
+ }
1866
3876
  assert(packageJson.name === vertical.packageName, `${vertical.id} package name is incorrect`);
1867
3877
  assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', `${vertical.id} must expose cloudflare:deploy`);
1868
- assertBuildCacheBase(vertical.id, modernConfig);
3878
+ assertTargetIsolatedBuildArtifacts(vertical.id, modernConfig);
1869
3879
  assertCloudflareBuildSkipsDeployRebuild(vertical.id, packageJson);
1870
3880
  assert(packageJson.scripts?.['cloudflare:proof']?.includes(`--app ${vertical.id}`), `${vertical.id} must expose cloudflare:proof`);
1871
3881
  assert(packageJson.devDependencies?.['@modern-js/app-tools'] === expectedModernPackageSpecifier('@modern-js/app-tools'), `${vertical.id} app-tools dependency must match package source metadata`);
1872
- assert(packageJson.dependencies?.['@modern-js/plugin-bff'] === expectedModernPackageSpecifier('@modern-js/plugin-bff'), `${vertical.id} plugin-bff dependency must match package source metadata`);
3882
+ if (vertical.emitsApi) {
3883
+ assert(packageJson.dependencies?.['@modern-js/plugin-bff'] === expectedModernPackageSpecifier('@modern-js/plugin-bff'), `${vertical.id} plugin-bff dependency must match package source metadata`);
3884
+ }
1873
3885
  assert(packageJson.dependencies?.['@modern-js/plugin-i18n'] === expectedModernPackageSpecifier('@modern-js/plugin-i18n'), `${vertical.id} plugin-i18n dependency must match package source metadata`);
1874
3886
  assert(packageJson.dependencies?.['@modern-js/plugin-tanstack'] === expectedModernPackageSpecifier('@modern-js/plugin-tanstack'), `${vertical.id} plugin-tanstack dependency must match package source metadata`);
1875
3887
  assert(packageJson.dependencies?.['@modern-js/runtime'] === expectedModernPackageSpecifier('@modern-js/runtime'), `${vertical.id} runtime dependency must match package source metadata`);
1876
- assert(packageJson.exports?.['./api/client'] === `./src/api/${vertical.stem}-client.ts`, `${vertical.id} must export its API client`);
1877
- assert(packageJson.exports?.['./api'] === './shared/api.ts', `${vertical.id} must export its API contract`);
3888
+ if (vertical.emitsApi) {
3889
+ assert(packageJson.exports?.[vertical.apiClientExport] === `./${vertical.apiClientPath}`, `${vertical.id} must export its API client`);
3890
+ assert(packageJson.exports?.['./api'] === `./${vertical.apiContractPath}`, `${vertical.id} must export its API contract`);
3891
+ // API protocol exclusivity (G7a): an RPC unit ships only the RPC contract
3892
+ // and `${stem}-rpc-client`; a REST unit ships only the REST contract and
3893
+ // `${stem}-client`. Neither may carry the other protocol's surface.
3894
+ if (vertical.apiProtocol === 'rpc') {
3895
+ assert(!fs.existsSync(path.join(root, `${vertical.path}/src/api/${vertical.stem}-client.ts`)), `${vertical.id} RPC unit must not emit the REST API client`);
3896
+ assert(!fs.existsSync(path.join(root, `${vertical.path}/shared/api.ts`)), `${vertical.id} RPC unit must not emit the REST API contract`);
3897
+ } else {
3898
+ assert(!fs.existsSync(path.join(root, `${vertical.path}/src/api/${vertical.stem}-rpc-client.ts`)), `${vertical.id} REST unit must not emit the RPC API client`);
3899
+ assert(!fs.existsSync(path.join(root, `${vertical.path}/shared/rpc.ts`)), `${vertical.id} REST unit must not emit the RPC API contract`);
3900
+ }
3901
+ }
1878
3902
  const expectedVerticalZephyrDependencies = Object.fromEntries(
1879
3903
  fullStackVerticals
1880
3904
  .filter(candidate => vertical.verticalRefs.includes(candidate.id))
@@ -1884,8 +3908,7 @@ for (const vertical of fullStackVerticals) {
1884
3908
  ]),
1885
3909
  );
1886
3910
  assert(
1887
- JSON.stringify(packageJson['zephyr:dependencies']) ===
1888
- JSON.stringify(expectedVerticalZephyrDependencies),
3911
+ sameJson(packageJson['zephyr:dependencies'], expectedVerticalZephyrDependencies),
1889
3912
  `${vertical.id} Zephyr dependencies must match declared vertical refs`,
1890
3913
  );
1891
3914
 
@@ -1895,8 +3918,8 @@ for (const vertical of fullStackVerticals) {
1895
3918
  assert(contractEntry?.deploy?.cloudflare?.workerName === expectedWorkerName(vertical.id), `${vertical.id} Cloudflare workerName is incorrect`);
1896
3919
  assert(contractEntry?.deploy?.cloudflare?.publicUrlEnv === `ULTRAMODERN_PUBLIC_URL_${vertical.id.replace(/-/g, '_').toUpperCase()}`, `${vertical.id} Cloudflare public URL env is incorrect`);
1897
3920
  assert(contractEntry?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, `${vertical.id} Cloudflare compatibilityDate is incorrect`);
1898
- assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), `${vertical.id} Cloudflare compatibility flags are incorrect`);
1899
- assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), `${vertical.id} Cloudflare security contract is incorrect`);
3921
+ assert(sameJson(contractEntry?.deploy?.cloudflare?.compatibilityFlags, expectedCloudflareCompatibilityFlags), `${vertical.id} Cloudflare compatibility flags are incorrect`);
3922
+ assert(sameJson(contractEntry?.deploy?.cloudflare?.security, expectedCloudflareSecurity), `${vertical.id} Cloudflare security contract is incorrect`);
1900
3923
  assertCloudflareQualityGates(vertical.id, contractEntry?.deploy?.cloudflare?.qualityGates);
1901
3924
  assert(contractEntry?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, `${vertical.id} worker compatibilityDate is incorrect`);
1902
3925
  assert(contractEntry?.deploy?.worker?.name === expectedWorkerName(vertical.id), `${vertical.id} worker name is incorrect`);
@@ -1912,37 +3935,64 @@ for (const vertical of fullStackVerticals) {
1912
3935
  assert(!verticalAssetPrefixExpression.includes('configuredSiteUrl') && !verticalAssetPrefixExpression.includes('MODERN_PUBLIC_SITE_URL'), `${vertical.id} asset prefix must not fall back to MODERN_PUBLIC_SITE_URL`);
1913
3936
  assert(modernConfig.includes(`envValue('ULTRAMODERN_PUBLIC_URL_${vertical.id.replace(/-/g, '_').toUpperCase()}')`), `${vertical.id} asset prefix must read its per-app public URL`);
1914
3937
  assert(modernConfig.includes('inferredCloudflareUrl'), `${vertical.id} asset prefix must support workers.dev origin inference`);
1915
- assert(modernConfig.includes("assetPrefix: '/'"), `${vertical.id} modern.config.ts must keep dev assets origin-relative`);
3938
+ assert(/dev:\s*\{[\s\S]*?\/\/ Remote dev manifests must publish an absolute publicPath[\s\S]*?assetPrefix,/u.test(modernConfig), `${vertical.id} modern.config.ts must publish dev assets from its own remote origin`);
1916
3939
  assert(modernConfig.includes('assetPrefix,'), `${vertical.id} modern.config.ts must wire output.assetPrefix to the derived asset prefix`);
1917
- assert(contractEntry?.config?.dev?.assetPrefix === '/', `${vertical.id} dev asset prefix must stay origin-relative`);
3940
+ assert(contractEntry?.config?.dev?.assetPrefix === 'app-public-origin', `${vertical.id} dev asset prefix must default to its app public origin`);
1918
3941
  assert(contractEntry?.config?.output?.assetPrefix?.default === 'app-public-origin', `${vertical.id} asset prefix must default to its app public origin`);
1919
- assert(JSON.stringify(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), `${vertical.id} asset prefix env fallback order is incorrect`);
3942
+ assert(sameJson(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder, ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), `${vertical.id} asset prefix env fallback order is incorrect`);
1920
3943
  assert(contractEntry?.config?.output?.disableTsChecker === false, `${vertical.id} must keep the framework TypeScript checker enabled`);
1921
3944
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.default === 'enabled', `${vertical.id} performance readiness diagnostics must be default-on`);
1922
3945
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', `${vertical.id} performance readiness diagnostics must only fail framework invariants by default`);
1923
3946
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.optOut?.config === 'scripts/ultramodern-performance-readiness.config.mjs', `${vertical.id} performance readiness opt-out config is incorrect`);
1924
- assert(contractEntry?.deploy?.cloudflare?.routes?.apiReadiness === `${vertical.apiPrefix}/${vertical.stem}/readiness`, `${vertical.id} Cloudflare proof readiness route is incorrect`);
3947
+ if (vertical.emitsApi) {
3948
+ // Per policy.ts:76 the Cloudflare proof advertises a REST readiness route
3949
+ // only for `rest` units; an `rpc` unit must not carry one.
3950
+ if (vertical.apiProtocol === 'rpc') {
3951
+ assert(contractEntry?.deploy?.cloudflare?.routes?.apiReadiness === undefined, `${vertical.id} rpc unit must not carry a REST Cloudflare readiness route`);
3952
+ } else {
3953
+ assert(contractEntry?.deploy?.cloudflare?.routes?.apiReadiness === `${vertical.apiPrefix}/${vertical.stem}/readiness`, `${vertical.id} Cloudflare proof readiness route is incorrect`);
3954
+ }
3955
+ }
1925
3956
  assert(contractEntry?.config?.rspack?.output?.uniqueName === vertical.mfName, `${vertical.id} Rspack uniqueName is incorrect`);
1926
3957
  assert(contractEntry?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal(vertical.mfName), `${vertical.id} Rspack chunkLoadingGlobal is incorrect`);
1927
3958
  assert(contractEntry?.moduleFederation?.name === vertical.mfName, `${vertical.id} MF name is incorrect`);
1928
- assert(JSON.stringify(contractEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), `${vertical.id} MF exposes are incorrect`);
1929
- assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'tsgo', `${vertical.id} must keep mandatory DTS compiler`);
1930
- assert(contractEntry?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', `${vertical.id} must keep dedicated Module Federation DTS tsconfig`);
1931
- assert(moduleFederationConfig.includes("tsConfigPath: './tsconfig.mf-types.json'"), `${vertical.id} Module Federation config must use the dedicated DTS tsconfig`);
1932
- assert(JSON.stringify(contractEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), `${vertical.id} MF verticalRefs are incorrect`);
3959
+ assert(sameJson(contractEntry?.moduleFederation?.exposes, vertical.exposes), `${vertical.id} MF exposes are incorrect`);
3960
+ // The browser Module Federation DTS surface and its config file only exist
3961
+ // for UI-emitting units; a headless api-only unit federates no browser types.
3962
+ if (vertical.emitsUi) {
3963
+ assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'effect-tsgo', `${vertical.id} must keep mandatory DTS compiler`);
3964
+ assert(contractEntry?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', `${vertical.id} must keep dedicated Module Federation DTS tsconfig`);
3965
+ assert(moduleFederationConfig.includes("tsConfigPath: './tsconfig.mf-types.json'"), `${vertical.id} Module Federation config must use the dedicated DTS tsconfig`);
3966
+ }
3967
+ assert(sameJson(contractEntry?.moduleFederation?.verticalRefs ?? [], vertical.verticalRefs), `${vertical.id} MF verticalRefs are incorrect`);
1933
3968
  assert(
1934
- JSON.stringify((contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id)) ===
1935
- JSON.stringify(vertical.verticalRefs),
3969
+ sameJson(
3970
+ (contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id),
3971
+ vertical.verticalRefs,
3972
+ ),
1936
3973
  `${vertical.id} MF consumed verticals are incorrect`,
1937
3974
  );
1938
- assert(contractEntry?.api?.prefix === vertical.apiPrefix, `${vertical.id} API prefix is incorrect`);
1939
- assert(contractEntry?.api?.group === vertical.group, `${vertical.id} API group is incorrect`);
1940
- assert(contractEntry?.api?.runtime === 'effect', `${vertical.id} API runtime must be Effect`);
1941
- assert(contractEntry?.api?.strictEffectApproach === true, `${vertical.id} strictEffectApproach must be enabled`);
1942
- assert(contractEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} readiness endpoint is incorrect`);
1943
- assert(contractEntry?.api?.operations?.readiness?.path === `/${vertical.stem}/readiness`, `${vertical.id} readiness operation is missing`);
1944
- assert(contractEntry?.api?.requestContext?.propagatedHeaders?.includes('traceparent'), `${vertical.id} trace context propagation is missing`);
1945
- assert(Object.keys(contractEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} domain operations are missing`);
3975
+ // API contract surface is only present for API-bearing units.
3976
+ if (vertical.emitsApi) {
3977
+ assert(contractEntry?.api?.prefix === vertical.apiPrefix, `${vertical.id} API prefix is incorrect`);
3978
+ assert(contractEntry?.api?.group === vertical.group, `${vertical.id} API group is incorrect`);
3979
+ assert(contractEntry?.api?.runtime === 'effect', `${vertical.id} API runtime must be Effect`);
3980
+ assert(contractEntry?.api?.strictEffectApproach === true, `${vertical.id} strictEffectApproach must be enabled`);
3981
+ assert(contractEntry?.api?.contract === vertical.apiContractExport, `${vertical.id} API contract export is incorrect`);
3982
+ assert(contractEntry?.api?.client === vertical.apiClientExport, `${vertical.id} API client export is incorrect`);
3983
+ if (vertical.apiProtocol === 'rpc') {
3984
+ // An `rpc` unit records the `/rpc` route/serialization; it must not carry
3985
+ // REST readiness or domain-operation semantics.
3986
+ assert(contractEntry?.api?.protocol === 'rpc', `${vertical.id} generated contract API protocol must be rpc`);
3987
+ assert(contractEntry?.api?.rpc?.path === '/rpc', `${vertical.id} generated contract RPC route path is incorrect`);
3988
+ assert(contractEntry?.api?.rpcPath === `${vertical.apiPrefix}/rpc`, `${vertical.id} generated contract RPC path is incorrect`);
3989
+ } else {
3990
+ assert(contractEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} readiness endpoint is incorrect`);
3991
+ assert(contractEntry?.api?.operations?.readiness?.path === `/${vertical.stem}/readiness`, `${vertical.id} readiness operation is missing`);
3992
+ assert(contractEntry?.api?.requestContext?.propagatedHeaders?.includes('traceparent'), `${vertical.id} trace context propagation is missing`);
3993
+ assert(Object.keys(contractEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} domain operations are missing`);
3994
+ }
3995
+ }
1946
3996
  assert(contractEntry?.i18n?.languages?.includes('en') && contractEntry?.i18n?.languages?.includes('cs'), `${vertical.id} must declare i18n languages`);
1947
3997
  assert(contractEntry?.i18n?.namespace === vertical.namespace, `${vertical.id} i18n namespace is incorrect`);
1948
3998
  assert(
@@ -1956,23 +4006,32 @@ for (const vertical of fullStackVerticals) {
1956
4006
  assert(contractEntry?.routes?.privateByDefault === true, `${vertical.id} routes must be private by default`);
1957
4007
  assert(contractEntry?.routes?.publicnessDefault === 'private-app-screen', `${vertical.id} route publicness default is incorrect`);
1958
4008
  assert(JSON.stringify(contractEntry?.routes?.publicRoutes ?? []) === '[]', `${vertical.id} must not expose generated public routes by default`);
1959
- assertPublicHeadContract(vertical.id, contractEntry?.routes?.publicHead, routeHead);
1960
- assertPublicSurfaceContract(vertical.id, contractEntry?.routes?.publicSurface);
1961
- assert(
1962
- (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
1963
- `${vertical.id} owned routes must be non-indexable private app screens by default and include description keys`,
1964
- );
1965
- assertPublicSurfaceAssets(vertical.path, contractEntry?.routes?.publicRoutes ?? []);
1966
- assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, `${vertical.id} CSS federation owner is missing`);
1967
- assert(contractEntry?.styling?.federation?.role === 'vertical-css', `${vertical.id} must own only vertical CSS`);
1968
- assert(contractEntry?.styling?.federation?.rootSelector === `[data-app-id="${vertical.id}"]`, `${vertical.id} CSS root selector is incorrect`);
1969
- assert(contractEntry?.styling?.federation?.classPrefix === `${vertical.tailwindPrefix}:`, `${vertical.id} CSS class prefix is incorrect`);
1970
- assert(contractEntry?.styling?.federation?.layers?.owned?.includes(`ultramodern-vertical-${vertical.domain}`), `${vertical.id} vertical CSS layer is missing`);
1971
- assert(!contractEntry?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-base'), `${vertical.id} must not own shell base CSS`);
1972
- assert(contractEntry?.styling?.federation?.entrypoints?.federationEntry === 'src/federation-entry.tsx', `${vertical.id} CSS contract must include federation entry`);
1973
- assert(contractEntry?.styling?.federation?.assets?.shared?.some(asset => asset.endsWith('/shared-design-tokens/tokens.css')), `${vertical.id} must import shared design token CSS`);
1974
- assert(contractEntry?.styling?.federation?.dedupe?.runtimeLoad === 'once-per-content-hash', `${vertical.id} CSS dedupe strategy is incorrect`);
1975
- assert(contractEntry?.styling?.federation?.ssr?.verticalCss === 'federated-manifest-owned-css', `${vertical.id} SSR CSS loading contract is incorrect`);
4009
+ // Public head/surface and owned browser routes only exist for UI-emitting
4010
+ // units; a headless api-only unit renders no route head or public surface.
4011
+ if (vertical.emitsUi) {
4012
+ assertPublicHeadContract(vertical.id, contractEntry?.routes?.publicHead, routeHead);
4013
+ assertPublicSurfaceContract(vertical.id, contractEntry?.routes?.publicSurface);
4014
+ assert(
4015
+ (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
4016
+ `${vertical.id} owned routes must be non-indexable private app screens by default and include description keys`,
4017
+ );
4018
+ assertPublicSurfaceAssets(vertical.path, contractEntry?.routes?.publicRoutes ?? []);
4019
+ }
4020
+ // CSS federation (a federated browser stylesheet keyed off the federation
4021
+ // entry) only applies to UI-emitting units; a headless api-only unit owns no
4022
+ // vertical CSS surface.
4023
+ if (vertical.emitsUi) {
4024
+ assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, `${vertical.id} CSS federation owner is missing`);
4025
+ assert(contractEntry?.styling?.federation?.role === 'vertical-css', `${vertical.id} must own only vertical CSS`);
4026
+ assert(contractEntry?.styling?.federation?.rootSelector === `[data-app-id="${vertical.id}"]`, `${vertical.id} CSS root selector is incorrect`);
4027
+ assert(contractEntry?.styling?.federation?.classPrefix === `${vertical.tailwindPrefix}:`, `${vertical.id} CSS class prefix is incorrect`);
4028
+ assert(contractEntry?.styling?.federation?.layers?.owned?.includes(`ultramodern-vertical-${vertical.domain}`), `${vertical.id} vertical CSS layer is missing`);
4029
+ assert(!contractEntry?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-base'), `${vertical.id} must not own shell base CSS`);
4030
+ assert(contractEntry?.styling?.federation?.entrypoints?.federationEntry === 'src/federation-entry.tsx', `${vertical.id} CSS contract must include federation entry`);
4031
+ assert(contractEntry?.styling?.federation?.assets?.shared?.some(asset => asset.endsWith('/shared-design-tokens/tokens.css')), `${vertical.id} must import shared design token CSS`);
4032
+ assert(contractEntry?.styling?.federation?.dedupe?.runtimeLoad === 'once-per-content-hash', `${vertical.id} CSS dedupe strategy is incorrect`);
4033
+ assert(contractEntry?.styling?.federation?.ssr?.verticalCss === 'federated-manifest-owned-css', `${vertical.id} SSR CSS loading contract is incorrect`);
4034
+ }
1976
4035
 
1977
4036
  const topologyEntry = topology.verticals?.find(verticalEntry => verticalEntry.id === vertical.id);
1978
4037
  assert(topologyEntry?.kind === 'vertical', `${vertical.id} topology kind is incorrect`);
@@ -1981,16 +4040,135 @@ for (const vertical of fullStackVerticals) {
1981
4040
  assert(topologyEntry?.moduleFederation?.name === vertical.mfName, `${vertical.id} topology MF name is incorrect`);
1982
4041
  assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), `${vertical.id} topology exposes are incorrect`);
1983
4042
  assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), `${vertical.id} topology verticalRefs are incorrect`);
1984
- assert(topologyEntry?.api?.bff?.prefix === vertical.apiPrefix, `${vertical.id} topology API prefix is incorrect`);
1985
- assert(topologyEntry?.api?.bff?.strictEffectApproach === true, `${vertical.id} topology strictEffectApproach is incorrect`);
1986
- assert(topologyEntry?.api?.serverEntry === `${vertical.path}/api/index.ts`, `${vertical.id} topology server entry is incorrect`);
1987
- assert(topologyEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} topology readiness endpoint is incorrect`);
1988
- assert(Object.keys(topologyEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} topology domain operations are missing`);
4043
+ // API/BFF topology metadata only exists for API-bearing units; and the REST
4044
+ // readiness/domain-operation surface is absent for the RPC protocol (G7a).
4045
+ if (vertical.emitsApi) {
4046
+ assert(topologyEntry?.api?.bff?.prefix === vertical.apiPrefix, `${vertical.id} topology API prefix is incorrect`);
4047
+ assert(topologyEntry?.api?.bff?.strictEffectApproach === true, `${vertical.id} topology strictEffectApproach is incorrect`);
4048
+ assert(topologyEntry?.api?.serverEntry === `${vertical.path}/api/index.ts`, `${vertical.id} topology server entry is incorrect`);
4049
+ if (vertical.apiProtocol !== 'rpc') {
4050
+ assert(topologyEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} topology readiness endpoint is incorrect`);
4051
+ assert(Object.keys(topologyEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} topology domain operations are missing`);
4052
+ }
4053
+ }
4054
+
4055
+ if (vertical.deliveryUnit) {
4056
+ const expectedDeliveryUnit = deliveryUnitBlock(
4057
+ expectedDeliveryUnitFor(vertical, topologyEntry),
4058
+ );
4059
+ const compactAppEntry = ultramodernConfig.topology?.apps?.find(
4060
+ entry => entry?.id === vertical.id,
4061
+ );
4062
+ // The backend-federation delivery-unit mirror only exists for API-bearing
4063
+ // units; a UI-only vertical carries just the app-level delivery unit.
4064
+ if (vertical.emitsApi) {
4065
+ const compactBackendDeliveryUnit = deliveryUnitBlock(
4066
+ compactAppEntry?.backendFederation?.deliveryUnit,
4067
+ );
4068
+ assertSameJson(
4069
+ deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4070
+ compactBackendDeliveryUnit,
4071
+ `${compactConfigPath} topology.apps.${vertical.id}.deliveryUnit`,
4072
+ deliveryUnitIdentityFixArea,
4073
+ );
4074
+ assertSameJson(
4075
+ compactBackendDeliveryUnit,
4076
+ expectedDeliveryUnit,
4077
+ `${compactConfigPath} topology.apps.${vertical.id}.backendFederation.deliveryUnit`,
4078
+ deliveryUnitIdentityFixArea,
4079
+ );
4080
+ assertSelfCheck(
4081
+ compactAppEntry?.backendFederation?.versionBoundary?.identityRoot === 'deliveryUnit',
4082
+ `${compactConfigPath} topology.apps.${vertical.id}.backendFederation.versionBoundary.identityRoot`,
4083
+ `Expected "deliveryUnit", found ${formatJson(compactAppEntry?.backendFederation?.versionBoundary?.identityRoot)}`,
4084
+ deliveryUnitIdentityFixArea,
4085
+ );
4086
+ }
4087
+ assertSameJson(
4088
+ deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4089
+ expectedDeliveryUnit,
4090
+ `${compactConfigPath} topology.apps.${vertical.id}.deliveryUnit`,
4091
+ deliveryUnitIdentityFixArea,
4092
+ );
4093
+ assertSameJson(
4094
+ deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4095
+ deliveryUnitBlock(topologyEntry?.deliveryUnit),
4096
+ `${compactConfigPath} vs topology/reference-topology.json verticals.${vertical.id}.deliveryUnit`,
4097
+ deliveryUnitIdentityFixArea,
4098
+ );
4099
+ }
1989
4100
 
1990
4101
  assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), `${vertical.id} ownership entry is missing`);
1991
4102
  assert(overlay.ports?.[vertical.id], `${vertical.id} development port is missing`);
1992
- assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), `${vertical.id} development manifest is missing`);
1993
- assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiPrefix), `${vertical.id} development API URL is missing`);
4103
+ if (vertical.emitsUi) {
4104
+ assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), `${vertical.id} development manifest is missing`);
4105
+ }
4106
+ if (vertical.emitsApi) {
4107
+ assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiProtocol === 'rpc' ? `${vertical.apiPrefix}/rpc` : vertical.apiPrefix), `${vertical.id} development API URL is missing`);
4108
+ }
4109
+ }
4110
+
4111
+ // Delivery-unit identity for ALL unit kinds (G29). Every workspace app —
4112
+ // shell, UI-only vertical, and API-bearing vertical — is an indivisible
4113
+ // delivery unit and must carry one consistent identity record across the
4114
+ // compact config, the reference topology, and its generated build artifact
4115
+ // (ADR-0019: one delivery-unit record, one build marker).
4116
+ for (const expectedApp of workspaceValidationContract.topology.compactConfig?.apps ?? []) {
4117
+ const unitLabel = `delivery-unit identity for ${expectedApp.id}`;
4118
+ const expectedDeliveryUnit = deliveryUnitBlock(expectedApp.deliveryUnit);
4119
+ assertSelfCheck(
4120
+ typeof expectedDeliveryUnit.unitId === 'string' && expectedDeliveryUnit.unitId.length > 0 &&
4121
+ typeof expectedDeliveryUnit.buildMarker === 'string' && expectedDeliveryUnit.buildMarker.length > 0,
4122
+ unitLabel,
4123
+ `Every unit kind must declare a delivery-unit record; found ${formatJson(expectedApp.deliveryUnit)}`,
4124
+ deliveryUnitIdentityFixArea,
4125
+ );
4126
+
4127
+ const compactAppEntry = ultramodernConfig.topology?.apps?.find(
4128
+ entry => entry?.id === expectedApp.id,
4129
+ );
4130
+ assertSameJson(
4131
+ compactAppEntry?.deploy?.cloudflare,
4132
+ expectedApp.deploy?.cloudflare,
4133
+ `${compactConfigPath} topology.apps.${expectedApp.id}.deploy.cloudflare`,
4134
+ 'regenerate the app Cloudflare deployment contract; do not add local proof-only smoke checks',
4135
+ );
4136
+ assertSameJson(
4137
+ deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4138
+ expectedDeliveryUnit,
4139
+ `${compactConfigPath} topology.apps.${expectedApp.id}.deliveryUnit`,
4140
+ deliveryUnitIdentityFixArea,
4141
+ );
4142
+
4143
+ const topologyUnitEntry =
4144
+ expectedApp.kind === 'shell'
4145
+ ? topology.shell
4146
+ : topology.verticals?.find(entry => entry?.id === expectedApp.id);
4147
+ assertSameJson(
4148
+ deliveryUnitBlock(topologyUnitEntry?.deliveryUnit),
4149
+ expectedDeliveryUnit,
4150
+ `topology/reference-topology.json ${expectedApp.kind === 'shell' ? 'shell' : `verticals.${expectedApp.id}`}.deliveryUnit`,
4151
+ deliveryUnitIdentityFixArea,
4152
+ );
4153
+
4154
+ const appPath = expectedApp.path;
4155
+ const buildArtifactPath = `${appPath}/shared/ultramodern-build.json`;
4156
+ assertExists(buildArtifactPath);
4157
+ const buildIdentity = readJson(buildArtifactPath).deliveryUnit ?? {};
4158
+ assertSelfCheck(
4159
+ buildIdentity.unitId === expectedDeliveryUnit.unitId &&
4160
+ buildIdentity.buildMarker === expectedDeliveryUnit.buildMarker,
4161
+ `${buildArtifactPath} deliveryUnit`,
4162
+ `Expected ${formatJson({ unitId: expectedDeliveryUnit.unitId, buildMarker: expectedDeliveryUnit.buildMarker })}, found ${formatJson({ unitId: buildIdentity.unitId, buildMarker: buildIdentity.buildMarker })}`,
4163
+ deliveryUnitIdentityFixArea,
4164
+ );
4165
+ const buildModuleSource = readText(`${appPath}/shared/ultramodern-build.ts`);
4166
+ assertSelfCheck(
4167
+ buildModuleSource.includes('export const ultramodernDeliveryUnit'),
4168
+ `${appPath}/shared/ultramodern-build.ts`,
4169
+ 'Missing ultramodernDeliveryUnit export',
4170
+ deliveryUnitIdentityFixArea,
4171
+ );
1994
4172
  }
1995
4173
 
1996
4174
  console.log('UltraModern workspace scaffold validated');