@ultimat3/cli 29.4.0 → 30.1.0

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 (448) hide show
  1. package/CLAUDE.md +64 -40
  2. package/README.md +61 -11
  3. package/package.json +42 -28
  4. package/src/affected-tests.ts +97 -0
  5. package/src/affected-unowned.ts +43 -0
  6. package/src/affected.ts +25 -6
  7. package/src/app-boundaries.ts +29 -1
  8. package/src/app-evals.ts +12 -4
  9. package/src/app-load-unresolved.ts +62 -0
  10. package/src/app-load.ts +30 -37
  11. package/src/app-mcp.ts +14 -2
  12. package/src/app-module-graph.ts +232 -0
  13. package/src/app-reload-graph.ts +2 -2
  14. package/src/app-root.ts +37 -1
  15. package/src/bin.ts +1 -0
  16. package/src/boundary-findings.ts +6 -0
  17. package/src/budgets-freshness.ts +32 -0
  18. package/src/budgets.ts +20 -1
  19. package/src/build-capture-env.ts +9 -0
  20. package/src/cdp-shot-a11y.ts +2 -39
  21. package/src/ci-tested.ts +243 -0
  22. package/src/client-emit-customer-runtime.ts +139 -0
  23. package/src/client-emit-customer.ts +108 -0
  24. package/src/client-emit-drift.ts +72 -0
  25. package/src/client-emit-header.ts +67 -0
  26. package/src/client-emit-schemas.ts +175 -0
  27. package/src/client-emit-types.ts +121 -0
  28. package/src/client-emit-vendor.ts +146 -0
  29. package/src/client-errors-routes.ts +229 -0
  30. package/src/client-errors-script.ts +74 -0
  31. package/src/client-vitals-route.ts +197 -0
  32. package/src/client-vitals-script.ts +56 -0
  33. package/src/cmd-affected.ts +23 -0
  34. package/src/cmd-build-spec.ts +7 -1
  35. package/src/cmd-build.ts +35 -9
  36. package/src/cmd-ci-spec.ts +42 -6
  37. package/src/cmd-ci.ts +99 -2
  38. package/src/cmd-client-customer.ts +134 -0
  39. package/src/cmd-client-source.ts +81 -0
  40. package/src/cmd-client-spec.ts +48 -0
  41. package/src/cmd-client-write.ts +41 -0
  42. package/src/cmd-client.ts +111 -0
  43. package/src/cmd-db-recount.ts +120 -0
  44. package/src/cmd-db-spec.ts +36 -6
  45. package/src/cmd-db.ts +11 -3
  46. package/src/cmd-deploy-compose.ts +61 -3
  47. package/src/cmd-deploy-drain.ts +10 -2
  48. package/src/cmd-deploy-helm.ts +11 -0
  49. package/src/cmd-deploy-preflight.ts +118 -0
  50. package/src/cmd-deploy-spec.ts +58 -6
  51. package/src/cmd-deploy-verify.ts +380 -0
  52. package/src/cmd-deploy.ts +42 -9
  53. package/src/cmd-docs.ts +34 -4
  54. package/src/cmd-doctor.ts +66 -9
  55. package/src/cmd-drill-spec.ts +25 -0
  56. package/src/cmd-drill.ts +176 -0
  57. package/src/cmd-env-init.ts +102 -0
  58. package/src/cmd-env-spec.ts +10 -3
  59. package/src/cmd-env.ts +15 -10
  60. package/src/cmd-errors-recent.ts +60 -0
  61. package/src/cmd-errors-spec.ts +9 -1
  62. package/src/cmd-errors.ts +4 -0
  63. package/src/cmd-exercise-spec.ts +19 -0
  64. package/src/cmd-exercise.ts +129 -0
  65. package/src/cmd-generate-spec.ts +9 -1
  66. package/src/cmd-generate.ts +32 -27
  67. package/src/cmd-graph-load.ts +97 -0
  68. package/src/cmd-graph-spec.ts +16 -0
  69. package/src/cmd-graph.ts +73 -0
  70. package/src/cmd-i18n.ts +4 -1
  71. package/src/cmd-impact-spec.ts +13 -0
  72. package/src/cmd-impact.ts +64 -0
  73. package/src/cmd-jobs-resolve.ts +76 -0
  74. package/src/cmd-jobs-spec.ts +21 -2
  75. package/src/cmd-jobs.ts +10 -6
  76. package/src/cmd-mail-spec.ts +28 -0
  77. package/src/cmd-mail.ts +70 -0
  78. package/src/cmd-mcp-lock.ts +69 -0
  79. package/src/cmd-mcp-spec.ts +23 -4
  80. package/src/cmd-mcp.ts +17 -3
  81. package/src/cmd-new-spec.ts +8 -1
  82. package/src/cmd-new.ts +61 -9
  83. package/src/cmd-planned.ts +0 -6
  84. package/src/cmd-repro-spec.ts +31 -0
  85. package/src/cmd-repro.ts +174 -0
  86. package/src/cmd-stop-spec.ts +21 -0
  87. package/src/cmd-stop.ts +166 -0
  88. package/src/cmd-test-spec.ts +17 -4
  89. package/src/cmd-test.ts +70 -14
  90. package/src/cmd-upgrade-spec.ts +26 -0
  91. package/src/cmd-upgrade.ts +274 -0
  92. package/src/cmd-verify-spec.ts +3 -3
  93. package/src/db-backfill.ts +6 -0
  94. package/src/db-diagnostics.ts +106 -0
  95. package/src/db-generate.ts +9 -0
  96. package/src/db-reset-template.ts +1 -1
  97. package/src/db-rolling.ts +45 -0
  98. package/src/db-seed-generate.ts +188 -0
  99. package/src/deploy-verify-findings.ts +98 -0
  100. package/src/deploy-verify-flags.ts +109 -0
  101. package/src/deploy-verify-grade.ts +185 -0
  102. package/src/deploy-verify-queues.ts +53 -0
  103. package/src/dev-boot.ts +35 -15
  104. package/src/dev-child-watch.ts +16 -4
  105. package/src/dev-dashboard-guard.ts +8 -1
  106. package/src/dev-dashboard.ts +7 -0
  107. package/src/dev-lock-identity.ts +61 -0
  108. package/src/dev-lock-port-holder.ts +80 -0
  109. package/src/dev-lock-steal.ts +124 -0
  110. package/src/dev-lock.ts +92 -120
  111. package/src/dev-mail-templates.ts +51 -0
  112. package/src/dev-rebuild.ts +26 -2
  113. package/src/dev-route-table.ts +17 -0
  114. package/src/dev-supervisor.ts +5 -2
  115. package/src/dev-watch-tree.ts +11 -10
  116. package/src/dev-watch.ts +1 -1
  117. package/src/dispatch.ts +29 -6
  118. package/src/docs-section.ts +69 -0
  119. package/src/docs-sources.ts +100 -0
  120. package/src/doctor-auth.ts +11 -2
  121. package/src/doctor-chart-alerts.ts +55 -0
  122. package/src/doctor-pooler.ts +107 -0
  123. package/src/doctor-rolling.ts +35 -0
  124. package/src/doctor-row-security.ts +40 -0
  125. package/src/document-options.ts +13 -0
  126. package/src/drill-sync-lane.ts +25 -0
  127. package/src/drill-targets.ts +39 -0
  128. package/src/duplicate-packages.ts +1 -1
  129. package/src/env-production.ts +93 -0
  130. package/src/error-codes-shot.ts +59 -0
  131. package/src/error-codes-tooling.ts +29 -0
  132. package/src/error-codes.ts +100 -54
  133. package/src/error-contract.ts +2 -2
  134. package/src/errors.ts +22 -3
  135. package/src/exercise-database.ts +99 -0
  136. package/src/exercise-findings.ts +159 -0
  137. package/src/exercise-overrides.ts +42 -0
  138. package/src/exercise-registration.ts +58 -0
  139. package/src/exercise-static.ts +88 -0
  140. package/src/exercise-subject.ts +69 -0
  141. package/src/failure-capture-boot.ts +30 -0
  142. package/src/framework-env.ts +7 -1
  143. package/src/framework-schema.ts +72 -5
  144. package/src/generate-catalog-locales.ts +19 -0
  145. package/src/generate-chart-alerts.ts +156 -0
  146. package/src/generate-feature-required.ts +65 -0
  147. package/src/generate-feature.ts +83 -6
  148. package/src/generate-files.ts +24 -0
  149. package/src/generate-format-written.ts +32 -0
  150. package/src/generate-kinds.ts +6 -0
  151. package/src/generate-sign-in-config.ts +100 -0
  152. package/src/generate-sign-in-manifest.ts +28 -0
  153. package/src/generate-sign-in-mode.ts +101 -0
  154. package/src/generate-sign-in.ts +250 -0
  155. package/src/generate-write.ts +44 -7
  156. package/src/hand-rolled-socket.ts +72 -0
  157. package/src/i18n-app-key-shapes.ts +29 -0
  158. package/src/i18n-audit.ts +10 -3
  159. package/src/i18n-registration.ts +18 -3
  160. package/src/index.ts +2 -0
  161. package/src/island-bundle.ts +4 -3
  162. package/src/island-disposer.ts +116 -0
  163. package/src/island-realtime-state.ts +34 -6
  164. package/src/island-realtime.ts +6 -2
  165. package/src/island-routes.ts +3 -2
  166. package/src/isr-query-advice.ts +6 -11
  167. package/src/isr-query-guard.ts +4 -3
  168. package/src/job-budget.ts +95 -0
  169. package/src/jobs-json.ts +11 -0
  170. package/src/lint-findings.ts +104 -0
  171. package/src/local-cli.ts +34 -4
  172. package/src/loopback-host.ts +2 -2
  173. package/src/mail-preview-findings.ts +24 -0
  174. package/src/mcp-errors.ts +82 -2
  175. package/src/mcp-host.ts +11 -1
  176. package/src/mcp-lock-findings.ts +57 -0
  177. package/src/measure-paths.ts +12 -4
  178. package/src/measure-scope.ts +27 -13
  179. package/src/messages-client.ts +23 -0
  180. package/src/messages-db.ts +56 -0
  181. package/src/messages-gate.ts +107 -0
  182. package/src/messages-repro.ts +11 -0
  183. package/src/messages-shot.ts +51 -0
  184. package/src/messages-upgrade.ts +45 -0
  185. package/src/messages.ts +53 -178
  186. package/src/metrics-endpoint.ts +23 -5
  187. package/src/new-shapes-generate.ts +71 -0
  188. package/src/new-shapes.ts +63 -0
  189. package/src/openapi-compile-ir.ts +68 -0
  190. package/src/openapi-compile-schema.ts +284 -0
  191. package/src/openapi-compile.ts +273 -0
  192. package/src/openapi-refuse.ts +45 -0
  193. package/src/otlp-export.ts +2 -1
  194. package/src/output-findings.ts +123 -0
  195. package/src/output.ts +32 -8
  196. package/src/package-boundaries.ts +110 -0
  197. package/src/page-pins.ts +23 -0
  198. package/src/plain-routes.ts +13 -0
  199. package/src/prerender-entries.ts +59 -0
  200. package/src/prerender-heaviest.ts +27 -0
  201. package/src/prerender-preload.ts +6 -0
  202. package/src/prerender-report.ts +12 -0
  203. package/src/prerender-seo-reads.ts +38 -0
  204. package/src/prerender-served.ts +22 -7
  205. package/src/prerender-vitals.ts +42 -0
  206. package/src/prerender.ts +61 -32
  207. package/src/pwa-artifacts.ts +4 -3
  208. package/src/pwa-manifest-locales.ts +1 -1
  209. package/src/registry.ts +18 -0
  210. package/src/repro-bundle.ts +65 -0
  211. package/src/repro-errors.ts +47 -0
  212. package/src/repro-job.ts +72 -0
  213. package/src/repro-literal.ts +41 -0
  214. package/src/repro-request.ts +48 -0
  215. package/src/repro-write.ts +100 -0
  216. package/src/retain-sweep.ts +27 -0
  217. package/src/retained-assets-boot.ts +113 -0
  218. package/src/retained-assets-layout.ts +110 -0
  219. package/src/retained-assets-read.ts +142 -0
  220. package/src/retained-assets-sweep.ts +70 -0
  221. package/src/retained-assets.ts +171 -0
  222. package/src/revalidated-response.ts +8 -5
  223. package/src/role-replicator.ts +7 -1
  224. package/src/role-start-devices.ts +119 -0
  225. package/src/role-start-types.ts +6 -0
  226. package/src/role-start.ts +22 -5
  227. package/src/role-sync.ts +7 -1
  228. package/src/route-islands.ts +42 -0
  229. package/src/route-table.ts +2 -1
  230. package/src/row-security-boot.ts +58 -0
  231. package/src/row-security-wanted.ts +32 -0
  232. package/src/runtime-admin.ts +0 -1
  233. package/src/runtime-assets-site-cache.ts +55 -0
  234. package/src/runtime-assets-site.ts +136 -0
  235. package/src/runtime-assets.ts +99 -55
  236. package/src/runtime-bindings.ts +19 -3
  237. package/src/runtime-cache.ts +23 -14
  238. package/src/runtime-isr-outcome.ts +114 -60
  239. package/src/runtime-purge.ts +14 -2
  240. package/src/runtime-queue.ts +31 -4
  241. package/src/runtime-render-document.ts +204 -0
  242. package/src/runtime-render.ts +48 -198
  243. package/src/runtime-replica-position.ts +62 -0
  244. package/src/runtime-replica.ts +18 -3
  245. package/src/runtime-s3-path-style.ts +17 -0
  246. package/src/runtime-services.ts +19 -6
  247. package/src/runtime-statement-bound.ts +22 -0
  248. package/src/runtime-storage-upload.ts +9 -0
  249. package/src/runtime-storage.ts +41 -81
  250. package/src/schema-diff-views.ts +54 -0
  251. package/src/schema-diff.ts +21 -3
  252. package/src/schema-drift.ts +28 -9
  253. package/src/schema-dump-drift.ts +91 -7
  254. package/src/script-csp.ts +1 -1
  255. package/src/seo-cards.ts +26 -0
  256. package/src/seo-routes.ts +22 -8
  257. package/src/serve-boot.ts +54 -21
  258. package/src/serve-drain.ts +1 -0
  259. package/src/serve-env.ts +26 -8
  260. package/src/serve-retire.ts +12 -6
  261. package/src/serve-web.ts +42 -4
  262. package/src/serve.ts +9 -7
  263. package/src/site-asset-routes.ts +7 -1
  264. package/src/site-config.ts +4 -0
  265. package/src/site-llms-pages.ts +106 -0
  266. package/src/site-llms.ts +64 -0
  267. package/src/site-seo-write.ts +4 -2
  268. package/src/site-seo.ts +55 -6
  269. package/src/source-mtime.ts +50 -0
  270. package/src/static-capture-errors.ts +41 -0
  271. package/src/static-capture.ts +56 -0
  272. package/src/static-cards-serve.ts +39 -0
  273. package/src/static-cards.ts +93 -0
  274. package/src/static-document.ts +14 -13
  275. package/src/static-images.ts +115 -0
  276. package/src/stream-hole-scope.ts +55 -0
  277. package/src/style-bundle.ts +1 -1
  278. package/src/style-routes.ts +3 -2
  279. package/src/sw-precache-plan.ts +2 -2
  280. package/src/sw-routes.ts +2 -2
  281. package/src/templates/action-tests.ts +306 -0
  282. package/src/templates/action.ts +81 -269
  283. package/src/templates/backfill.ts +1 -1
  284. package/src/templates/entity.ts +21 -5
  285. package/src/templates/github/ci.yml.ts +8 -9
  286. package/src/templates/guard-bare-error.ts +2 -2
  287. package/src/templates/guard-error-declared.ts +263 -0
  288. package/src/templates/guard-native-confirm.ts +132 -0
  289. package/src/templates/guard-raw-fetch.ts +151 -0
  290. package/src/templates/guard-raw-interval.ts +121 -0
  291. package/src/templates/guard-repo-raw-sql.ts +71 -2
  292. package/src/templates/job.ts +1 -1
  293. package/src/templates/naming.ts +5 -0
  294. package/src/templates/pinned/docker-compose.yml +6 -0
  295. package/src/templates/pinned-images.ts +8 -0
  296. package/src/templates/policy.ts +85 -2
  297. package/src/templates/query.ts +70 -13
  298. package/src/templates/resource-create.ts +3 -1
  299. package/src/templates/resource-form-island.ts +15 -4
  300. package/src/templates/resource-page-db.ts +83 -0
  301. package/src/templates/resource-page.ts +161 -52
  302. package/src/templates/route.ts +4 -6
  303. package/src/templates/scaffold-app.ts +3 -0
  304. package/src/templates/scaffold-auth.ts +2 -1
  305. package/src/templates/scaffold-bin.ts +5 -52
  306. package/src/templates/scaffold-claude-agents.ts +19 -19
  307. package/src/templates/scaffold-claude-commands.ts +28 -31
  308. package/src/templates/scaffold-claude.ts +26 -23
  309. package/src/templates/scaffold-container-compose.ts +6 -1
  310. package/src/templates/scaffold-container.ts +24 -4
  311. package/src/templates/scaffold-dashboard-bare.ts +3 -3
  312. package/src/templates/scaffold-dashboard-example-tests.ts +49 -9
  313. package/src/templates/scaffold-dashboard-example-view.ts +156 -0
  314. package/src/templates/scaffold-dashboard-example.ts +43 -131
  315. package/src/templates/scaffold-dashboard-shared.ts +3 -7
  316. package/src/templates/scaffold-db-client.ts +20 -6
  317. package/src/templates/scaffold-db-package.ts +5 -14
  318. package/src/templates/scaffold-docs.ts +73 -57
  319. package/src/templates/scaffold-domain-package.ts +18 -14
  320. package/src/templates/scaffold-entries.ts +7 -2
  321. package/src/templates/scaffold-exercise.ts +37 -0
  322. package/src/templates/scaffold-guards.ts +13 -1
  323. package/src/templates/scaffold-helm-alerts.ts +234 -0
  324. package/src/templates/scaffold-helm-templates.ts +7 -5
  325. package/src/templates/scaffold-helm.ts +46 -12
  326. package/src/templates/scaffold-i18n.ts +6 -2
  327. package/src/templates/scaffold-mcp-endpoint.ts +182 -0
  328. package/src/templates/scaffold-mcp-json.ts +31 -0
  329. package/src/templates/scaffold-mcp-package.ts +59 -56
  330. package/src/templates/scaffold-repo.ts +7 -2
  331. package/src/templates/scaffold-shell.ts +45 -79
  332. package/src/templates/scaffold-test-services.ts +13 -5
  333. package/src/templates/shapes/api-keys.ts +170 -0
  334. package/src/templates/shapes/api.ts +101 -0
  335. package/src/templates/shapes/blog-site.ts +340 -0
  336. package/src/templates/shapes/blog-slice.ts +265 -0
  337. package/src/templates/shapes/blog.ts +114 -0
  338. package/src/templates/shapes/dashboard-detail.ts +242 -0
  339. package/src/templates/shapes/dashboard.ts +53 -0
  340. package/src/templates/shapes/index.ts +17 -0
  341. package/src/templates/shapes/overlay.ts +99 -0
  342. package/src/templates/shapes/shared-edits.ts +46 -0
  343. package/src/templates/sign-in-action-tests.ts +237 -0
  344. package/src/templates/sign-in-authenticator.ts +112 -0
  345. package/src/templates/sign-in-oidc.ts +189 -0
  346. package/src/templates/sign-in-pages.ts +148 -0
  347. package/src/templates/sign-in-password.ts +216 -0
  348. package/src/templates/sign-in-shared.ts +268 -0
  349. package/src/templates/slice-foundation.ts +70 -15
  350. package/src/test-batches.ts +1 -1
  351. package/src/test-database.ts +1 -1
  352. package/src/test-shards.ts +7 -1
  353. package/src/test-workers.ts +18 -12
  354. package/src/tool-gate-names.ts +34 -0
  355. package/src/upgrade-agents-link.ts +49 -0
  356. package/src/upgrade-bun-floor.ts +225 -0
  357. package/src/upgrade-gate.ts +228 -0
  358. package/src/upgrade-pins.ts +63 -0
  359. package/src/upgrade-plan.ts +214 -0
  360. package/src/upgrade-registry.ts +53 -0
  361. package/src/upgrade-report.ts +139 -0
  362. package/src/upgrade-root.ts +74 -0
  363. package/src/verify-budgets-build.ts +118 -0
  364. package/src/verify-checks.ts +67 -24
  365. package/src/verify-coverage-run.ts +1 -1
  366. package/src/verify-e2e-lane.ts +41 -0
  367. package/src/verify-exercise.ts +64 -0
  368. package/src/verify-floor.ts +41 -0
  369. package/src/verify-merge.ts +19 -4
  370. package/src/verify-role-load.ts +2 -0
  371. package/src/verify-run.ts +28 -8
  372. package/src/verify-shard.ts +12 -2
  373. package/src/verify-skip-ceiling.ts +87 -0
  374. package/src/verify-step.ts +5 -0
  375. package/src/verify-tests.ts +25 -5
  376. package/src/view-drift.ts +55 -0
  377. package/src/worker-bundle.ts +3 -2
  378. package/src/workspace-graph.ts +38 -19
  379. package/src/write-generated.ts +43 -0
  380. package/src/zone-boot.ts +20 -0
  381. package/wiki/Actions.md +342 -0
  382. package/wiki/Admin-Dashboard.md +432 -0
  383. package/wiki/Agents.md +958 -0
  384. package/wiki/Auth.md +195 -0
  385. package/wiki/Bare-VM.md +111 -0
  386. package/wiki/Batching-And-Preloading.md +136 -0
  387. package/wiki/Building-Your-Own-Base.md +203 -0
  388. package/wiki/CI-Parallel-Gate.md +220 -0
  389. package/wiki/CLI-Reference.md +1894 -0
  390. package/wiki/Caching-And-Invalidation.md +255 -0
  391. package/wiki/Client-Data.md +194 -0
  392. package/wiki/Client-Navigation.md +513 -0
  393. package/wiki/Compared.md +93 -0
  394. package/wiki/Configuration.md +868 -0
  395. package/wiki/Contributing.md +12 -0
  396. package/wiki/Deploy-Recipes.md +122 -0
  397. package/wiki/Deployment.md +508 -0
  398. package/wiki/Entities-And-Migrations.md +1116 -0
  399. package/wiki/FAQ.md +175 -0
  400. package/wiki/Feature-Flags.md +65 -0
  401. package/wiki/Getting-Started.md +221 -0
  402. package/wiki/Home.md +160 -0
  403. package/wiki/I18n.md +239 -0
  404. package/wiki/Installation.md +206 -0
  405. package/wiki/Interface-Rules.md +339 -0
  406. package/wiki/Jobs-And-Workflows.md +783 -0
  407. package/wiki/Known-Gaps.md +185 -0
  408. package/wiki/MCP-And-AI.md +753 -0
  409. package/wiki/Mail.md +138 -0
  410. package/wiki/Migrating-An-Existing-App.md +594 -0
  411. package/wiki/Migrating-From-Django.md +54 -0
  412. package/wiki/Migrating-From-Laravel.md +53 -0
  413. package/wiki/Migrating-From-Node-Drizzle.md +53 -0
  414. package/wiki/Migrating-From-Rails.md +53 -0
  415. package/wiki/Migrations-And-Backfills.md +552 -0
  416. package/wiki/Money.md +338 -0
  417. package/wiki/N-Plus-One-Detection.md +197 -0
  418. package/wiki/Notify.md +208 -0
  419. package/wiki/Observability.md +245 -0
  420. package/wiki/Outbound-HTTP.md +109 -0
  421. package/wiki/PWA-And-Offline.md +343 -0
  422. package/wiki/Policies-And-Authz.md +251 -0
  423. package/wiki/Project-Layout.md +150 -0
  424. package/wiki/Queries-And-Live-Queries.md +429 -0
  425. package/wiki/Realtime.md +734 -0
  426. package/wiki/Resource-Management.md +121 -0
  427. package/wiki/Routes-And-Render-Modes.md +521 -0
  428. package/wiki/SEO.md +288 -0
  429. package/wiki/Scheduled-Tasks.md +175 -0
  430. package/wiki/Scraping.md +493 -0
  431. package/wiki/Shapes.md +63 -0
  432. package/wiki/Stability.md +97 -0
  433. package/wiki/Static-Assets.md +166 -0
  434. package/wiki/Storage-And-Uploads.md +404 -0
  435. package/wiki/Testing.md +1314 -0
  436. package/wiki/The-Eight-Primitives.md +247 -0
  437. package/wiki/Theming.md +362 -0
  438. package/wiki/Timezones-And-Dates.md +194 -0
  439. package/wiki/Troubleshooting.md +163 -0
  440. package/wiki/Tutorial-01-First-App.md +289 -0
  441. package/wiki/Tutorial-02-First-Feature.md +339 -0
  442. package/wiki/Tutorial-03-Auth-And-Admin.md +379 -0
  443. package/wiki/Tutorial-04-Jobs-And-Realtime.md +263 -0
  444. package/wiki/Tutorial-05-Deploy-Free.md +231 -0
  445. package/wiki/Tutorial-06-Growing-Up.md +158 -0
  446. package/wiki/UI-Components.md +156 -0
  447. package/wiki/Upgrading.md +3667 -0
  448. package/src/stored-object-headers.ts +0 -43
