@ultimat3/cli 25.1.0 → 26.0.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 (44) hide show
  1. package/README.md +3 -3
  2. package/package.json +28 -28
  3. package/src/cmd-db-branch.ts +15 -11
  4. package/src/cmd-db-spec.ts +2 -2
  5. package/src/cmd-deploy-drain.ts +6 -1
  6. package/src/cmd-docs.ts +1 -1
  7. package/src/cmd-jobs-spec.ts +11 -7
  8. package/src/cmd-jobs.ts +10 -10
  9. package/src/cmd-planned.ts +3 -3
  10. package/src/cmd-registries-spec.ts +6 -6
  11. package/src/cmd-registries.ts +6 -6
  12. package/src/cmd-routes-spec.ts +1 -1
  13. package/src/cmd-routes.ts +9 -52
  14. package/src/cmd-tasks.ts +2 -2
  15. package/src/db-branch.ts +6 -6
  16. package/src/db-finding.ts +1 -1
  17. package/src/dev-restart-retire.ts +3 -1
  18. package/src/doctor-guards.ts +74 -19
  19. package/src/document-styles.ts +2 -4
  20. package/src/error-contract.ts +1 -1
  21. package/src/errors.ts +4 -4
  22. package/src/fix-command.ts +1 -1
  23. package/src/index.ts +6 -6
  24. package/src/jobs-report.ts +3 -3
  25. package/src/jobs-table.ts +1 -1
  26. package/src/live-routes.ts +0 -1
  27. package/src/mcp-db-target.ts +1 -1
  28. package/src/mcp-errors.ts +2 -2
  29. package/src/mcp-host.ts +4 -2
  30. package/src/messages.ts +4 -2
  31. package/src/parse.ts +23 -2
  32. package/src/role-replicator.ts +1 -1
  33. package/src/route-table.ts +113 -0
  34. package/src/runtime-jobs.ts +1 -1
  35. package/src/serve-boot.ts +8 -2
  36. package/src/serve-drain.ts +5 -1
  37. package/src/serve-retire.ts +7 -3
  38. package/src/site-seo.ts +1 -4
  39. package/src/templates/scaffold-claude-agents.ts +4 -4
  40. package/src/templates/scaffold-claude-commands.ts +2 -2
  41. package/src/templates/scaffold-claude.ts +1 -1
  42. package/src/templates/scaffold-guards.ts +12 -0
  43. package/src/templates/scaffold-helm-templates.ts +6 -2
  44. package/src/templates/scaffold-helm.ts +4 -1
@@ -40,7 +40,7 @@ stop; that answer is worth more than a plan built on a shape the app cannot hold
40
40
  |---|---|
