@bleedingdev/modern-js-create 3.2.0-ultramodern.57 → 3.2.0-ultramodern.59

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.
@@ -35,7 +35,7 @@ export declare const EN_LOCALE: {
35
35
  optionUltramodernPackageSource: string;
36
36
  optionUltramodernPackageScope: string;
37
37
  optionUltramodernPackageNamePrefix: string;
38
- optionMicroVertical: string;
38
+ optionVertical: string;
39
39
  optionSub: string;
40
40
  examples: string;
41
41
  example1: string;
@@ -35,7 +35,7 @@ export declare const ZH_LOCALE: {
35
35
  optionUltramodernPackageSource: string;
36
36
  optionUltramodernPackageScope: string;
37
37
  optionUltramodernPackageNamePrefix: string;
38
- optionMicroVertical: string;
38
+ optionVertical: string;
39
39
  optionSub: string;
40
40
  examples: string;
41
41
  example1: string;
@@ -1,4 +1,3 @@
1
- export type MicroVerticalKind = 'remote' | 'horizontal-remote' | 'service' | 'shared';
2
1
  type UltramodernPackageSourceStrategy = 'workspace' | 'install';
3
2
  export type UltramodernWorkspaceOptions = {
4
3
  targetDir: string;
@@ -13,16 +12,15 @@ export type UltramodernWorkspaceOptions = {
13
12
  aliasPackageNamePrefix?: string;
14
13
  };
15
14
  };
16
- export type AddUltramodernMicroVerticalOptions = {
15
+ export type AddUltramodernVerticalOptions = {
17
16
  workspaceRoot: string;
18
17
  name: string;
19
- kind: MicroVerticalKind;
20
18
  modernVersion: string;
21
19
  enableTailwind?: boolean;
22
20
  packageSource?: UltramodernWorkspaceOptions['packageSource'];
23
21
  };
24
22
  export declare const ULTRAMODERN_WORKSPACE_FLAG = "--ultramodern-workspace";
25
- export declare function addUltramodernMicroVertical(options: AddUltramodernMicroVerticalOptions): void;
23
+ export declare function addUltramodernVertical(options: AddUltramodernVerticalOptions): void;
26
24
  export declare function generateUltramodernWorkspace(options: UltramodernWorkspaceOptions): void;
27
25
  export declare const ultramodernWorkspaceVersions: {
28
26
  tanstackRouter: string;
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.57",
24
+ "version": "3.2.0-ultramodern.59",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "@types/node": "^25.9.1",
42
42
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
43
43
  "tsx": "^4.22.3",
44
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.57"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.58"
45
45
  },
46
46
  "publishConfig": {
47
47
  "registry": "https://registry.npmjs.org/",
@@ -54,6 +54,6 @@
54
54
  "start": "node ./dist/index.js"
55
55
  },
56
56
  "ultramodern": {
57
- "frameworkVersion": "3.2.0-ultramodern.57"
57
+ "frameworkVersion": "3.2.0-ultramodern.58"
58
58
  }
59
59
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "Stop": [
3
+ {
4
+ "command": "pnpm format && pnpm lint:fix && pnpm ultramodern:check",
5
+ "timeout": 600000,
6
+ "statusMessage": "Running UltraModern quality gates"
7
+ }
8
+ ],
9
+ "SubagentStop": [
10
+ {
11
+ "command": "pnpm format && pnpm lint:fix && pnpm ultramodern:check",
12
+ "timeout": 600000,
13
+ "statusMessage": "Running UltraModern quality gates"
14
+ }
15
+ ]
16
+ }
@@ -8,7 +8,9 @@ This project is generated for Codex-first UltraModern.js work.
8
8
  - `pnpm format` runs oxfmt.
9
9
  - `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
10
10
  - `pnpm i18n:check` rejects hardcoded user-visible JSX text.
11
- - `mise exec -- pnpm ultramodern:check` verifies the generated contract.
11
+ - `pnpm ultramodern:check` verifies the generated contract.
12
+ - Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm ultramodern:check`.
13
+ - `postinstall` installs `lefthook` when the app is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format`, `pnpm lint:fix`, and `pnpm ultramodern:check` on pre-commit; pre-push runs `pnpm ultramodern:check`.
12
14
 
13
15
  ## Internationalization
14
16
 
@@ -18,10 +20,4 @@ Routes are locale-prefixed by default through `localePathRedirect: true`. Keep l
18
20
 
19
21
  ## Private Skills
20
22
 
21
- Private orchestration skills are not vendored into this template. If you are authorized for `TechsioCZ/skills`, run:
22
-
23
- ```bash
24
- pnpm skills:install
25
- ```
26
-
27
- The installer clones that private repository and copies only the allowlisted skills from `.agents/skills-lock.json`.
23
+ Private orchestration skills are installed automatically during `pnpm install` when the current developer is authorized for `TechsioCZ/skills`. The installer clones that private repository and copies only the allowlisted skills from `.agents/skills-lock.json`; unauthorized developers get a warning and can continue with the public contract.
@@ -1,14 +1,14 @@
1
1
  # UltraModern.js 3.0 Starter
2
2
 
3
3
  This generated workspace is the Tractor reference SuperApp. It is built around
4
- one shell and three full-stack Micro Verticals:
4
+ one shell and three full-stack Verticals:
5
5
 
6
6
  | Package | Boundary |
7
7
  | --- | --- |
8
8
  | `apps/shell-super-app` | Module Federation host, route assembly, topology selection, shell fallback policy |
9
- | `apps/remotes/remote-explore` | Explore routes, MF exposes, locale JSON, CSS layer, and `/explore-api/effect/explore/*` |
10
- | `apps/remotes/remote-decide` | Decide routes, MF exposes, locale JSON, CSS layer, and `/decide-api/effect/decide/*` |
11
- | `apps/remotes/remote-checkout` | Checkout routes, MF exposes, locale JSON, CSS layer, and `/checkout-api/effect/checkout/*` |
9
+ | `verticals/explore` | Explore routes, MF exposes, locale JSON, CSS layer, and `/explore-api/effect/explore/*` |
10
+ | `verticals/decide` | Decide routes, MF exposes, locale JSON, CSS layer, and `/decide-api/effect/decide/*` |
11
+ | `verticals/checkout` | Checkout routes, MF exposes, locale JSON, CSS layer, and `/checkout-api/effect/checkout/*` |
12
12
 
13
13
  Each vertical is a one-package ownership boundary. Its UI, Effect BFF contract,
14
14
  generated client, `localisedUrls`, dynamic locale JSON, CSS, MF manifest, and
@@ -20,7 +20,7 @@ Install the dependencies:
20
20
 
21
21
  ```bash
22
22
  mise install
23
- mise exec -- pnpm install
23
+ pnpm install
24
24
  ```
25
25
 
26
26
  ## Get Started
@@ -28,27 +28,27 @@ mise exec -- pnpm install
28
28
  Start the dev server:
29
29
 
30
30
  ```bash
31
- mise exec -- pnpm dev
31
+ pnpm dev
32
32
  ```
33
33
 
34
34
  Build the app for production:
35
35
 
36
36
  ```bash
37
- mise exec -- pnpm build
37
+ pnpm build
38
38
  ```
39
39
 
40
40
  Validate the generated Ultramodern preset contract locally:
41
41
 
42
42
  ```bash
43
- mise exec -- pnpm run ultramodern:check
43
+ pnpm run ultramodern:check
44
44
  ```
45
45
 
46
46
  Run the normal local gates before treating the scaffold as adoption-ready:
47
47
 
48
48
  ```bash
49
- mise exec -- pnpm run check
50
- mise exec -- pnpm run build
51
- mise exec -- pnpm run cloudflare:build
49
+ pnpm run check
50
+ pnpm run build
51
+ pnpm run cloudflare:build
52
52
  ```
53
53
 
54
54
  The generated preset defaults are opt-out. Disable specific contracts via env vars:
@@ -61,31 +61,29 @@ MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
61
61
 
62
62
  The generated starter also includes `.github/workflows/ultramodern-gates.yml`
63
63
  and `.github/renovate.json`. The workflow runs
64
- `mise exec -- pnpm run ultramodern:check` and `mise exec -- pnpm run build` on
64
+ `pnpm run ultramodern:check` and `pnpm run build` on
65
65
  every push and pull request with read-only permissions, commit-pinned actions,
66
66
  frozen installs, and StepSecurity audit-mode runner hardening. Renovate is
67
67
  configured for dependency dashboard review, one-day release age, grouped
68
68
  updates, action digest pinning, and manual approval for major upgrades.
69
69
 
70
- ## Micro Vertical Workspaces
70
+ ## Vertical Workspaces
71
71
 
72
- Inside a Micro Vertical workspace, add packages from the workspace root with
73
- the UltraModern add flow. It derives paths, package names, ports, Module
74
- Federation names, topology entries, overlays, ownership, and root dev scripts:
72
+ Inside an UltraModern workspace, add a full-stack vertical from the workspace
73
+ root. The add flow derives paths, package names, ports, Module Federation names,
74
+ topology entries, overlays, ownership, Effect BFF contracts, and root dev
75
+ scripts:
75
76
 
76
77
  ```bash
77
- mise exec -- pnpm dlx @modern-js/create catalog --microvertical remote
78
- mise exec -- pnpm dlx @modern-js/create design-system --microvertical horizontal-remote
79
- mise exec -- pnpm dlx @modern-js/create catalog-api --microvertical service
80
- mise exec -- pnpm dlx @modern-js/create catalog-contracts --microvertical shared
78
+ pnpm dlx @modern-js/create catalog --vertical
81
79
  ```
82
80
 
83
81
  The canonical topology is documented in
84
82
  `docs/super-app-rfc-adr/WORKSPACE-0001-micro-vertical-workspace-scaffolding.md`.
85
- Shell packages own route assembly and topology selection, remote packages own
86
- route subtrees and degraded UI, service packages own Effect or explicit Hono
87
- contracts, and shared packages are limited to tokens, primitives, generated
88
- clients, or domain-neutral utilities.
83
+ Shell packages own route assembly and topology selection. Verticals own route
84
+ subtrees, degraded UI, Effect BFF contracts, generated clients, and deployment
85
+ evidence. Ordinary workspace packages are limited to tokens, primitives,
86
+ generated clients, or domain-neutral utilities.
89
87
 
90
88
  Use a new vertical only when the feature needs its own route subtree owner,
91
89
  independent rollout, rollback, Cloudflare/Zephyr evidence, or incident routing.
@@ -93,23 +91,22 @@ Keep code inside an existing vertical when it shares the same product owner,
93
91
  fallback behavior, release train, and Effect contract. Do not use a shared
94
92
  package for feature composites or workflow state.
95
93
 
96
- If the design system needs independent deployment, keep it as a horizontal
97
- Module Federation remote with the same topology, trust, SSR compatibility, and
98
- fallback rules as the vertical remotes. Do not add a second preset or a
99
- design-system-specific framework mode.
94
+ Do not add separate service or shared "vertical types". A vertical is the
95
+ full-stack ownership unit. Supporting packages are implementation details and
96
+ should stay ordinary workspace packages unless a real vertical owns the product
97
+ behavior.
100
98
 
101
99
  The public opinionated entrypoint is `presetUltramodern(...)`. It is the default
102
- UltraModern.js SuperApp surface for Effect, TanStack, SSR, BFF, and Micro
103
- Verticals.
100
+ UltraModern.js SuperApp surface for Effect, TanStack, SSR, BFF, and Verticals.
104
101
 
105
102
  ## Module Federation And Effect Ownership
106
103
 
107
104
  The shell consumes vertical UI through MF manifests and consumes vertical data
108
- through generated Effect clients exported by the remote packages:
105
+ through generated Effect clients exported by the vertical packages:
109
106
 
110
107
  - Explore exposes header, footer, recommendations, store picker, and route UI.
111
108
  - Decide exposes product page and route UI, and may consume Explore or Checkout
112
- remotes through declared `remoteRefs`.
109
+ verticals through declared `verticalRefs`.
113
110
  - Checkout exposes cart, checkout, thanks, mini-cart, add-to-cart, and route UI.
114
111
 
115
112
  The shell owns orchestration and fallback policy. A vertical owns its route-local
@@ -123,14 +120,14 @@ Route localization is owned by the package that owns the route. Each app emits
123
120
  `src/routes/ultramodern-route-metadata` and passes
124
121
  `ultramodernLocalisedUrls` to `@modern-js/plugin-i18n`. Dynamic JSON resources
125
122
  are served from `/locales/{{lng}}/{{ns}}.json`; shell rewrites must not replace
126
- remote-owned localized URL maps.
123
+ vertical-owned localized URL maps.
127
124
 
128
125
  CSS federation is recorded in `.modernjs/ultramodern-generated-contract.json`:
129
126
 
130
127
  - `packages/shared-design-tokens` owns `ultramodern-shared-tokens` and exports
131
128
  `./tokens.css`.
132
129
  - The shell owns shell base and overlay CSS under its app root marker.
133
- - Each remote owns one vertical CSS layer and one remote app root marker.
130
+ - Each vertical owns one vertical CSS layer and one vertical app root marker.
134
131
  - Tailwind CSS v4 is app-local through `@tailwindcss/postcss`.
135
132
  - Duplicate base styles are forbidden. SSR first paint requires shared tokens
136
133
  and Modern/Rspack-emitted app CSS.
@@ -140,28 +137,28 @@ CSS federation is recorded in `.modernjs/ultramodern-generated-contract.json`:
140
137
  Build all Cloudflare Worker artifacts:
141
138
 
142
139
  ```bash
143
- MODERN_PUBLIC_SITE_URL=https://shell-super-app.example.test mise exec -- pnpm run cloudflare:build
140
+ MODERN_PUBLIC_SITE_URL=https://shell-super-app.example.test pnpm run cloudflare:build
144
141
  ```
145
142
 
146
143
  Validate local or deployed Cloudflare output with the repo harness:
147
144
 
148
145
  ```bash
149
146
  node scripts/ultramodern-cloudflare-ssr-validation/validate-cloudflare-ssr.js \
150
- --root-dir apps/remotes/remote-checkout \
147
+ --root-dir verticals/checkout \
151
148
  --bff /checkout-api/effect/checkout/readiness \
152
- --expect-en "Checkout Remote" \
149
+ --expect-en "Checkout Vertical" \
153
150
  --match-build-marker \
154
- --out .codex/reports/cloudflare-ssr/remote-checkout-local.json
151
+ --out .codex/reports/cloudflare-ssr/checkout-local.json
155
152
  ```
156
153
 
157
154
  After deploying public Workers, run the generated public URL proof:
158
155
 
159
156
  ```bash
160
157
  ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP=https://shell-super-app.example.workers.dev \
161
- ULTRAMODERN_PUBLIC_URL_REMOTE_EXPLORE=https://remote-explore.example.workers.dev \
162
- ULTRAMODERN_PUBLIC_URL_REMOTE_DECIDE=https://remote-decide.example.workers.dev \
163
- ULTRAMODERN_PUBLIC_URL_REMOTE_CHECKOUT=https://remote-checkout.example.workers.dev \
164
- mise exec -- pnpm run cloudflare:proof -- --require-public-urls
158
+ ULTRAMODERN_PUBLIC_URL_EXPLORE=https://explore.example.workers.dev \
159
+ ULTRAMODERN_PUBLIC_URL_DECIDE=https://decide.example.workers.dev \
160
+ ULTRAMODERN_PUBLIC_URL_CHECKOUT=https://checkout.example.workers.dev \
161
+ pnpm run cloudflare:proof -- --require-public-urls
165
162
  ```
166
163
 
167
164
  Zephyr upload and live switching proof are opt-in evidence workflows. The
@@ -172,7 +169,7 @@ credentials. Without those values, only dry-run Zephyr evidence can be claimed.
172
169
  Preview the production build locally:
173
170
 
174
171
  ```bash
175
- mise exec -- pnpm serve
172
+ pnpm serve
176
173
  ```
177
174
 
178
175
  For more information, see the
@@ -0,0 +1,15 @@
1
+ pre-commit:
2
+ commands:
3
+ format:
4
+ run: pnpm format
5
+ stage_fixed: true
6
+ lint:
7
+ run: pnpm lint:fix
8
+ stage_fixed: true
9
+ check:
10
+ run: pnpm ultramodern:check
11
+
12
+ pre-push:
13
+ commands:
14
+ check:
15
+ run: pnpm ultramodern:check
@@ -15,13 +15,13 @@
15
15
  {{#unless isSubproject}}
16
16
  "skills:install": "node ./scripts/bootstrap-agent-skills.mjs",
17
17
  "skills:check": "node ./scripts/bootstrap-agent-skills.mjs --check",
18
+ "postinstall": "node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)",
18
19
  {{/unless}}
19
20
  "ultramodern:check": "{{#unless isSubproject}}pnpm format:check && pnpm lint && {{/unless}}pnpm typecheck && pnpm i18n:check && pnpm test{{#unless isSubproject}} && pnpm skills:check{{/unless}} && node ./scripts/validate-ultramodern.mjs"{{#unless isSubproject}},
20
21
  "format": "oxfmt .",
21
22
  "format:check": "oxfmt --check .",
22
23
  "lint": "oxlint .",
23
- "lint:fix": "oxlint . --fix",
24
- "prepare": "simple-git-hooks"{{/unless}}
24
+ "lint:fix": "oxlint . --fix"{{/unless}}
25
25
  },
26
26
  "dependencies": {
27
27
  "@modern-js/plugin-i18n": "{{pluginI18nVersion}}",
@@ -51,26 +51,16 @@
51
51
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
52
52
  "happy-dom": "^20.9.0",
53
53
  {{#unless isSubproject}}
54
- "lint-staged": "~17.0.5",
54
+ "lefthook": "^2.1.9",
55
55
  "oxfmt": "0.51.0",
56
56
  "oxlint": "1.66.0",
57
57
  {{/unless}}{{#if enableTailwind}} "postcss": "^8.5.6",
58
- {{/if}} "rimraf": "^6.1.3"{{#unless isSubproject}},
59
- "simple-git-hooks": "^2.13.1"{{/unless}}{{#if enableTailwind}},
58
+ {{/if}} "rimraf": "^6.1.3"{{#if enableTailwind}},
60
59
  "tailwindcss": "^{{tailwindVersion}}"{{/if}}{{#unless isSubproject}},
61
60
  "ultracite": "7.7.0"{{/unless}}
62
- }{{#unless isSubproject}},
63
- "simple-git-hooks": {
64
- "pre-commit": "npx lint-staged"
65
61
  },
66
- "lint-staged": {
67
- "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
68
- "oxfmt --write",
69
- "oxlint --fix"
70
- ]
71
- }{{/unless}},
72
62
  "engines": {
73
63
  "node": ">=20",
74
- "pnpm": ">={{pnpmVersion}} <11.5.0"
64
+ "pnpm": ">={{pnpmVersion}} <11.6.0"
75
65
  }
76
66
  }
@@ -13,7 +13,7 @@ allowBuilds:
13
13
  '@swc/core': true
14
14
  core-js: true
15
15
  esbuild: true
16
+ lefthook: true
16
17
  msgpackr-extract: true
17
18
  sharp: true
18
- simple-git-hooks: true
19
19
  workerd: true
@@ -55,32 +55,64 @@ if (!fs.existsSync(lockPath)) {
55
55
 
56
56
  const lock = readJson(lockPath);
57
57
  const installDir = path.join(root, lock.installDir ?? '.agents/skills');
58
- const privateSources = (lock.sources ?? []).filter(
58
+ const sources = lock.sources ?? [];
59
+ const requiredCloneSources = sources.filter((source) => source.install === 'clone');
60
+ const optionalCloneSources = sources.filter(
59
61
  (source) => source.install === 'clone-if-authorized',
60
62
  );
63
+ const requiredSkills = [
64
+ ...(lock.baseline ?? []),
65
+ ...requiredCloneSources.flatMap((source) => source.baseline ?? []),
66
+ ].filter(
67
+ (skill, index, skills) =>
68
+ skills.findIndex((candidate) => candidate.name === skill.name) === index,
69
+ );
61
70
 
62
71
  if (checkOnly) {
63
- const missing = privateSources.flatMap((source) =>
72
+ const missingRequired = requiredSkills
73
+ .map((skill) => skill.name)
74
+ .filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')));
75
+ const missingOptional = optionalCloneSources.flatMap((source) =>
64
76
  (source.baseline ?? [])
65
77
  .map((skill) => skill.name)
66
78
  .filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
67
79
  );
68
- if (missing.length > 0) {
80
+
81
+ if (missingRequired.length > 0) {
82
+ console.error(
83
+ `Required agent skills not installed: ${missingRequired.join(', ')}. Run pnpm skills:install.`,
84
+ );
85
+ process.exit(1);
86
+ }
87
+
88
+ if (missingOptional.length > 0) {
69
89
  console.warn(
70
- `Private skills not installed: ${missing.join(', ')}. Run pnpm skills:install if you have access.`,
90
+ `Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
71
91
  );
72
92
  } else {
73
- console.log('Agent skills are installed.');
93
+ console.log('Required and private agent skills are installed.');
94
+ process.exit(0);
74
95
  }
96
+ console.log('Required agent skills are installed.');
75
97
  process.exit(0);
76
98
  }
77
99
 
78
100
  fs.mkdirSync(installDir, { recursive: true });
79
101
 
80
- for (const source of privateSources) {
102
+ for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
81
103
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
82
104
  try {
83
- cloneSource(source, tempDir);
105
+ try {
106
+ cloneSource(source, tempDir);
107
+ } catch (error) {
108
+ if (source.install === 'clone-if-authorized') {
109
+ console.warn(
110
+ `Skipping ${source.repository}; current developer may not have access.`,
111
+ );
112
+ continue;
113
+ }
114
+ throw error;
115
+ }
84
116
  for (const skill of source.baseline ?? []) {
85
117
  const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
86
118
  if (!sourceSkillDir) {
@@ -34,7 +34,7 @@ const activePnpmVersion = execFileSync('pnpm', ['--version'], {
34
34
 
35
35
  if (activePnpmVersion !== expectedPnpmVersion) {
36
36
  console.error(
37
- `Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun through mise exec -- pnpm ...`,
37
+ `Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun pnpm from the activated shell`,
38
38
  );
39
39
  process.exit(1);
40
40
  }
@@ -81,7 +81,7 @@ const requiredDeniedPaths = [
81
81
  ];
82
82
  const requiredPostMaterialization = [
83
83
  'ultramodern-contract-check',
84
- 'dependency-install-with-lifecycle-deny',
84
+ 'agent-skill-postinstall-allowed',
85
85
  'github-workflow-security-enforced',
86
86
  'package-source-retained',
87
87
  'pnpm-11-policy-enforced',
@@ -92,8 +92,10 @@ const requiredPaths = [
92
92
  {{#unless isSubproject}}
93
93
  'AGENTS.md',
94
94
  '.agents/skills-lock.json',
95
+ '.codex/hooks.json',
95
96
  '.github/renovate.json',
96
97
  '.github/workflows/ultramodern-gates.yml',
98
+ 'lefthook.yml',
97
99
  'oxlint.config.ts',
98
100
  'oxfmt.config.ts',
99
101
  'scripts/bootstrap-agent-skills.mjs',
@@ -152,9 +154,9 @@ for (const requiredSnippet of [
152
154
  'pull_request:',
153
155
  'persist-credentials: false',
154
156
  'jdx/mise-action',
155
- 'mise exec -- pnpm install --frozen-lockfile',
156
- 'mise exec -- pnpm run ultramodern:check',
157
- 'mise exec -- pnpm run build',
157
+ 'pnpm install --frozen-lockfile',
158
+ 'pnpm run ultramodern:check',
159
+ 'pnpm run build',
158
160
  'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
159
161
  'timeout-minutes:',
160
162
  'egress-policy: audit',
@@ -218,6 +220,12 @@ for (const deniedPath of requiredDeniedPaths) {
218
220
  if (templateManifest.lifecyclePolicy?.denyByDefault !== true) {
219
221
  manifestErrors.push('lifecyclePolicy.denyByDefault');
220
222
  }
223
+ if (
224
+ JSON.stringify(templateManifest.lifecyclePolicy?.allowedScripts) !==
225
+ JSON.stringify(['postinstall'])
226
+ ) {
227
+ manifestErrors.push('lifecyclePolicy.allowedScripts');
228
+ }
221
229
 
222
230
  for (const token of requiredPostMaterialization) {
223
231
  if (!templateManifest.validation?.postMaterializationValidation?.includes(token)) {
@@ -260,6 +268,7 @@ const requiredScripts = {
260
268
  'lint:fix': 'oxlint . --fix',
261
269
  'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
262
270
  'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
271
+ postinstall: 'node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)',
263
272
  {{/unless}}
264
273
  };
265
274
 
@@ -299,8 +308,8 @@ if (packageJson.packageManager !== `pnpm@${expectedPnpmVersion}`) {
299
308
  process.exit(1);
300
309
  }
301
310
 
302
- if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.5.0`) {
303
- console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.5.0`);
311
+ if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.6.0`) {
312
+ console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.6.0`);
304
313
  process.exit(1);
305
314
  }
306
315
 
@@ -348,9 +357,9 @@ if (
348
357
  '@swc/core': true,
349
358
  'core-js': true,
350
359
  esbuild: true,
360
+ lefthook: true,
351
361
  'msgpackr-extract': true,
352
362
  sharp: true,
353
- 'simple-git-hooks': true,
354
363
  workerd: true,
355
364
  })
356
365
  ) {
@@ -467,6 +476,7 @@ for (const dependency of [
467
476
  'tailwindcss',
468
477
  {{/if}}
469
478
  {{#unless isSubproject}}
479
+ 'lefthook',
470
480
  'oxlint',
471
481
  'oxfmt',
472
482
  'ultracite',
@@ -127,7 +127,7 @@ const Index = () => {
127
127
  <code className="code">modern.config.ts</code>
128
128
  {/* i18n-ignore technical token */}
129
129
  {t('home.description.afterConfig')}
130
- <code className="code">mise exec -- pnpm run ultramodern:check</code>
130
+ <code className="code">pnpm run ultramodern:check</code>
131
131
  {/* i18n-ignore technical token */}
132
132
  {t('home.description.end')}
133
133
  </p>
@@ -0,0 +1,16 @@
1
+ {
2
+ "Stop": [
3
+ {
4
+ "command": "pnpm format && pnpm lint:fix && pnpm check",
5
+ "timeout": 600000,
6
+ "statusMessage": "Running UltraModern quality gates"
7
+ }
8
+ ],
9
+ "SubagentStop": [
10
+ {
11
+ "command": "pnpm format && pnpm lint:fix && pnpm check",
12
+ "timeout": 600000,
13
+ "statusMessage": "Running UltraModern quality gates"
14
+ }
15
+ ]
16
+ }
@@ -8,6 +8,8 @@ This workspace is generated as an agent-ready UltraModern.js SuperApp. Agents sh
8
8
  - `pnpm format` runs oxfmt.
9
9
  - `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
10
10
  - `pnpm check` runs formatting, linting, effect-tsgo, private-skill availability checks, and the generated workspace contract.
11
+ - Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm check`.
12
+ - `postinstall` installs `lefthook` when the workspace is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format`, `pnpm lint:fix`, and `pnpm check` on pre-commit; pre-push runs `pnpm check`.
11
13
 
12
14
  ## Localized Routes
13
15
 
@@ -26,7 +28,7 @@ Use these skills when the task touches the matching subsystem:
26
28
  - `rstest-best-practices`: Rstest configuration, test writing, mocking, snapshots, coverage, and CI test behavior.
27
29
  - `mf`: Module Federation docs, Modern.js integration, DTS/type checks, shared dependency checks, runtime errors, and observability troubleshooting.
28
30
 
29
- The public `module-federation/agent-skills` repository is installed during `mise exec -- pnpm install` and `mise exec -- pnpm skills:install`. `mise exec -- pnpm skills:check` fails when the required public `mf` skill is missing.
31
+ The public `module-federation/agent-skills` repository is installed during `pnpm install` and `pnpm skills:install`. `pnpm skills:check` fails when the required public `mf` skill is missing.
30
32
 
31
33
  ## Private Skills
32
34
 
@@ -40,7 +42,7 @@ The installer copies only the pinned private skills from `.agents/skills-lock.js
40
42
 
41
43
  ## Agent Reference Repositories
42
44
 
43
- The workspace installs read-only source references under `repos/` by default during `mise exec -- pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
45
+ The workspace installs read-only source references under `repos/` by default during `pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
44
46
 
45
47
  - `repos/effect` from `Effect-TS/effect`.
46
48
  - `repos/ultramodern.js` from `BleedingDev/ultramodern.js`.
@@ -6,34 +6,33 @@ This workspace keeps `presetUltramodern(...)` as the single public
6
6
  UltraModern.js 3.0 SuperApp surface and scaffolds the canonical Micro Vertical
7
7
  starter topology:
8
8
 
9
- - `apps/shell-super-app` owns shell route assembly and remote manifest wiring.
10
- - `apps/remotes/remote-commerce` owns the commerce vertical remote, its
9
+ - `apps/shell-super-app` owns shell route assembly and federated manifest wiring.
10
+ - `verticals/commerce` owns the commerce vertical, its
11
11
  browser-safe Module Federation exposes, and its Effect BFF/API surface.
12
- - `apps/remotes/remote-identity` owns the identity vertical remote, its
12
+ - `verticals/identity` owns the identity vertical, its
13
13
  browser-safe Module Federation exposes, and its Effect BFF/API surface.
14
- - `apps/remotes/remote-design-system` owns the horizontal design-system remote.
14
+ - `verticals/design-system` owns the design-system vertical.
15
15
  - `packages/shared-*` provide placeholders for cross-workspace contracts.
16
16
 
17
- Default vertical APIs are owned by the vertical package, not by `services/*`.
17
+ Default vertical APIs are owned by the vertical package.
18
18
  Each full-stack vertical keeps its `api/effect/index.ts` handlers next to its
19
19
  `shared/effect/api.ts` contract and `src/effect/*-client.ts` typed client
20
20
  surface. The Module Federation config exposes only browser-safe Route and
21
21
  Widget modules; server handlers and Effect client/contract modules stay out of
22
- browser remotes. `services/*` is reserved for explicit external service
23
- packages.
22
+ browser verticals.
24
23
 
25
24
  Run the scaffold validator before adding business code:
26
25
 
27
26
  ```bash
28
27
  mise install
29
- mise exec -- pnpm ultramodern:check
28
+ pnpm ultramodern:check
30
29
  ```
31
30
 
32
- By default, `mise exec -- pnpm install` also prepares read-only agent reference repositories
31
+ By default, `pnpm install` also prepares read-only agent reference repositories
33
32
  under `repos/` for Effect and UltraModern.js source lookup using squashed git
34
33
  subtrees. Disable this setup with
35
- `ULTRAMODERN_SKIP_AGENT_REPOS=1 mise exec -- pnpm install`, or rerun it
36
- explicitly with `mise exec -- pnpm agents:refs:install`.
34
+ `ULTRAMODERN_SKIP_AGENT_REPOS=1 pnpm install`, or rerun it
35
+ explicitly with `pnpm agents:refs:install`.
37
36
 
38
37
  The topology and ownership metadata are generated under `topology/`. The
39
38
  workspace also ships `.github/workflows/ultramodern-workspace-gates.yml` and