@@ -6,6 +6,7 @@
6
6
  import { sortedImports } from './imports';
7
7
  import type { GeneratedFile, NameSet } from './naming';
8
8
  import { examplePageTest, exampleViewTest } from './scaffold-dashboard-example-tests';
9
+ import { exampleView } from './scaffold-dashboard-example-view';
9
10
  import { DASHBOARD_DIR, routeConfig, themeActions, themeIsland } from './scaffold-dashboard-shared';
10
11
 
11
12
  // Plain strings for the framework lines, never template literals: the workspace-dependency scanner
@@ -24,7 +25,7 @@ ${sortedImports([
24
25
  "import { isUltimateError } from '@ultimat3/core';",
25
26
  "import { currentLocale } from '@ultimat3/i18n';",
26
27
  "import { formatMoney } from '@ultimat3/money';",
27
- "import { defineRoute, island } from '@ultimat3/render';",
28
+ "import { defineRoute, island, type RouteContext } from '@ultimat3/render';",
28
29
  [
29
30
  'import {',
30
31
  ' AreaChart,',
@@ -38,6 +39,8 @@ ${sortedImports([
38
39
  ' Section,',
39
40
  ' StatTile,',
40
41
  ' ThemeToggle,',
42
+ ' TimeRangePicker,',
43
+ ' timeRangeUrl,',
41
44
  "} from '@ultimat3/ui';",
42
45
  ].join('\n'),
43
46
  ])}
@@ -45,7 +48,10 @@ import { Shell } from '../../shared/shell';
45
48
  import { postList } from '../post/live/post-list';
46
49
  import {
47
50
  bucketByDay,
48
- CHART_DAYS,
51
+ CHART_RANGES,
52
+ CHART_ZONE,
53
+ chartDays,
54
+ DEFAULT_CHART_RANGE,
49
55
  formatCount,
50
56
  type PostRow,
51
57
  postStats,
@@ -66,6 +72,8 @@ export interface DashboardData {
66
72
  readonly rows: readonly PostRow[];
67
73
  /** The instant the windows were cut at, so server and test agree on "the last 7 days". */
68
74
  readonly now: string;
75
+ /** The request's URL: the chart's window lives in it (\`?range=\`, or a custom \`from\`/\`to\`). */
76
+ readonly url: string;
69
77
  }
70
78
 
71
79
  /**
@@ -76,12 +84,13 @@ export interface DashboardData {
76
84
  */
77
85
  export async function dashboardData(
78
86
  read: () => Promise<readonly Parameters<typeof toPostRow>[0][]>,
87
+ url: string,
79
88
  ): Promise<DashboardData> {
80
89
  const now = new Date().toISOString();
81
90
  try {
82
- return { rows: (await read()).map(toPostRow), now };
91
+ return { rows: (await read()).map(toPostRow), now, url };
83
92
  } catch (error) {
84
- if (isUltimateError(error) && error.code === 'X_DB_UNAVAILABLE') return { rows: [], now };
93
+ if (isUltimateError(error) && error.code === 'X_DB_UNAVAILABLE') return { rows: [], now, url };
85
94
  throw error;
86
95
  }
87
96
  }
@@ -93,7 +102,8 @@ export async function dashboardData(
93
102
  * read to the request's actor, so the viewer \`auth/dev-actor.ts\` resolves sees the rows
94
103
  * \`packages/db/src/seed.ts\` wrote, and a real session sees its own org's the day one exists.
95
104
  */
96
- const load = (): Promise<DashboardData> => dashboardData(() => postList({ limit: ROW_LIMIT }));
105
+ const load = ({ url }: RouteContext): Promise<DashboardData> =>
106
+ dashboardData(() => postList({ limit: ROW_LIMIT }), url);
97
107
 
98
108
  ${themeIsland}
99
109
 
@@ -108,7 +118,27 @@ export function Page(props: PageProps) {
108
118
  const locale = currentLocale();
109
119
  const now = new Date(props.data.now);
110
120
  const stats = postStats(props.data.rows, now);
111
- const points = bucketByDay(props.data.rows, now, CHART_DAYS);
121
+ // The chart's window, from the URL: a preset link or the custom form, kept across reload and
122
+ // share. A window the URL cannot hold (backwards, malformed) is the default, and the picker says so.
123
+ const labels = new Map<string, string>([
124
+ ['week', t('app.dashboard.rangeWeek')],
125
+ ['fortnight', t('app.dashboard.rangeFortnight')],
126
+ ['month', t('app.dashboard.rangeMonth')],
127
+ ]);
128
+ const range = timeRangeUrl(props.data.url, {
129
+ presets: CHART_RANGES.map((preset) => ({
130
+ id: preset.id,
131
+ label: labels.get(preset.id) ?? preset.id,
132
+ seconds: preset.days * 86_400,
133
+ })),
134
+ default: DEFAULT_CHART_RANGE,
135
+ timeZone: CHART_ZONE,
136
+ now,
137
+ });
138
+ // A capped read cannot see the days before its oldest row: those are unknown, never zero.
139
+ const points = bucketByDay(props.data.rows, range.to, chartDays(range.from, range.to), {
140
+ capped: props.data.rows.length >= ROW_LIMIT,
141
+ });
112
142
  const mix = priceMix(props.data.rows);
113
143
  const latest = props.data.rows.slice(0, TABLE_ROWS);
114
144
  // Per currency: a bar is a share of the dearest post IN ITS CURRENCY, never of another's minor units.
@@ -137,6 +167,8 @@ export function Page(props: PageProps) {
137
167
  label={t('app.dashboard.statWeek')}
138
168
  value={formatCount(stats.lastWeek, locale)}
139
169
  delta={stats.delta}
170
+ goodWhen="up"
171
+ trend={points}
140
172
  hint={t('app.dashboard.hintVsPriorWeek')}
141
173
  />
142
174
  <StatTile
@@ -157,6 +189,11 @@ export function Page(props: PageProps) {
157
189
  title={t('app.dashboard.chartTitle')}
158
190
  description={t('app.dashboard.chartRange')}
159
191
  >
192
+ <TimeRangePicker
193
+ range={range}
194
+ timeZone={CHART_ZONE}
195
+ label={t('app.dashboard.rangeLabel')}
196
+ />
160
197
  <AreaChart
161
198
  label={t('app.dashboard.chartLabel')}
162
199
  keys={points.map((point) => point.key)}
@@ -225,131 +262,6 @@ export function Page(props: PageProps) {
225
262
  }
226
263
  `;
227
264
 
228
- const exampleView =
229
- (): string => `// The dashboard's numbers, apart from its markup: pure functions over the rows the page loaded,
230
- // so the server render and a test cannot disagree about a figure. No I/O, no \`t()\`, no JSX.
231
-
232
- import type { Money } from '@ultimat3/money';
233
- import { type ChartPoint, deltaOf, type StatDelta } from '@ultimat3/ui';
234
-
235
- /** Days of history the chart shows, oldest first. */
236
- export const CHART_DAYS = 14;
237
-
238
- const DAY_MS = 86_400_000;
239
-
240
- /** One post as the page reads it. \`createdAt\` is ISO text: a \`Date\` cannot cross a JSON seam. */
241
- export interface PostRow {
242
- readonly id: string;
243
- readonly title: string;
244
- /** Integer minor units plus an ISO code, never a float — the entity's \`money()\` column. */
245
- readonly price: Money;
246
- readonly createdAt: string;
247
- }
248
-
249
- /**
250
- * Duck-typed rather than the entity's own row: the page only ever reads these four columns. The
251
- * query reads through the typed handle, so \`createdAt\` arrives as the \`Date\` the entity declares.
252
- */
253
- export function toPostRow(row: {
254
- readonly id: string;
255
- readonly title: string;
256
- readonly price: Money;
257
- readonly createdAt: Date | string;
258
- }): PostRow {
259
- const at = row.createdAt;
260
- return {
261
- id: row.id,
262
- title: row.title,
263
- price: row.price,
264
- createdAt: at instanceof Date ? at.toISOString() : at,
265
- };
266
- }
267
-
268
- /** Free (a zero price) against paid, over every row loaded — the two halves of the ring. */
269
- export interface PriceMix {
270
- readonly free: number;
271
- readonly paid: number;
272
- }
273
-
274
- export function priceMix(rows: readonly PostRow[]): PriceMix {
275
- const paid = rows.filter((row) => row.price.minor > 0).length;
276
- return { free: rows.length - paid, paid };
277
- }
278
-
279
- /**
280
- * The dearest price per currency, in minor units — what each row's in-cell bar is a share of.
281
- * Per currency because 1,900 cents and 1,900 yen are not the same height.
282
- */
283
- export function priceCeilings(rows: readonly PostRow[]): ReadonlyMap<string, number> {
284
- const ceilings = new Map<string, number>();
285
- for (const row of rows) {
286
- const { currency, minor } = row.price;
287
- ceilings.set(currency, Math.max(ceilings.get(currency) ?? 0, minor));
288
- }
289
- return ceilings;
290
- }
291
-
292
- export interface PostStats {
293
- readonly total: number;
294
- /** Created in the seven days ending at \`now\`. */
295
- readonly lastWeek: number;
296
- /** \`lastWeek\` against the seven days before it; absent when that baseline is zero. */
297
- readonly delta: StatDelta | undefined;
298
- /** Created in the UTC day \`now\` falls in — a partial day, so it carries no delta. */
299
- readonly today: number;
300
- }
301
-
302
- const createdAtOf = (row: PostRow): number => Date.parse(row.createdAt);
303
-
304
- const within = (rows: readonly PostRow[], from: number, to: number): number =>
305
- rows.filter((row) => {
306
- const at = createdAtOf(row);
307
- return Number.isFinite(at) && at > from && at <= to;
308
- }).length;
309
-
310
- export function postStats(rows: readonly PostRow[], now: Date): PostStats {
311
- const end = now.getTime();
312
- const weekAgo = end - 7 * DAY_MS;
313
- const dayStart = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
314
- const lastWeek = within(rows, weekAgo, end);
315
- return {
316
- total: rows.length,
317
- lastWeek,
318
- delta: deltaOf(lastWeek, within(rows, weekAgo - 7 * DAY_MS, weekAgo)),
319
- today: within(rows, dayStart - 1, end),
320
- };
321
- }
322
-
323
- /** \`MM-DD\` of a UTC day. ISO, never \`toLocaleDateString\`: the chart's axis is data, not prose. */
324
- const dayKey = (at: number): string => new Date(at).toISOString().slice(5, 10);
325
-
326
- /**
327
- * One bucket per UTC day, the last one being the day \`now\` falls in, oldest first — the shape
328
- * \`BarChart\` draws. Every day is present even when nothing was created on it: a chart with the
329
- * quiet days removed reads as busier than the app is.
330
- */
331
- export function bucketByDay(
332
- rows: readonly PostRow[],
333
- now: Date,
334
- days: number,
335
- ): readonly ChartPoint[] {
336
- const today = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
337
- const start = today - (days - 1) * DAY_MS;
338
- const counts: number[] = Array.from({ length: days }, () => 0);
339
- for (const row of rows) {
340
- const at = createdAtOf(row);
341
- if (!Number.isFinite(at)) continue;
342
- const index = Math.floor((at - start) / DAY_MS);
343
- if (index >= 0 && index < days) counts[index] = (counts[index] ?? 0) + 1;
344
- }
345
- return counts.map((value, index) => ({ key: dayKey(start + index * DAY_MS), value }));
346
- }
347
-
348
- /** A figure for a tile, in the page's locale. \`Intl.NumberFormat\` needs no time zone. */
349
- export const formatCount = (value: number, locale: string): string =>
350
- new Intl.NumberFormat(locale).format(value);
351
- `;
352
-
353
265
  /** The example dashboard's page, view module and the tests beside both. */
354
266
  export const exampleDashboardFiles = (app: NameSet): readonly GeneratedFile[] => [
355
267
  { path: `${DASHBOARD_DIR}/page.tsx`, contents: examplePage(app) },
@@ -5,12 +5,8 @@
5
5
  export const DASHBOARD_DIR = 'apps/web/app/dashboard';
6
6
 
7
7
  export const routeConfig = (load: string): string => `export const config = defineRoute({
8
- // 'ssr', not 'stream', and this is not a downgrade: 'stream' needs a boundary to stream into,
9
- // and the framework has no hole marker yet. Solid's <Suspense> is not it — it throws outside a
10
- // Solid renderer, and the server JSX factory is inert on purpose. A scaffolded 'stream' route
11
- // therefore failed x routes with X_ROUTE_MODE_INVALID on the first run, printing a fix nobody
12
- // could follow. Ship the mode that works. Async data needs no boundary: await it in the page.
13
- render: 'ssr',
8
+ // The app/ default: the shell flushes first, and a read moved into a <Hole> arrives after it.
9
+ render: 'stream',
14
10
  // 'visible' for the one island on this page, the theme toggle in the header.
15
11
  hydrate: 'visible',
16
12
  offline: 'runtime',
@@ -22,7 +18,7 @@ export const routeConfig = (load: string): string => `export const config = defi
22
18
  // toggle, provider, the ui runtime they share and the error registry are the rest. It was
23
19
  // 60.9kb before the framework stopped shipping solid-js twice and the i18n catalog with it
24
20
  // (issue #490), which is what put this at 64kb; 60kb is the figure the scaffold budgets held
25
- // before that, kept rather than tightened so a Bun patch cannot red a first \`bun run check\`.
21
+ // before that, kept rather than tightened so a Bun patch cannot red a first \`bun run verify\`.
26
22
  budget: { js: '60kb' },${load}
27
23
  meta: ({ t }) => ({
28
24
  title: t('app.dashboard.title'),
@@ -75,7 +75,7 @@ export const dbClientSource = (
75
75
 
76
76
  ${sortedImports([
77
77
  "import { storeMode } from '@ultimat3/core';",
78
- "import { type Driver, database, memoryDriver, postgresDriver } from '@ultimat3/entity';",
78
+ "import { type Driver, database, postgresDriver, testDriver } from '@ultimat3/entity';",
79
79
  ...entries.map(handleImportLine),
80
80
  ])}
81
81
 
@@ -84,13 +84,13 @@ ${sortedImports([
84
84
  * reads through (\`driver.reset?.()\`).
85
85
  *
86
86
  * \`postgresDriver()\` takes no connection: it resolves \`@ultimat3/db\`'s process client, which is
87
- * the embedded PGlite under \`x dev\` and the \`DATABASE_URL\` pool in a container. \`bun test\`
88
- * installs no client, so a test gets the in-memory driver — the same contract, no database.
89
- * \`storeMode\` is core's one answer to that question, shared with every other seam that has a
90
- * memory and a database store.
87
+ * the embedded PGlite under \`x dev\` and the \`DATABASE_URL\` pool in a container. Under
88
+ * \`bun test\` it is \`testDriver()\`: memory until a test installs its migrated database with
89
+ * \`setDbClient\`, that database while it is installed — decided per call, so this module-load
90
+ * constant follows the client. \`storeMode\` is core's one answer to which store a seam takes.
91
91
  */
92
92
  export const selectDriver = (env: Readonly<Record<string, string | undefined>>): Driver =>
93
- storeMode(env) === 'memory' ? memoryDriver() : postgresDriver();
93
+ storeMode(env) === 'memory' ? testDriver() : postgresDriver();
94
94
 
95
95
  export const driver = selectDriver(Bun.env);
96
96
 
@@ -111,6 +111,7 @@ const clientTest = (
111
111
  ): string => `// Which store the handle reads: memory under \`bun test\`, the process's Postgres client
112
112
  // everywhere else. A handle that silently took the memory driver in a container would serve an
113
113
  // empty database with a green gate.
114
+ import { recordingClient, setDbClient } from '@ultimat3/db';
114
115
  import { expect, unitTest } from '@ultimat3/testing';
115
116
  import { db, driver, selectDriver } from './client';
116
117
 
@@ -126,6 +127,19 @@ unitTest('the handle is built over the driver this module exports', () => {
126
127
  expect(driver.reset).toBeDefined();
127
128
  expect(typeof db).toBe('object');
128
129
  });
130
+
131
+ // The test driver decides per call: a migrated database installed with \`setDbClient\` is what
132
+ // \`db\` reaches, and clearing it hands the handle back to memory.
133
+ unitTest('under test the handle follows an installed client', async () => {
134
+ const client = recordingClient();
135
+ setDbClient(client);
136
+ try {
137
+ await driver.transactor().run(async () => undefined);
138
+ } finally {
139
+ setDbClient(undefined);
140
+ }
141
+ expect(client.texts).toContain('BEGIN');
142
+ });
129
143
  `;
130
144
 
131
145
  /**
@@ -101,19 +101,6 @@ export {};
101
101
  *
102
102
  * `x db seed` owns the connection, the tier and the per-seed transaction. One runner, one answer.
103
103
  */
104
- /**
105
- * The seed's two package imports in the order Biome sorts them: the app's own scope can sort on
106
- * either side of \`@ultimat3\`, and a fixed order is a lint failure for half of all app names.
107
- */
108
- const seedImports = (app: NameSet): string =>
109
- [
110
- [`@${app.kebab}/web/shared/demo-org`, 'DEMO_ORG_LABEL'],
111
- ['@ultimat3/entity', 'defineSeed'],
112
- ]
113
- .sort(([a = ''], [b = '']) => (a < b ? -1 : a > b ? 1 : 0))
114
- .map(([from, name]) => `import { ${name} } from '${from}';`)
115
- .join('\n');
116
-
117
104
  const dbSeed = (app: NameSet, example: boolean): string =>
118
105
  example
119
106
  ? `// Deterministic fixtures: the same rows every time, so a test, a demo and a branch database
@@ -124,9 +111,13 @@ const dbSeed = (app: NameSet, example: boolean): string =>
124
111
  // included), and wraps each seed in its own transaction. Never a plain \`bun run\` script: that
125
112
  // reaches the database through \`db()\`, which needs a \`postgres:\` \`DATABASE_URL\` and so cannot
126
113
  // see the embedded database at all.
127
- ${seedImports(app)}
114
+ import { defineSeed } from '@ultimat3/entity';
128
115
  import { post } from './schema';
129
116
 
117
+ // \`apps/web/shared/demo-org.ts\`'s label, spelled here because a package imports no app
118
+ // (X_BOUNDARY_PACKAGE_TO_APP). \`seed.test.ts\` reads that module and holds the two equal.
119
+ const DEMO_ORG_LABEL = 'org:demo';
120
+
130
121
  /** Stable across runs: \`id('post:hello')\` is a UUID v5 of the label, not a random one. */
131
122
  export const ${app.camel}Seed = defineSeed('${app.kebab}', async ({ insert, id }) => {
132
123
  await insert(post, [
@@ -2,34 +2,52 @@
2
2
  // the bin/ scripts and the optional dev compose. Separated from the config half so neither file has
3
3
  // to be scrolled to find the other — one file, one job applies to templates too. The image, its
4
4
  // ignore file, the production topology and the deploy page are `scaffold-container.ts`; the
5
- // `.claude/` harness that reads AGENTS.md is `scaffold-claude.ts`; the one workflow is
5
+ // `.claude/` harness that reads CLAUDE.md is `scaffold-claude.ts`; the one workflow is
6
6
  // `github/ci.yml.ts`.
7
7
 
8
8
  import { LINE_CEILING } from '../workspace-checks';
9
9
  import { githubFiles } from './github/ci.yml';
10
10
  import type { GeneratedFile, NameSet } from './naming';
11
+ import { S3_GATEWAY_IMAGE } from './pinned-images';
11
12
  import { binFiles } from './scaffold-bin';
12
13
  import { claudeFiles } from './scaffold-claude';
13
14
  import { containerFiles } from './scaffold-container';
15
+ import { mcpJsonFile } from './scaffold-mcp-json';
14
16
  import { testServicesFiles } from './scaffold-test-services';
15
17
 
16
- const agents = (app: NameSet): string => `# AGENTS.md
18
+ /**
19
+ * The one convention file, under two names: `CLAUDE.md` holds it and `AGENTS.md` is a relative
20
+ * symlink to it (the owner's ruling, 2026-11-03), so there is no second copy to drift. Every word
21
+ * stays vendor-neutral, because every agent reads it — `scaffold-agents-md.test.ts` refuses one.
22
+ */
23
+ const conventions = (app: NameSet): string => `# ${app.pascal}
17
24
 
18
- Human-authored, short, stable. Facts live in \`x.manifest.json\`; this file holds only what an
19
- agent cannot infer from the code.
25
+ Human-authored, short, stable — one file under two names, \`AGENTS.md\` being a symlink to it.
26
+ Facts live in \`x.manifest.json\`; this file holds only what an agent cannot infer from the code.
20
27
 
21
28
  Every row below names what refuses it. A rule with nothing in the last column is a rule that does
22
- not exist — five of these had an empty column and were each measured green on \`x verify\`.
29
+ not exist — five of these had an empty column and were each measured green on \`bunx x verify\`.
30
+
31
+ There is no global \`x\`: every command here is \`bunx x …\`, the CLI this app installs.
32
+
33
+ Before stating or working around a framework limit, ask the installed framework:
34
+ \`bunx x docs "<question>"\`, and \`bunx x errors explain <CODE>\` for an error code.
35
+
36
+ Before an edit, ask what it reaches: \`bunx x impact <file> --json\` names the routes, actions,
37
+ queries, jobs, MCP tools, published operations and tests it touches, each with provenance.
23
38
 
24
39
  | Rule | Detail | Refused by |
25
40
  |---|---|---|
26
- | One gate | \`bun run check\` — \`x build\` and then \`x verify\`. Green means shippable; never merge red. | the gate itself, and \`.github/workflows/ci.yml\` on every push and pull request; \`.github/workflows/image.yml\` publishes only a commit it passed |
41
+ | One gate | \`bun run verify\` — \`bunx x verify\`, which builds what its \`budgets\` step weighs. Green means shippable; never merge red. CI runs the same two commands as your terminal: \`bun run setup && bun run verify\` | the gate itself, and \`.github/workflows/ci.yml\` on every push and pull request; \`.github/workflows/image.yml\` publishes only a commit it passed |
27
42
  | Coverage | the unit suite covers 95% of the lines and functions of this app's own source — every file under \`apps/\` and \`packages/\`, one no test loads counted at 0%. A floor, not the goal: a test added to raise it is proven by mutation — break the source, watch it go red, restore | \`X_COVERAGE_BELOW_FLOOR\` on the \`unit\` step; \`coverage\` in \`x.verify.json\` states the floor, and it only rises |
28
- | One way | generators, not hand-rolled files: \`x g resource\`, \`x g action\`, \`x g route\` | review |
43
+ | One way | generators, not hand-rolled files: \`bunx x g resource\`, \`bunx x g action\`, \`bunx x g route\` | review |
29
44
  | Surfaces | \`site/\` is 0kb JS and may not import \`app/\`; \`shared/\` is a leaf | \`X_BOUNDARY_SITE_TO_APP\` |
30
45
  | Data | routes call actions and queries; only \`repo.ts\` touches the database | \`X_BOUNDARY_ROUTE_TO_DB\` |
31
46
  | Repos | a \`repo.ts\` reads through the typed handle, never a \`sql\` literal the handle can express | \`guards/repo-raw-sql.ts\` |
32
- | Errors | never \`throw new Error\` — subclass \`UltimateError\` with a code, a cause and a fix | \`guards/bare-error.ts\` |
47
+ | Vendors | a server \`fetch(\` carries a \`signal\` in its own options — better, it is \`outbound()\` from \`@ultimat3/core/outbound\`: a deadline, the caller's signal, safe-only retry | \`guards/raw-fetch.ts\` |
48
+ | Polling | an island re-reads on a timer with \`visibleInterval(run, ms)\` from \`@ultimat3/core/page\` — no call while the tab is hidden, none after a lost session — never a bare \`setInterval(\`; a \`// why:\` on the line admits a tick that is not a read | \`guards/raw-interval.ts\` |
49
+ | Confirmation | a destructive act asks through \`ConfirmDialog\` from \`@ultimat3/ui\` — Cancel focused, a native post, stays open on a refusal — never \`confirm(\`; a \`// why:\` on the line admits a call that is not that question | \`guards/native-confirm.ts\` |
50
+ | Errors | never \`throw new Error\` — declare each code once, \`defineError({ code, title, status, cause, fix })\` from \`@ultimat3/core\`, and throw its class; never a handwritten \`UltimateError\` subclass | \`guards/bare-error.ts\`, \`guards/error-declared.ts\` |
33
51
  | Money | \`{ minor, currency }\`, never a float — \`money()\` on the column | the type: \`price: 19.99\` is TS2322, \`number\` is not \`MoneyInput\` |
34
52
  | Time | store UTC, format with an explicit IANA time zone | \`guards/unzoned-date.ts\` |
35
53
  | Strings | every user-facing string goes through \`t()\` | \`guards/untranslated-string.ts\` |
@@ -45,11 +63,11 @@ not exist — five of these had an empty column and were each measured green on
45
63
  | Focus | \`outline: none\` replaces the ring in the same rule or the one beside it, or it does not remove it | \`guards/focus-visible.ts\` |
46
64
  | Images | every image carries width + height or an aspect-ratio, and the priority one is never \`loading="lazy"\` | \`guards/image-dimensions.ts\` |
47
65
  | Motion | animate \`transform\` and \`opacity\` — never a layout property, never \`transition: all\` | \`guards/animated-layout-property.ts\` |
48
- | Islands | every \`*.island.tsx\` has a sibling \`*.island.states.ts\`, so \`x shot --island\` can photograph its failures | \`guards/island-without-states.ts\` |
66
+ | Islands | every \`*.island.tsx\` has a sibling \`*.island.states.ts\`, so \`bunx x shot --island\` can photograph its failures | \`guards/island-without-states.ts\` |
49
67
  | Size | one file, one job — ${LINE_CEILING} lines of reviewable logic, and split past it | \`X_FILE_TOO_LONG\` |
50
68
 
51
- \`guards/\` is yours: each file is one rule, discovered by \`x verify\` and run inside its
52
- \`boundaries\` step. Delete one to drop the rule, and \`x g guard <name>\` writes the next.
69
+ \`guards/\` is yours: each file is one rule, discovered by \`bunx x verify\` and run inside its
70
+ \`boundaries\` step. Delete one to drop the rule, and \`bunx x g guard <name>\` writes the next.
53
71
 
54
72
  The last five rows are about what this app is like to USE, and each is decidable from the file
55
73
  alone — which is why they are those five and not the many that are not. A role is a promise:
@@ -58,21 +76,21 @@ already does. Only \`transform\` and \`opacity\` animate without a layout pass.
58
76
  moves everything under it when its bytes land.
59
77
 
60
78
  Size is a hard line and not a style note: past ${LINE_CEILING} lines a file has stopped being the
61
- unit of review, and \`x verify\` refuses it. The one exemption is a file that is nothing but re-exports —
79
+ unit of review, and \`bunx x verify\` refuses it. The one exemption is a file that is nothing but re-exports —
62
80
  it has one job by construction, and its length tracks the API's size rather than its complexity;
63
81
  one statement of logic in such a file re-arms the ceiling on the same save.
64
82
 
65
83
  Coverage counts execution, not validation: 100% is reachable with zero assertions, and a covered
66
84
  branch whose test cannot fail is worse than an uncovered one, because it reads as done. An
67
85
  \`exclude\` entry under \`coverage\` is for code a unit test cannot execute — an island's
68
- browser-only mount, a container entry point — carries its \`why\`, and is printed by \`x doctor\`.
86
+ browser-only mount, a container entry point — carries its \`why\`, and is printed by \`bunx x doctor\`.
69
87
 
70
88
  Every test the scaffold and the generators write is the idiom to copy. All of them run on the
71
89
  \`unit\` step — in process, on the in-memory driver, with no database, browser or server.
72
90
 
73
91
  A suite that needs a real server reads it from \`docker/test-services.env\`: Postgres 18, Redis, NATS
74
92
  and S3 from \`docker/docker-compose.test.yml\`, in RAM, with durability and logging off — CI starts
75
- it before \`bun run check\`, a laptop with \`docker compose -f docker/docker-compose.test.yml up -d
93
+ it before \`bun run verify\`, a laptop with \`docker compose -f docker/docker-compose.test.yml up -d
76
94
  --wait\` then \`set -a; . docker/test-services.env; set +a\`. A resource a test opens is held with
77
95
  \`await using\` — \`await using db = await acquireWorkerDatabase()\` drops its database however
78
96
  the test ends:
@@ -83,7 +101,7 @@ the test ends:
83
101
  | a component | \`renderView(Component, props)\` | \`apps/web/shared/shell.test.ts\` |
84
102
  | an island | \`mountIsland\`, once per state its \`*.island.states.ts\` declares | \`apps/web/shared/theme-toggle.island.test.ts\` |
85
103
  | an action or a query | \`target.as(actor, input)\` — the refusal first, then what it answers | \`apps/web/api/health.test.ts\` |
86
- | a job, a task, a backfill | \`test('…', async ({ runJobs }) => …)\` — a worker in this process; assert on \`trace.steps\` and each run's \`outcome\` | what \`x g job\` writes |
104
+ | a job, a task, a backfill | \`test('…', async ({ runJobs }) => …)\` — a worker in this process; assert on \`trace.steps\` and each run's \`outcome\` | what \`bunx x g job\` writes |
87
105
  | a repo, a seed | as an actor, \`runWithContext(ctxOf({ actor }), …)\`, with \`driver.reset?.()\` after each | \`packages/db/src/seed.test.ts\` |
88
106
 
89
107
  Money is the one row with no guard, deliberately: a float has no static signature a text rule can
@@ -91,45 +109,37 @@ see, and the type already fires — measured, \`price: 19.99\` in a seed is
91
109
  \`TS2322: Type 'number' is not assignable to type 'MoneyInput'\`. A guard that pretended to check
92
110
  it would be worse than the type that really does.
93
111
 
94
- \`bun run check\` is the gate and \`x verify\` is only its second half: the first is
95
- \`x build --target static\`, and the build is what writes the \`.x/build-stats.json\` the
96
- \`budgets\` step measures. Run \`x verify\` on a tree nobody has built and \`budgets\` is red with
97
- X_BUDGET_UNMEASURED — the gate reporting on a file that does not exist, not on your code.
112
+ \`bun run verify\` is \`bunx x verify\`, the whole gate: its \`budgets\` step runs
113
+ \`bunx x build --target static\` itself when \`.x/build-stats.json\` is missing or older than the
114
+ source, so nothing has to be built first.
98
115
 
99
- The platform's \`.dz/\` is ADDITIVE, in both directions. developerz.ai keeps its own files under
100
- \`.dz/maintainer/\` and \`.dz/pipeline/\`; nothing \`x new\` writes lands under \`.dz/\` and nothing
101
- here is generated from it. So the scaffold never clobbers a maintainer policy, the platform never
102
- clobbers \`AGENTS.md\`, \`bin/\`, \`.claude/\` or \`.github/\`, and deleting either side leaves the
103
- other exactly as it was.
116
+ Commands: \`bun run setup\`, \`bun run dev\`, \`bun run verify\`, \`bunx x g <primitive>\`,
117
+ \`bunx x g guard <name>\`, \`bunx x db branch create <name>\`, \`bunx x doctor\`. \`setup\` is
118
+ \`bin/setup.ts\`, a file Bun runs, so all three are the same command in PowerShell, cmd and bash.
104
119
 
105
- Commands: \`bun run setup\`, \`bun run dev\`, \`bun run check\`, \`x g <primitive>\`,
106
- \`x g guard <name>\`, \`x db branch create <name>\`, \`x doctor\`. \`setup\` and \`check\` are
107
- \`bin/*.ts\` files Bun runs, so all three are the same command in PowerShell, cmd and bash.
120
+ - \`bunx x g --help\` lists every generator kind, and is the only place that list is stated.
121
+ - Destructive database work goes in a branch, \`bunx x db branch create <name>\`, never the shared
122
+ dev database.
123
+ - \`bunx x db gen\` also writes \`packages/db/schema/\`, the whole schema as SQL, one file per
124
+ table: read it, never edit it. \`bunx x verify\` fails with \`X_SCHEMA_DUMP_DRIFT\` when it is not
125
+ what the migrations produce, and \`bunx x db gen\` rewrites it.
126
+ - \`bunx x doctor\` explains a broken environment and prints the fix for every finding.
127
+ - \`.claude/\` holds a harness that reads this file: \`/feature\`, \`/planx\`, \`/verify\` and four
128
+ boundary-scoped subagents. \`.claude/README.md\` says what each costs; every file is deletable.
108
129
 
109
- Project notes for ${app.kebab}: replace this line with the conventions a newcomer could not guess.
110
- `;
130
+ ## Agent tools
131
+
132
+ | Tool | What it answers | How to reach it |
133
+ |---|---|---|
134
+ | \`x.manifest.json\` | every route, action, query, job and policy, generated — read it before searching the tree | \`bunx x manifest\` rewrites it |
135
+ | \`bunx x impact <file|primitive> --json\` · \`bunx x graph --json\` | what an edit reaches, and the app as one graph (declared, import and observed edges) | rebuilt from the loaded app on every call |
136
+ | \`.mcp.json\` | the dev tools over MCP: routes, schema, policies, the database, tests, the gate — \`bunx x mcp tools\` lists them | a client that reads \`.mcp.json\` starts it; from any other, run \`bunx @ultimat3/cli mcp serve --transport stdio\` here |
137
+ | \`POST /mcp\` | this app's own tools — every action declaring \`mcp: { expose: true }\` — for a remote agent | \`apps/web/mcp.ts\`; under \`bun run dev\`, \`Authorization: Bearer dev-agent.admin\`, else an API key |
138
+ | \`.claude/commands/feature.md\` | build or fix one thing end to end, done when \`bun run verify\` is green | plain markdown: any agent can read one and follow it |
139
+ | \`.claude/commands/planx.md\` | write a plan to \`docs/plans/\` for another agent to execute | the same |
140
+ | \`.claude/commands/verify.md\` | run the gate and fix what it reports | the same |
111
141
 
112
- const claude = (app: NameSet): string => `# CLAUDE.md
113
-
114
- ${app.kebab} — Ultimate app. Read AGENTS.md first; it is the same content in the same order.
115
-
116
- - Gate: \`bun run check\` (add \`--json\` for machine output — it reaches both halves). It is
117
- \`x build --target static\` and then \`x verify\`; \`x verify\` alone leaves \`budgets\` with
118
- nothing to measure. \`.github/workflows/ci.yml\` runs \`bun run setup && bun run check\` on
119
- every push and pull request, so CI and your terminal run the same two commands.
120
- - \`.dz/\` belongs to the developerz.ai platform and is additive both ways: the scaffold writes
121
- nothing there, and nothing there is generated from this repo. Neither side clobbers the other.
122
- - Scaffold, do not hand-write: \`x g <kind> <name>\` — \`x g --help\` lists every kind, and is the
123
- only place that list is stated.
124
- - Destructive DB work goes in a branch: \`x db branch create <name>\`, never the shared dev DB.
125
- - \`x db gen\` also writes \`packages/db/schema/\` — the whole schema as SQL, one file per table.
126
- Read it; never edit it. \`x verify\` fails with \`X_SCHEMA_DUMP_DRIFT\` when it is not what the
127
- migrations produce, and \`x db gen\` rewrites it.
128
- - \`x doctor\` explains a broken environment and prints the fix command for every finding.
129
-
130
- \`.claude/\` holds the harness that reads this file: \`/feature\`, \`/planx\`, \`/verify\` and four
131
- boundary-scoped subagents. It is yours — \`.claude/README.md\` says what each one costs, and every
132
- file in it is deletable.
142
+ Project notes for ${app.kebab}: replace this line with the conventions a newcomer could not guess.
133
143
  `;
134
144
 
135
145
  const readme = (app: NameSet): string => `# ${app.pascal}
@@ -141,7 +151,7 @@ Built with [Ultimate](https://github.com/developerz-ai/ultimate). Bun-only, Post
141
151
  \`\`\`sh
142
152
  bun run setup # prerequisites, deps, env, the first migration, migrate, seed, the manifest
143
153
  bun run dev # all roles in one process, embedded Postgres, /_x mounted
144
- bun run check # the gate: a static build, then typecheck, lint, boundaries, tests, drift, budgets
154
+ bun run verify # the gate: typecheck, lint, boundaries, tests, drift, budgets (it builds to weigh)
145
155
  \`\`\`
146
156
 
147
157
  Tests against real servers — Postgres 18, Redis, NATS, S3, in RAM and silent, as CI runs them:
@@ -149,7 +159,7 @@ Tests against real servers — Postgres 18, Redis, NATS, S3, in RAM and silent,
149
159
  \`\`\`sh
150
160
  docker compose -f docker/docker-compose.test.yml up -d --wait
151
161
  set -a; . docker/test-services.env; set +a
152
- bun run check
162
+ bun run verify
153
163
  \`\`\`
154
164
 
155
165
  \`packages/db/migrations\` starts empty and \`x db gen\` is its only writer — \`bun run setup\` runs
@@ -168,7 +178,9 @@ with \`X_DB_DRIFT\`, and that is the fix it names.
168
178
  | \`packages/*\` | domain, db, i18n, ui, mcp |
169
179
  | \`app.config.ts\` | the one config file |
170
180
  | \`x.manifest.json\` | generated facts: routes, actions, jobs, policies |
171
- | \`.github/workflows/ci.yml\` | \`bun run setup\` then \`bun run check\`, on push and pull request |
181
+ | \`.mcp.json\` | MCP client config: the dev tools, \`bunx @ultimat3/cli mcp serve --transport stdio\` |
182
+ | \`apps/web/mcp.ts\` | the app's own \`POST /mcp\`: exposed actions, behind a bearer token |
183
+ | \`.github/workflows/ci.yml\` | \`bun run setup\` then \`bun run verify\`, on push and pull request |
172
184
  | \`.github/workflows/image.yml\` | after a green \`ci\` on the default branch: \`docker/Dockerfile\` to \`ghcr.io\`, tagged \`sha-<7>\`, always on \`ubuntu-latest\` (bash and Docker). \`ci.yml\` runs on \`vars.CI_RUNNER\`, default \`ubuntu-latest\` |
173
185
  `;
174
186
 
@@ -206,7 +218,7 @@ services:
206
218
  # the region a request was signed for, and an app that sets no S3_REGION signs for \`auto\` — so
207
219
  # that is this gateway's region. A server for a laptop; production is any S3-compatible endpoint.
208
220
  s3:
209
- image: versity/versitygw:v1.8.0@sha256:30292fc2eeacc67a36993b01f7a7a5e3361a19cced0e80c1d71cfa2a4b0a2499
221
+ image: ${S3_GATEWAY_IMAGE}
210
222
  environment:
211
223
  VGW_BACKEND: posix
212
224
  VGW_BACKEND_ARG: /data
@@ -225,9 +237,13 @@ volumes:
225
237
  export function docsFiles(app: NameSet): readonly GeneratedFile[] {
226
238
  return [
227
239
  { path: 'README.md', contents: readme(app) },
228
- { path: 'AGENTS.md', contents: agents(app) },
229
- { path: 'CLAUDE.md', contents: claude(app) },
230
- // The scripts `bun run setup` and `bun run check` run — TypeScript, so one file per OS.
240
+ { path: 'CLAUDE.md', contents: conventions(app) },
241
+ // A link, after its target. \`contents\` is the copy written only where the OS refuses a
242
+ // symlink (Windows without the privilege): \`write-generated.ts\` decides, never this list.
243
+ { path: 'AGENTS.md', contents: conventions(app), link: 'CLAUDE.md' },
244
+ // The one MCP client config — what AGENTS.md's "Agent tools" block names first.
245
+ mcpJsonFile(app),
246
+ // The script `bun run setup` runs — TypeScript, so one file per OS.
231
247
  ...binFiles(),
232
248
  { path: 'docker/docker-compose.dev.yml', contents: composeDev(app) },
233
249
  // What the suites run against — in RAM, silent; ci.yml starts it before the gate.