@bleedingdev/modern-js-create 3.2.0-ultramodern.2 → 3.2.0-ultramodern.21

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 (63) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +676 -198
  3. package/package.json +5 -2
  4. package/template/.agents/skills-lock.json +34 -0
  5. package/template/.github/renovate.json +53 -0
  6. package/template/.github/workflows/ultramodern-gates.yml.handlebars +26 -4
  7. package/template/AGENTS.md +27 -0
  8. package/template/README.md +7 -3
  9. package/template/api/effect/index.ts.handlebars +7 -45
  10. package/template/config/public/locales/cs/translation.json +39 -0
  11. package/template/config/public/locales/en/translation.json +39 -0
  12. package/template/modern.config.ts.handlebars +44 -23
  13. package/template/oxfmt.config.ts +8 -0
  14. package/template/oxlint.config.ts +12 -0
  15. package/template/package.json.handlebars +56 -27
  16. package/template/pnpm-workspace.yaml +24 -0
  17. package/template/rstest.config.mts +7 -0
  18. package/template/scripts/bootstrap-agent-skills.mjs +95 -0
  19. package/template/scripts/check-i18n-strings.mjs +83 -0
  20. package/template/scripts/validate-ultramodern.mjs.handlebars +350 -16
  21. package/template/shared/effect/api.ts.handlebars +1 -2
  22. package/template/src/modern-app-env.d.ts +2 -0
  23. package/template/src/modern.runtime.ts.handlebars +17 -3
  24. package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
  25. package/template/src/routes/index.css.handlebars +14 -3
  26. package/template/src/routes/layout.tsx.handlebars +1 -1
  27. package/template/tests/tsconfig.json +7 -0
  28. package/template/tests/ultramodern.contract.test.ts +67 -0
  29. package/template/tsconfig.json +106 -2
  30. package/template-workspace/.agents/agent-reference-repos.json +24 -0
  31. package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
  32. package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
  33. package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
  34. package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
  35. package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
  36. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
  37. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
  38. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
  39. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
  40. package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
  41. package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
  42. package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
  43. package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
  44. package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
  45. package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
  46. package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
  47. package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
  48. package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
  49. package/template-workspace/.agents/skills-lock.json +95 -0
  50. package/template-workspace/.github/renovate.json +29 -0
  51. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +52 -0
  52. package/template-workspace/.gitignore.handlebars +5 -0
  53. package/template-workspace/AGENTS.md +61 -0
  54. package/template-workspace/README.md.handlebars +11 -1
  55. package/template-workspace/oxfmt.config.ts +16 -0
  56. package/template-workspace/oxlint.config.ts +19 -0
  57. package/template-workspace/pnpm-workspace.yaml +24 -6
  58. package/template-workspace/scripts/bootstrap-agent-skills.mjs +95 -0
  59. package/template-workspace/scripts/check-i18n-strings.mjs +83 -0
  60. package/template-workspace/scripts/setup-agent-reference-repos.mjs +364 -0
  61. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +405 -59
  62. package/template/biome.json +0 -41
  63. package/template/src/routes/page.tsx.handlebars +0 -119
@@ -4,52 +4,99 @@ import path from 'node:path';
4
4
  const root = process.cwd();
5
5
  const packageScope = '{{packageScope}}';
6
6
  const tanstackVersion = '1.170.1';
7
+ const rstackAgentSkillsCommit = '61c948b42512e223bad44b83af4080eba48b2677';
7
8
  const modernPackages = [
8
9
  '@modern-js/app-tools',
9
10
  '@modern-js/plugin-bff',
11
+ '@modern-js/plugin-i18n',
10
12
  '@modern-js/plugin-tanstack',
11
13
  '@modern-js/runtime',
12
14
  ];
15
+ const baselineAgentSkills = [
16
+ 'rsbuild-best-practices',
17
+ 'rspack-best-practices',
18
+ 'rspack-tracing',
19
+ 'rsdoctor-analysis',
20
+ 'rslib-best-practices',
21
+ 'rslib-modern-package',
22
+ 'rstest-best-practices',
23
+ ];
24
+ const privateAgentSkills = ['plan-graph', 'dag', 'subagent-graph', 'helm', 'debugger-mode'];
13
25
 
14
- function readText(relativePath) {
15
- return fs.readFileSync(path.join(root, relativePath), 'utf-8');
16
- }
17
-
18
- function readJson(relativePath) {
19
- return JSON.parse(readText(relativePath));
20
- }
21
-
22
- function assert(condition, message) {
26
+ const readText = (relativePath) => fs.readFileSync(path.join(root, relativePath), 'utf-8');
27
+ const readJson = (relativePath) => JSON.parse(readText(relativePath));
28
+ const assert = (condition, message) => {
23
29
  if (!condition) {
24
30
  throw new Error(message);
25
31
  }
26
- }
27
-
28
- function assertExists(relativePath) {
32
+ };
33
+ const assertExists = (relativePath) => {
29
34
  assert(fs.existsSync(path.join(root, relativePath)), `Missing ${relativePath}`);
30
- }
35
+ };
31
36
 
32
37
  const requiredPaths = [
38
+ 'AGENTS.md',
39
+ '.gitignore',
33
40
  'package.json',
34
41
  'pnpm-workspace.yaml',
35
42
  'tsconfig.base.json',
43
+ 'oxlint.config.ts',
44
+ 'oxfmt.config.ts',
45
+ '.github/renovate.json',
46
+ '.github/workflows/ultramodern-workspace-gates.yml',
47
+ '.agents/skills-lock.json',
48
+ '.agents/agent-reference-repos.json',
49
+ '.agents/rstackjs-agent-skills-LICENSE',
50
+ '.agents/skills/rsbuild-best-practices/SKILL.md',
51
+ '.agents/skills/rspack-best-practices/SKILL.md',
52
+ '.agents/skills/rspack-tracing/SKILL.md',
53
+ '.agents/skills/rspack-tracing/references/tracing-guide.md',
54
+ '.agents/skills/rspack-tracing/scripts/analyze_trace.js',
55
+ '.agents/skills/rsdoctor-analysis/SKILL.md',
56
+ '.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md',
57
+ '.agents/skills/rslib-best-practices/SKILL.md',
58
+ '.agents/skills/rslib-modern-package/SKILL.md',
59
+ '.agents/skills/rstest-best-practices/SKILL.md',
36
60
  'topology/reference-topology.json',
37
61
  'topology/ownership.json',
38
62
  'topology/local-overlays/development.json',
39
63
  '.modernjs/ultramodern-workspace-template-manifest.json',
40
64
  '.modernjs/ultramodern-package-source.json',
65
+ 'scripts/bootstrap-agent-skills.mjs',
66
+ 'scripts/setup-agent-reference-repos.mjs',
67
+ 'scripts/check-i18n-strings.mjs',
41
68
  'apps/shell-super-app/package.json',
69
+ 'apps/shell-super-app/config/public/locales/en/translation.json',
70
+ 'apps/shell-super-app/config/public/locales/cs/translation.json',
42
71
  'apps/shell-super-app/modern.config.ts',
43
72
  'apps/shell-super-app/module-federation.config.ts',
73
+ 'apps/shell-super-app/src/modern-app-env.d.ts',
74
+ 'apps/shell-super-app/src/modern.runtime.ts',
75
+ 'apps/shell-super-app/src/routes/[lang]/page.tsx',
44
76
  'apps/remotes/remote-commerce/package.json',
77
+ 'apps/remotes/remote-commerce/config/public/locales/en/translation.json',
78
+ 'apps/remotes/remote-commerce/config/public/locales/cs/translation.json',
45
79
  'apps/remotes/remote-commerce/modern.config.ts',
46
80
  'apps/remotes/remote-commerce/module-federation.config.ts',
81
+ 'apps/remotes/remote-commerce/src/modern-app-env.d.ts',
82
+ 'apps/remotes/remote-commerce/src/modern.runtime.ts',
83
+ 'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
47
84
  'apps/remotes/remote-identity/package.json',
85
+ 'apps/remotes/remote-identity/config/public/locales/en/translation.json',
86
+ 'apps/remotes/remote-identity/config/public/locales/cs/translation.json',
48
87
  'apps/remotes/remote-identity/modern.config.ts',
49
88
  'apps/remotes/remote-identity/module-federation.config.ts',
89
+ 'apps/remotes/remote-identity/src/modern-app-env.d.ts',
90
+ 'apps/remotes/remote-identity/src/modern.runtime.ts',
91
+ 'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
50
92
  'apps/remotes/remote-design-system/package.json',
93
+ 'apps/remotes/remote-design-system/config/public/locales/en/translation.json',
94
+ 'apps/remotes/remote-design-system/config/public/locales/cs/translation.json',
51
95
  'apps/remotes/remote-design-system/modern.config.ts',
52
96
  'apps/remotes/remote-design-system/module-federation.config.ts',
97
+ 'apps/remotes/remote-design-system/src/modern-app-env.d.ts',
98
+ 'apps/remotes/remote-design-system/src/modern.runtime.ts',
99
+ 'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
53
100
  'services/service-recommendations-effect/package.json',
54
101
  'services/service-recommendations-effect/modern.config.ts',
55
102
  'services/service-recommendations-effect/api/effect/index.ts',
@@ -63,14 +110,30 @@ for (const requiredPath of requiredPaths) {
63
110
  assertExists(requiredPath);
64
111
  }
65
112
 
113
+ for (const appDirectory of [
114
+ 'apps/shell-super-app',
115
+ 'apps/remotes/remote-commerce',
116
+ 'apps/remotes/remote-identity',
117
+ 'apps/remotes/remote-design-system',
118
+ ]) {
119
+ assert(
120
+ !fs.existsSync(path.join(root, appDirectory, 'src/routes/page.tsx')),
121
+ `${appDirectory} must use src/routes/[lang]/page.tsx`,
122
+ );
123
+ }
124
+
66
125
  const rootPackage = readJson('package.json');
67
126
  const packageSource = readJson('.modernjs/ultramodern-package-source.json');
127
+ const skillsLock = readJson('.agents/skills-lock.json');
128
+ const agentReferenceRepos = readJson('.agents/agent-reference-repos.json');
129
+ const pnpmWorkspace = readText('pnpm-workspace.yaml');
130
+ const workflowContent = readText('.github/workflows/ultramodern-workspace-gates.yml');
131
+ const renovateConfig = readJson('.github/renovate.json');
132
+ const deprecatedTanstackRuntime = '@modern-js/runtime/' + 'tanstack-router';
68
133
  const expectedModernSpecifier =
69
- packageSource.strategy === 'install'
70
- ? packageSource.modernPackages?.specifier
71
- : 'workspace:*';
134
+ packageSource.strategy === 'install' ? packageSource.modernPackages?.specifier : 'workspace:*';
72
135
 
73
- function expectedModernDependency(packageName) {
136
+ const expectedModernDependency = (packageName) => {
74
137
  if (packageSource.strategy !== 'install') {
75
138
  return 'workspace:*';
76
139
  }
@@ -79,13 +142,74 @@ function expectedModernDependency(packageName) {
79
142
  return aliasPackageName
80
143
  ? `npm:${aliasPackageName}@${expectedModernSpecifier}`
81
144
  : expectedModernSpecifier;
82
- }
145
+ };
83
146
 
84
147
  assert(rootPackage.private === true, 'Root package must be private');
85
148
  assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
149
+ assert(rootPackage.packageManager === 'pnpm@11.1.2', 'Root must pin pnpm 11.1.2');
150
+ assert(rootPackage.engines?.pnpm === '>=11.0.0', 'Root must require pnpm >=11');
151
+ for (const requiredSnippet of [
152
+ 'packages:\n - apps/*\n - apps/remotes/*\n - services/*\n - packages/*',
153
+ 'minimumReleaseAge: 1440',
154
+ 'minimumReleaseAgeStrict: true',
155
+ 'minimumReleaseAgeIgnoreMissingTime: false',
156
+ "minimumReleaseAgeExclude:\n - '@modern-js/*'\n - '@bleedingdev/*'\n - '@effect/tsgo'\n - '@effect/tsgo-*'\n - '@typescript/native-preview'\n - '@typescript/native-preview-*'",
157
+ 'trustPolicy: no-downgrade',
158
+ 'trustPolicyIgnoreAfter: 1440',
159
+ 'blockExoticSubdeps: true',
160
+ 'engineStrict: true',
161
+ 'pmOnFail: error',
162
+ 'verifyDepsBeforeRun: error',
163
+ 'strictDepBuilds: true',
164
+ "allowBuilds:\n '@swc/core': true\n core-js: true\n esbuild: true\n msgpackr-extract: true\n simple-git-hooks: true",
165
+ ]) {
166
+ assert(
167
+ pnpmWorkspace.includes(requiredSnippet),
168
+ `pnpm-workspace.yaml must retain ${requiredSnippet.split('\n')[0]}`,
169
+ );
170
+ }
171
+ assert(
172
+ !pnpmWorkspace.includes('onlyBuiltDependencies'),
173
+ 'pnpm-workspace.yaml must use pnpm 11 allowBuilds instead of onlyBuiltDependencies',
174
+ );
175
+ for (const requiredSnippet of [
176
+ 'permissions:\n contents: read',
177
+ 'pull_request:',
178
+ 'persist-credentials: false',
179
+ 'pnpm install --frozen-lockfile',
180
+ 'pnpm -r --filter "./apps/**" run build',
181
+ 'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
182
+ 'timeout-minutes:',
183
+ 'egress-policy: audit',
184
+ ]) {
185
+ assert(
186
+ workflowContent.includes(requiredSnippet),
187
+ `Generated workspace workflow must retain ${requiredSnippet.split('\n')[0]}`,
188
+ );
189
+ }
86
190
  assert(
87
- rootPackage.modernjs?.packageSource?.config ===
88
- './.modernjs/ultramodern-package-source.json',
191
+ !workflowContent.includes('pull_request_target'),
192
+ 'Generated workspace workflow must not use pull_request_target',
193
+ );
194
+ for (const match of workflowContent.matchAll(/^\s*uses:\s*([^@\s]+)@([^\s#]+)/gmu)) {
195
+ const [, actionName, actionRef] = match;
196
+ assert(
197
+ /^[a-f0-9]{40}$/u.test(actionRef),
198
+ `Generated workspace workflow must pin ${actionName}@${actionRef} to a commit SHA`,
199
+ );
200
+ }
201
+ assert(
202
+ renovateConfig.dependencyDashboard === true &&
203
+ renovateConfig.minimumReleaseAge === '1 day' &&
204
+ renovateConfig.extends?.includes('helpers:pinGitHubActionDigests') &&
205
+ renovateConfig.packageRules?.some(
206
+ (rule) =>
207
+ rule.dependencyDashboardApproval === true && rule.matchUpdateTypes?.includes('major'),
208
+ ),
209
+ 'Generated workspace Renovate config must retain dashboard, release-age, action pinning, and major-approval policy',
210
+ );
211
+ assert(
212
+ rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json',
89
213
  'Root must point to the UltraModern package source metadata',
90
214
  );
91
215
  assert(
@@ -101,21 +225,122 @@ assert(
101
225
  'Generated shared packages must keep workspace:* links',
102
226
  );
103
227
  assert(
104
- modernPackages.every(packageName =>
228
+ modernPackages.every((packageName) =>
105
229
  packageSource.modernPackages?.packages?.includes(packageName),
106
230
  ),
107
231
  'Package source metadata must list all Modern runtime/tooling packages',
108
232
  );
109
233
  assert(
110
- expectedModernSpecifier &&
111
- packageSource.modernPackages?.specifier === expectedModernSpecifier,
234
+ expectedModernSpecifier && packageSource.modernPackages?.specifier === expectedModernSpecifier,
112
235
  'Package source metadata must provide a Modern package specifier',
113
236
  );
237
+
238
+ const requiredRootScripts = {
239
+ format: 'oxfmt .',
240
+ 'format:check': 'oxfmt --check .',
241
+ 'i18n:check': 'node ./scripts/check-i18n-strings.mjs',
242
+ lint: 'oxlint .',
243
+ 'lint:fix': 'oxlint . --fix',
244
+ 'agents:refs:check': 'node ./scripts/setup-agent-reference-repos.mjs --check',
245
+ 'agents:refs:install': 'node ./scripts/setup-agent-reference-repos.mjs',
246
+ postinstall: 'node ./scripts/setup-agent-reference-repos.mjs',
247
+ 'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
248
+ 'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
249
+ typecheck: `pnpm -r --filter "@${packageScope}/*" typecheck`,
250
+ 'ultramodern:check': 'node ./scripts/validate-ultramodern-workspace.mjs',
251
+ };
252
+ for (const [scriptName, scriptCommand] of Object.entries(requiredRootScripts)) {
253
+ assert(rootPackage.scripts?.[scriptName] === scriptCommand, `Root must expose ${scriptName}`);
254
+ }
255
+
256
+ for (const dependency of [
257
+ '@effect/tsgo',
258
+ '@typescript/native-preview',
259
+ 'oxfmt',
260
+ 'oxlint',
261
+ 'ultracite',
262
+ ]) {
263
+ assert(rootPackage.devDependencies?.[dependency], `Root must depend on ${dependency}`);
264
+ }
265
+
266
+ const agentsInstructions = readText('AGENTS.md');
267
+ assert(
268
+ agentsInstructions.includes('UltraModern Agent Contract') &&
269
+ agentsInstructions.includes('Required Skill Baseline'),
270
+ 'Root AGENTS.md must document the UltraModern agent contract',
271
+ );
272
+ assert(
273
+ skillsLock.source?.repository === 'https://github.com/rstackjs/agent-skills',
274
+ 'Agent skills lock must retain the Rstack skill source repository',
275
+ );
276
+ assert(
277
+ skillsLock.source?.commit === rstackAgentSkillsCommit,
278
+ 'Agent skills lock must pin the expected Rstack skill commit',
279
+ );
280
+ assert(
281
+ skillsLock.installDir === '.agents/skills',
282
+ 'Agent skills lock must use .agents/skills as installDir',
283
+ );
284
+ assert(
285
+ agentReferenceRepos.defaultEnabled === true,
286
+ 'Agent reference repositories must be enabled by default',
287
+ );
288
+ assert(
289
+ agentReferenceRepos.strategy === 'git-subtree-squash',
290
+ 'Agent reference repositories must use git subtree squash strategy',
291
+ );
292
+ assert(
293
+ agentReferenceRepos.repositories?.some(
294
+ (repo) =>
295
+ repo.id === 'effect' &&
296
+ repo.url === 'https://github.com/Effect-TS/effect.git' &&
297
+ repo.path === 'repos/effect',
298
+ ),
299
+ 'Agent reference repositories must include Effect',
300
+ );
301
+ assert(
302
+ agentReferenceRepos.repositories?.some(
303
+ (repo) =>
304
+ repo.id === 'ultramodern-js' &&
305
+ repo.url === 'https://github.com/BleedingDev/ultramodern.js.git' &&
306
+ repo.path === 'repos/ultramodern.js',
307
+ ),
308
+ 'Agent reference repositories must include UltraModern.js',
309
+ );
310
+ assert(
311
+ readText('scripts/setup-agent-reference-repos.mjs').includes(
312
+ 'ULTRAMODERN_SKIP_AGENT_REPOS',
313
+ ),
314
+ 'Agent reference repository setup must expose an opt-out environment variable',
315
+ );
114
316
  assert(
115
- rootPackage.scripts?.['ultramodern:check'] ===
116
- 'node ./scripts/validate-ultramodern-workspace.mjs',
117
- 'Root must expose the ultramodern:check script',
317
+ readText('scripts/setup-agent-reference-repos.mjs').includes('git-subtree-dir'),
318
+ 'Agent reference repository setup must validate git subtree evidence',
118
319
  );
320
+ for (const skillName of baselineAgentSkills) {
321
+ assert(
322
+ skillsLock.baseline?.some((skill) => skill.name === skillName),
323
+ `Agent skills lock must include ${skillName}`,
324
+ );
325
+ assert(
326
+ readText(`.agents/skills/${skillName}/SKILL.md`).includes(`name: ${skillName}`),
327
+ `${skillName} must contain matching skill metadata`,
328
+ );
329
+ }
330
+
331
+ const privateSource = skillsLock.sources?.find(
332
+ (source) => source.repository === 'https://github.com/TechsioCZ/skills',
333
+ );
334
+ assert(
335
+ privateSource?.install === 'clone-if-authorized',
336
+ 'Agent skills lock must configure TechsioCZ skills as clone-if-authorized',
337
+ );
338
+ for (const skillName of privateAgentSkills) {
339
+ assert(
340
+ privateSource.baseline?.some((skill) => skill.name === skillName),
341
+ `Agent skills lock must allowlist private skill ${skillName}`,
342
+ );
343
+ }
119
344
 
120
345
  const appPackagePaths = [
121
346
  'apps/shell-super-app/package.json',
@@ -126,31 +351,47 @@ const appPackagePaths = [
126
351
 
127
352
  for (const packagePath of appPackagePaths) {
128
353
  const packageJson = readJson(packagePath);
354
+ assert(
355
+ packageJson.dependencies?.['@modern-js/plugin-i18n'] ===
356
+ expectedModernDependency('@modern-js/plugin-i18n'),
357
+ `${packagePath} must use @modern-js/plugin-i18n through ${expectedModernDependency(
358
+ '@modern-js/plugin-i18n',
359
+ )}`,
360
+ );
129
361
  assert(
130
362
  packageJson.dependencies?.['@modern-js/plugin-tanstack'] ===
131
363
  expectedModernDependency('@modern-js/plugin-tanstack'),
132
- `${packagePath} must depend on @modern-js/plugin-tanstack through ${expectedModernDependency('@modern-js/plugin-tanstack')}`,
364
+ `${packagePath} must use @modern-js/plugin-tanstack through ${expectedModernDependency(
365
+ '@modern-js/plugin-tanstack',
366
+ )}`,
133
367
  );
134
368
  assert(
135
369
  packageJson.dependencies?.['@modern-js/runtime'] ===
136
370
  expectedModernDependency('@modern-js/runtime'),
137
- `${packagePath} must depend on @modern-js/runtime through ${expectedModernDependency('@modern-js/runtime')}`,
371
+ `${packagePath} must use @modern-js/runtime through ${expectedModernDependency(
372
+ '@modern-js/runtime',
373
+ )}`,
138
374
  );
139
375
  assert(
140
376
  packageJson.devDependencies?.['@modern-js/app-tools'] ===
141
377
  expectedModernDependency('@modern-js/app-tools'),
142
- `${packagePath} must depend on @modern-js/app-tools through ${expectedModernDependency('@modern-js/app-tools')}`,
378
+ `${packagePath} must use @modern-js/app-tools through ${expectedModernDependency(
379
+ '@modern-js/app-tools',
380
+ )}`,
143
381
  );
144
382
  assert(
145
- packageJson.dependencies?.[`@${packageScope}/shared-contracts`] ===
146
- 'workspace:*',
383
+ packageJson.dependencies?.[`@${packageScope}/shared-contracts`] === 'workspace:*',
147
384
  `${packagePath} must link generated shared contracts through workspace:*`,
148
385
  );
149
386
  assert(
150
- packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] ===
151
- 'workspace:*',
387
+ packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] === 'workspace:*',
152
388
  `${packagePath} must link generated shared design tokens through workspace:*`,
153
389
  );
390
+ assert(packageJson.dependencies?.i18next === '26.2.0', `${packagePath} must include i18next`);
391
+ assert(
392
+ packageJson.dependencies?.['react-i18next'] === '17.0.8',
393
+ `${packagePath} must include react-i18next`,
394
+ );
154
395
  assert(
155
396
  packageJson.dependencies?.['@tanstack/react-router'] === tanstackVersion,
156
397
  `${packagePath} must use @tanstack/react-router ${tanstackVersion}`,
@@ -159,6 +400,10 @@ for (const packagePath of appPackagePaths) {
159
400
  packageJson.dependencies?.['@module-federation/modern-js-v3'] === '2.4.0',
160
401
  `${packagePath} must include the Module Federation plugin`,
161
402
  );
403
+ assert(
404
+ packageJson.dependencies?.['zephyr-modernjs-plugin'] === '1.1.1',
405
+ `${packagePath} must include the official Zephyr Modern.js plugin`,
406
+ );
162
407
  assert(
163
408
  packageJson.modernjs?.preset === 'presetUltramodern',
164
409
  `${packagePath} must keep presetUltramodern metadata`,
@@ -173,57 +418,133 @@ for (const configPath of [
173
418
  ]) {
174
419
  const config = readText(configPath);
175
420
  assert(config.includes('presetUltramodern('), `${configPath} must use presetUltramodern`);
421
+ assert(config.includes('i18nPlugin('), `${configPath} must enable plugin-i18n`);
422
+ assert(config.includes('localePathRedirect: true'), `${configPath} must prefix localized URLs`);
423
+ assert(config.includes('ULTRAMODERN_SITE_URL'), `${configPath} must expose site URL metadata`);
424
+ assert(
425
+ config.includes('MODERN_PUBLIC_SITE_URL must be set for production builds'),
426
+ `${configPath} must require MODERN_PUBLIC_SITE_URL for production builds`,
427
+ );
176
428
  assert(config.includes('tanstackRouterPlugin()'), `${configPath} must enable plugin-tanstack`);
177
- assert(config.includes('moduleFederationPlugin()'), `${configPath} must enable Module Federation`);
429
+ assert(
430
+ config.includes('moduleFederationPlugin()'),
431
+ `${configPath} must enable Module Federation`,
432
+ );
433
+ assert(
434
+ config.includes("from 'zephyr-modernjs-plugin'"),
435
+ `${configPath} must import the official Zephyr Modern.js plugin`,
436
+ );
437
+ assert(config.includes('withZephyr()'), `${configPath} must enable Zephyr through plugins`);
438
+ assert(
439
+ config.includes("bundler: 'rspack'"),
440
+ `${configPath} must keep appTools on the rspack bundler for Zephyr`,
441
+ );
442
+ assert(
443
+ config.includes("html: './'"),
444
+ `${configPath} must keep Modern.js output.distPath.html compatible with Zephyr`,
445
+ );
446
+ assert(
447
+ config.includes("outputStructure: 'flat'"),
448
+ `${configPath} must keep Modern.js HTML output flat for Zephyr`,
449
+ );
450
+ assert(
451
+ config.includes("mainEntryName: 'index'"),
452
+ `${configPath} must keep Modern.js source.mainEntryName compatible with Zephyr`,
453
+ );
454
+ }
455
+
456
+ for (const routePath of [
457
+ 'apps/shell-super-app/src/routes/[lang]/page.tsx',
458
+ 'apps/remotes/remote-commerce/src/routes/[lang]/page.tsx',
459
+ 'apps/remotes/remote-identity/src/routes/[lang]/page.tsx',
460
+ 'apps/remotes/remote-design-system/src/routes/[lang]/page.tsx',
461
+ ]) {
462
+ const route = readText(routePath);
463
+ assert(route.includes('rel="canonical"'), `${routePath} must emit canonical metadata`);
464
+ assert(route.includes('rel="alternate"'), `${routePath} must emit alternate locale metadata`);
465
+ assert(route.includes('hrefLang="x-default"'), `${routePath} must emit x-default metadata`);
466
+ assert(route.includes('localizedPath('), `${routePath} must build localized URLs`);
467
+ assert(
468
+ route.includes('@modern-js/plugin-tanstack/runtime'),
469
+ `${routePath} must import TanStack runtime from @modern-js/plugin-tanstack/runtime`,
470
+ );
471
+ assert(
472
+ !route.includes(deprecatedTanstackRuntime),
473
+ `${routePath} must not import deprecated TanStack runtime path`,
474
+ );
178
475
  }
179
476
 
180
477
  const shellMf = readText('apps/shell-super-app/module-federation.config.ts');
181
478
  assert(shellMf.includes("name: 'shellSuperApp'"), 'Shell MF config must name the shell');
182
- assert(shellMf.includes('remoteCommerce@http://localhost:3021/mf-manifest.json'), 'Shell must reference commerce remote');
183
- assert(shellMf.includes('remoteIdentity@http://localhost:3022/mf-manifest.json'), 'Shell must reference identity remote');
184
- assert(shellMf.includes('remoteDesignSystem@http://localhost:3023/mf-manifest.json'), 'Shell must reference design-system remote');
479
+ assert(
480
+ shellMf.includes('remoteCommerce@http://localhost:3021/mf-manifest.json'),
481
+ 'Shell must reference commerce remote',
482
+ );
483
+ assert(
484
+ shellMf.includes('remoteIdentity@http://localhost:3022/mf-manifest.json'),
485
+ 'Shell must reference identity remote',
486
+ );
487
+ assert(
488
+ shellMf.includes('remoteDesignSystem@http://localhost:3023/mf-manifest.json'),
489
+ 'Shell must reference design-system remote',
490
+ );
185
491
 
186
492
  const commerceMf = readText('apps/remotes/remote-commerce/module-federation.config.ts');
187
493
  assert(commerceMf.includes("name: 'remoteCommerce'"), 'Commerce remote MF name is missing');
188
- assert(commerceMf.includes('"./Widget"'), 'Commerce remote must expose a widget');
494
+ assert(commerceMf.includes("'./Widget'"), 'Commerce remote must expose a widget');
189
495
 
190
496
  const designMf = readText('apps/remotes/remote-design-system/module-federation.config.ts');
191
497
  assert(designMf.includes("name: 'remoteDesignSystem'"), 'Design-system remote MF name is missing');
192
- assert(designMf.includes('"./Button"'), 'Design-system remote must expose Button');
193
- assert(designMf.includes('"./tokens"'), 'Design-system remote must expose tokens');
498
+ assert(designMf.includes("'./Button'"), 'Design-system remote must expose Button');
499
+ assert(designMf.includes("'./tokens'"), 'Design-system remote must expose tokens');
194
500
 
195
501
  const servicePackage = readJson('services/service-recommendations-effect/package.json');
196
502
  assert(
197
503
  servicePackage.dependencies?.['@modern-js/runtime'] ===
198
504
  expectedModernDependency('@modern-js/runtime'),
199
- `Effect service must use @modern-js/runtime through ${expectedModernDependency('@modern-js/runtime')}`,
505
+ `Effect service must use @modern-js/runtime through ${expectedModernDependency(
506
+ '@modern-js/runtime',
507
+ )}`,
200
508
  );
201
509
  assert(
202
510
  servicePackage.devDependencies?.['@modern-js/app-tools'] ===
203
511
  expectedModernDependency('@modern-js/app-tools'),
204
- `Effect service must use @modern-js/app-tools through ${expectedModernDependency('@modern-js/app-tools')}`,
512
+ `Effect service must use @modern-js/app-tools through ${expectedModernDependency(
513
+ '@modern-js/app-tools',
514
+ )}`,
205
515
  );
206
516
  assert(
207
517
  servicePackage.devDependencies?.['@modern-js/plugin-bff'] ===
208
518
  expectedModernDependency('@modern-js/plugin-bff'),
209
- `Effect service must use @modern-js/plugin-bff through ${expectedModernDependency('@modern-js/plugin-bff')}`,
519
+ `Effect service must use @modern-js/plugin-bff through ${expectedModernDependency(
520
+ '@modern-js/plugin-bff',
521
+ )}`,
210
522
  );
211
523
  assert(
212
- servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] ===
213
- 'workspace:*',
524
+ servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] === 'workspace:*',
214
525
  'Effect service must link generated shared Effect API through workspace:*',
215
526
  );
216
527
 
217
528
  const serviceConfig = readText('services/service-recommendations-effect/modern.config.ts');
218
- assert(serviceConfig.includes("runtimeFramework: 'effect'"), 'Effect service must use Effect runtime');
529
+ assert(
530
+ serviceConfig.includes("runtimeFramework: 'effect'"),
531
+ 'Effect service must use Effect runtime',
532
+ );
219
533
  assert(serviceConfig.includes('bffPlugin()'), 'Effect service must enable bffPlugin');
220
534
 
221
535
  const serviceEntry = readText('services/service-recommendations-effect/api/effect/index.ts');
222
- assert(serviceEntry.includes('defineEffectBff'), 'Effect service must expose defineEffectBff placeholder');
223
- assert(serviceEntry.includes('recommendationsEffectApi'), 'Effect service must use shared recommendations API');
224
-
225
- const serviceApi = readText('services/service-recommendations-effect/shared/effect/api.ts');
226
- assert(serviceApi.includes('HttpApi.make'), 'Effect shared API placeholder must define HttpApi');
536
+ assert(
537
+ serviceEntry.includes('defineEffectBff'),
538
+ 'Effect service must expose defineEffectBff placeholder',
539
+ );
540
+ assert(
541
+ serviceEntry.includes('recommendationsEffectApi'),
542
+ 'Effect service must use shared recommendations API',
543
+ );
544
+ assert(
545
+ readText('services/service-recommendations-effect/shared/effect/api.ts').includes('HttpApi.make'),
546
+ 'Effect shared API placeholder must define HttpApi',
547
+ );
227
548
 
228
549
  const topology = readJson('topology/reference-topology.json');
229
550
  assert(topology.preset === 'presetUltramodern', 'Topology must reference presetUltramodern');
@@ -232,27 +553,26 @@ assert(topology.shell?.remoteRefs?.length === 3, 'Topology shell must reference
232
553
  assert(topology.remotes?.length === 3, 'Topology must contain three remotes');
233
554
  assert(
234
555
  topology.remotes.some(
235
- remote =>
236
- remote.id === 'remote-design-system' &&
237
- remote.kind === 'horizontal-design-system',
556
+ (remote) => remote.id === 'remote-design-system' && remote.kind === 'horizontal-design-system',
238
557
  ),
239
558
  'Topology must contain the horizontal design-system remote',
240
559
  );
241
- assert(topology.effectServices?.[0]?.runtime === 'effect', 'Topology must contain an Effect service');
560
+ assert(
561
+ topology.effectServices?.[0]?.runtime === 'effect',
562
+ 'Topology must contain an Effect service',
563
+ );
242
564
  assert(topology.sharedPackages?.length === 3, 'Topology must contain shared package placeholders');
243
565
 
244
566
  const ownership = readJson('topology/ownership.json');
245
567
  assert(
246
568
  ownership.owners?.some(
247
- owner =>
248
- owner.id === 'remote-commerce' &&
249
- owner.ownership?.team === 'commerce-experience',
569
+ (owner) => owner.id === 'remote-commerce' && owner.ownership?.team === 'commerce-experience',
250
570
  ),
251
571
  'Ownership metadata must retain commerce owner',
252
572
  );
253
573
  assert(
254
574
  ownership.owners?.some(
255
- owner =>
575
+ (owner) =>
256
576
  owner.id === 'service-recommendations-effect' &&
257
577
  owner.package === `@${packageScope}/service-recommendations-effect`,
258
578
  ),
@@ -268,9 +588,35 @@ assert(
268
588
  manifest.packageSource?.strategy === packageSource.strategy,
269
589
  'Template manifest must retain the generated package source strategy',
270
590
  );
591
+ assert(
592
+ manifest.agentSkills?.source?.commit === rstackAgentSkillsCommit,
593
+ 'Template manifest must retain the Rstack agent skills commit',
594
+ );
595
+ assert(
596
+ baselineAgentSkills.every((skillName) => manifest.agentSkills?.baseline?.includes(skillName)),
597
+ 'Template manifest must list every baseline agent skill',
598
+ );
599
+ assert(
600
+ manifest.agentSkills?.privateSource?.repository === 'https://github.com/TechsioCZ/skills',
601
+ 'Template manifest must retain the private TechsioCZ skill source',
602
+ );
603
+ assert(
604
+ privateAgentSkills.every((skillName) =>
605
+ manifest.agentSkills?.privateSource?.baseline?.includes(skillName),
606
+ ),
607
+ 'Template manifest must list every private agent skill allowlist entry',
608
+ );
271
609
  assert(
272
610
  manifest.validation?.expectedCommands?.includes('pnpm run ultramodern:check'),
273
611
  'Template manifest must document the validation command',
274
612
  );
613
+ assert(
614
+ manifest.validation?.postMaterializationValidation?.includes('pnpm-11-policy-enforced'),
615
+ 'Template manifest must document pnpm 11 policy validation',
616
+ );
617
+ assert(
618
+ manifest.validation?.postMaterializationValidation?.includes('github-workflow-security-enforced'),
619
+ 'Template manifest must document generated workflow security validation',
620
+ );
275
621
 
276
622
  console.log('UltraModern workspace scaffold validated');