41
41
  | \`apps/web/site/<path>/page.tsx\` | public, SEO-critical, 0kb JS |
42
42
  | \`apps/web/app/<slice>/\` | the feature slice: entity, repo, policy, actions, queries, jobs, UI |
43
- | \`apps/web/api/<name>/route.ts\` | HTTP surface for actions |
43
+ | \`apps/web/api/index.ts\` | the one \`defineApi()\` collecting every action and query — no route file lives under \`api/\` |
44
44
  | \`apps/web/shared/\` | tokens, primitives, the actor type — a leaf, imports nothing of yours |
45
45
  | \`packages/db/\` | schema, migrations, seed |
46
46
  | \`packages/ui/\` | components with no feature knowledge |
@@ -79,7 +79,7 @@ feature slice. Nothing else. A change outside that set is a collision — report
79
79
  | Money | \`{ minor, currency }\` — integer minor units and an ISO code, both, always. Never a float |
80
80
  | Time | store UTC; a formatted date always names an explicit IANA time zone |
81
81
 
82
- Inspect before you change: \`x entities list\`, \`x entities describe <name>\`, both with \`--json\`.
82
+ Inspect before you change: \`x entities list\`, \`x entities show <name>\`, both with \`--json\`.
83
83
 
84
84
  Checks: \`bun test <path>/entity.test.ts\`, \`bunx biome check --write <paths>\`, and \`bun run typecheck\`
85
85
  once when you are otherwise done. Never \`x verify\` — that belongs to whoever coordinates you.
@@ -105,7 +105,7 @@ renders. A change outside that set is a collision — report it, do not make it.
105
105
  | Tasks name a time zone | a cron schedule with an ambient zone is a different time twice a year |
106
106
  | No \`any\` | \`unknown\` plus a schema parse |
107
107
 
108
- Inspect before you change: \`x actions describe <name>\`, \`x queries describe <name>\`, \`x jobs ls\`,
108
+ Inspect before you change: \`x actions show <name>\`, \`x queries show <name>\`, \`x jobs list\`,
109
109
  \`x tasks list\`, \`x policy explain <subject>\` — every one takes \`--json\`.
110
110
 
111
111
  Checks: \`bun test <path>/<file>.test.ts\`, \`bunx biome check --write <paths>\`, and \`bun run typecheck\`
@@ -126,7 +126,7 @@ calls. A change outside that set is a collision — report it, do not make it.
126
126
  |---|---|
127
127
  | \`site/\` is 0kb JS | and may not import from \`app/\`. One interactive control on a static page is an island: \`x g island <name> --at <dir>\` |
128
128
  | \`shared/\` is a leaf | tokens, primitives, the actor type. It imports nothing of yours |
129
- | The directory is the URL | \`page.tsx\` under \`site/\`/\`app/\`, \`route.ts\` under \`api/\`. The filename is never the path |
129
+ | The directory is the URL | \`page.tsx\` under \`site/\`/\`app/\`; \`api/\` holds no route file, only \`defineApi()\`'s actions and queries. The filename is never the path |
130
130
  | Every string through \`t()\` | a literal in a component is a string no locale can ever translate |
131
131
  | Semantic tokens only | never a raw hex, in a component or a stylesheet |
132
132
  | Dates name a zone | explicit IANA time zone at every call site, no ambient default |
@@ -66,12 +66,12 @@ SQL into \`packages/db/migrations/\`.
66
66
  |---|---|---|
67
67
  | \`apps/web/site/\` | 0kb JS, may not import \`apps/web/app/\` | the static path pays the app's bundle |
68
68
  | \`apps/web/app/\` | authed, streaming, hydrated | — |
69
- | \`apps/web/api/\` | actions only, \`route.ts\` | a second HTTP surface |
69
+ | \`apps/web/api/\` | actions and queries only, collected by \`defineApi()\` — no route file | a second HTTP surface |
70
70
  | \`apps/web/shared/\` | a leaf: imports nothing of yours | an import cycle across surfaces |
71
71
  | \`repo.ts\` | the only file that touches the database | authz bypassed by a raw read |
72
72
  | routes | call actions and queries, never a repo | policy skipped |
73
73
 
74
- Route files: \`page.tsx\` under \`site/\`/\`app/\`, \`route.ts\` under \`api/\`. **The directory is the URL** —
74
+ Route files: \`page.tsx\` under \`site/\`/\`app/\`, and none under \`api/\`. **The directory is the URL** —
75
75
  the filename never is. One interactive control on a 0kb page is an island: \`x g island <name> --at <dir>\`.
76
76
 
77
77
  ## 4. Gate it
@@ -32,7 +32,7 @@ const settings = (): string => `{
32
32
  "Bash(x actions:*)",
33
33
  "Bash(x queries:*)",
34
34
  "Bash(x entities:*)",
35
- "Bash(x jobs ls:*)",
35
+ "Bash(x jobs list:*)",
36
36
  "Bash(x jobs show:*)",
37
37
  "Bash(x tasks:*)",
38
38
  "Bash(x policy:*)",
@@ -75,6 +75,18 @@ export const SHIPPED_GUARD_NAMES: readonly string[] = SHIPPED.map(([name]) => na
75
75
  export const shippedGuardFiles = (name: string): readonly GeneratedFile[] | undefined =>
76
76
  SHIPPED.find(([shipped]) => shipped === name)?.[1]();
77
77
 
78
+ /**
79
+ * The current text of a shipped guard's rule file (`guards/<name>.ts`), or `undefined` when the
80
+ * name was never shipped — what `x doctor` compares an app's copy against.
81
+ */
82
+ export const shippedGuardSource = (name: string): string | undefined => {
83
+ const contents = shippedGuardFiles(name)?.find(
84
+ (file) => file.path === `guards/${name}.ts`,
85
+ )?.contents;
86
+ // A guard is source text; bytes would be a template bug, and comparing them as text would hide it.
87
+ return typeof contents === 'string' ? contents : undefined;
88
+ };
89
+
78
90
  /**
79
91
  * Every guard `x new` ships, each with its test. One module per guard, because an app deletes a
80
92
  * rule it does not want by deleting one file — and a guard the app then writes for itself is
@@ -195,14 +195,18 @@ nobody bound. Derived here rather than stated twice in values.yaml, where the tw
195
195
  {{/*
196
196
  A role's terminationGracePeriodSeconds, in the order the kubelet's budget is spent: the preStop
197
197
  sleep (web and sync, 1.30+), the worker's opt-in retire (roles.worker.retireSeconds), the
198
- readiness grace (web and sync — every other role drains with none), the drain budget (app.config.ts drain.deadlineMs, every role) and the teardown margin.
198
+ readiness grace (web and sync — every other role drains with none), the drain budget (app.config.ts drain.deadlineMs, every role;
199
+ the worker's is drain.workerDeadlineSeconds instead when > 0, from drain.workerDeadlineMs) and the teardown margin.
199
200
  Defaults: 45s web/sync (40s below 1.30), 35s the rest. x deploy --method helm sets
200
- drain.deadlineSeconds and drain.readinessGraceSeconds from app.config.ts.
201
+ drain.deadlineSeconds, drain.workerDeadlineSeconds and drain.readinessGraceSeconds from app.config.ts.
201
202
  */}}
202
203
  {{- define "${app.kebab}.terminationGracePeriodSeconds" -}}
203
204
  {{- $drain := .root.Values.drain -}}
204
205
  {{- $total := add (int $drain.deadlineSeconds) (int $drain.teardownMarginSeconds) -}}
205
206
  {{- if eq .role "worker" -}}
207
+ {{- if gt (int $drain.workerDeadlineSeconds) 0 -}}
208
+ {{- $total = add (int $drain.workerDeadlineSeconds) (int $drain.teardownMarginSeconds) -}}
209
+ {{- end -}}
206
210
  {{- $total = add $total (int .cfg.retireSeconds) -}}
207
211
  {{- end -}}
208
212
  {{- if .cfg.port -}}
@@ -99,11 +99,14 @@ securityContext:
99
99
  # readinessGraceSeconds app.config.ts drain.readinessGraceMs / 1000 (web and sync only)
100
100
  # deadlineSeconds app.config.ts drain.deadlineMs / 1000 — x deploy --method helm sets
101
101
  # both from app.config.ts, so a raised budget raises the grace with it
102
+ # workerDeadlineSeconds app.config.ts drain.workerDeadlineMs / 1000 — the worker's budget in place
103
+ # of deadlineSeconds, up to a day; 0 means the worker uses deadlineSeconds
102
104
  # teardownMarginSeconds the release after the drain, the telemetry flush, the pool close
103
105
  drain:
104
106
  preStopSleepSeconds: 5
105
107
  readinessGraceSeconds: 5
106
108
  deadlineSeconds: 25
109
+ workerDeadlineSeconds: 0
107
110
  teardownMarginSeconds: 10
108
111
 
109
112
  # How long a new pod must stay Ready before a rollout counts it and stops an old one.
@@ -166,7 +169,7 @@ roles:
166
169
  replicas: 2
167
170
  # The retire, OFF at 0. Set to the longest a held job may run, in seconds, and the worker's
168
171
  # preStop sends its PID 1 SIGUSR2: stop claiming, finish every held job, abort nothing, exit 0
169
- # — for a job whose side effect must happen at most once and may outlast drain.deadlineSeconds.
172
+ # — for a job whose side effect must happen at most once and may outlast its drain budget.
170
173
  # Added to this role's terminationGracePeriodSeconds, which the kubelet counts the preStop against.
171
174
  retireSeconds: 0
172
175
  resources: