@starci/hfs 4.7.31 → 4.7.33

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 (473) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +13 -6
  3. package/emit/contracts.mjs +4 -3
  4. package/emit/form-bounds-reader.ts.partial +58 -0
  5. package/emit/form-bounds.mjs +155 -0
  6. package/emit/graphql-input-bounds.mjs +97 -0
  7. package/emit/input-bounds.mjs +44 -0
  8. package/emit/operation-schemas.mjs +137 -0
  9. package/emit/operations-worker.mjs +2 -2
  10. package/emit/operations.mjs +52 -16
  11. package/emit/schema-worker.mjs +17 -4
  12. package/emit/type-schema.mjs +17 -5
  13. package/package.json +9 -2
  14. package/release/gate.mjs +18 -23
  15. package/release/index.mjs +1 -1
  16. package/release/push.mjs +4 -5
  17. package/runtime/engine/admission.mjs +331 -0
  18. package/runtime/engine/canonical-json.mjs +14 -0
  19. package/runtime/engine/db/blob.mjs +380 -0
  20. package/runtime/engine/db/event-compact.mjs +73 -0
  21. package/runtime/engine/db/ledger-open.mjs +38 -0
  22. package/runtime/engine/db/ledger-paths.mjs +87 -0
  23. package/runtime/engine/db/ledger.mjs +1068 -0
  24. package/runtime/engine/db/machine-connection.mjs +150 -0
  25. package/runtime/engine/db/machine-open.mjs +60 -0
  26. package/runtime/engine/db/machine-schema.mjs +105 -0
  27. package/runtime/engine/db/machine.mjs +1317 -0
  28. package/runtime/engine/db/provider-reservations.mjs +127 -0
  29. package/runtime/engine/db/provision-authority.mjs +76 -0
  30. package/runtime/engine/db/provision-lock.mjs +29 -0
  31. package/runtime/engine/db/provision-operations.mjs +146 -0
  32. package/runtime/engine/db/ref-value.mjs +67 -0
  33. package/runtime/engine/db/schema/machine.sql +920 -0
  34. package/runtime/engine/db/schema/runtime.sql +1113 -0
  35. package/runtime/engine/refuse.mjs +11 -0
  36. package/runtime/knowledge/hfs/canon-pins.yaml +65 -9
  37. package/runtime/knowledge/hfs/design-truths.yaml +149 -0
  38. package/runtime/knowledge/hfs/fe-stack.yaml +23 -5
  39. package/runtime/knowledge/hfs/rules.yaml +269 -48
  40. package/runtime/knowledge/hfs/slots.yaml +113 -53
  41. package/runtime/knowledge/patterns/be/api.yaml +46 -20
  42. package/runtime/knowledge/patterns/be/event-bus.yaml +19 -12
  43. package/runtime/knowledge/patterns/be/idempotency.yaml +265 -0
  44. package/runtime/knowledge/patterns/be/jobs.yaml +17 -12
  45. package/runtime/knowledge/patterns/be/projections.yaml +2 -2
  46. package/runtime/knowledge/patterns/be/queues.yaml +57 -22
  47. package/runtime/knowledge/patterns/be/reactors.yaml +1 -1
  48. package/runtime/knowledge/patterns/be/realtime.yaml +326 -27
  49. package/runtime/knowledge/patterns/be/saga.yaml +2 -2
  50. package/runtime/knowledge/sonar-gate.yaml +1 -1
  51. package/runtime/modules/kernel/command-policy.yaml +249 -0
  52. package/runtime/modules/kernel/failure-codes.yaml +211 -52
  53. package/runtime/modules/kernel/hook-input.yaml +6 -0
  54. package/runtime/modules/kernel/protected-zone.yaml +81 -0
  55. package/runtime/modules/kernel/removed-vocabulary.yaml +62 -0
  56. package/runtime/modules/kernel/roles.yaml +405 -0
  57. package/runtime/modules/models/registry.yaml +3 -1
  58. package/runtime/modules/models/runtimes.yaml +2 -0
  59. package/runtime/modules/schemas/application-stacks.schema.yaml +10 -2
  60. package/runtime/packages/cli/src/catalog.generated.mjs +46 -59
  61. package/runtime/packages/grammar/scripts/build-stamp.mjs +98 -0
  62. package/runtime/scripts/agent/codex-app-server.mjs +79 -0
  63. package/runtime/scripts/agent/codex-mirror-source.mjs +34 -0
  64. package/runtime/scripts/agent/host-agents.mjs +80 -0
  65. package/runtime/scripts/agent/launch-trust-policy.mjs +53 -0
  66. package/runtime/scripts/agent/trust-launch.mjs +191 -0
  67. package/runtime/scripts/agent/trust.mjs +523 -0
  68. package/runtime/scripts/api/fs/rename-over.mjs +21 -0
  69. package/runtime/scripts/api/fs/temp-path.mjs +8 -0
  70. package/runtime/scripts/api/fs/write-json-file.mjs +10 -0
  71. package/runtime/scripts/api/git/diff-names.mjs +8 -0
  72. package/runtime/scripts/api/git/head-time.mjs +10 -0
  73. package/runtime/scripts/api/git/index-lock.mjs +78 -0
  74. package/runtime/scripts/api/git/is-ancestor.mjs +5 -0
  75. package/runtime/scripts/api/git/log.mjs +5 -0
  76. package/runtime/scripts/api/git/merge-base-query.mjs +5 -0
  77. package/runtime/scripts/api/git/merge-tree.mjs +5 -0
  78. package/runtime/scripts/api/git/rev-list.mjs +5 -0
  79. package/runtime/scripts/api/git/rev-parse.mjs +13 -0
  80. package/runtime/scripts/api/git/status-query.mjs +5 -0
  81. package/runtime/scripts/api/git/worktree-list-query.mjs +5 -0
  82. package/runtime/scripts/api/node/spawn-node.mjs +6 -0
  83. package/runtime/scripts/api/npm/dist-tag.mjs +8 -0
  84. package/runtime/scripts/api/npm/lib.mjs +32 -0
  85. package/runtime/scripts/api/npm/pack-dry-run.mjs +9 -0
  86. package/runtime/scripts/api/npm/run-npm.mjs +6 -0
  87. package/runtime/scripts/api/npm/view.mjs +30 -0
  88. package/runtime/scripts/api/npm/whoami.mjs +8 -0
  89. package/runtime/scripts/api/process/hide-child-windows.mjs +31 -0
  90. package/runtime/scripts/api/process/kill-tree.mjs +22 -0
  91. package/runtime/scripts/api/process/lib.mjs +125 -0
  92. package/runtime/scripts/api/process/probe-guard-command.mjs +74 -0
  93. package/runtime/scripts/api/process/process-list.mjs +28 -0
  94. package/runtime/scripts/api/process/system-tool.mjs +37 -0
  95. package/runtime/scripts/gates/canon-digest.mjs +81 -0
  96. package/runtime/scripts/gates/canon-pins.mjs +16 -0
  97. package/runtime/scripts/gates/commit-proof.mjs +53 -0
  98. package/runtime/scripts/gates/gate-input.mjs +98 -0
  99. package/runtime/scripts/gates/gate.mjs +669 -0
  100. package/runtime/scripts/gates/grammar-dist.mjs +206 -0
  101. package/runtime/scripts/gates/runtime-package-selection.mjs +27 -0
  102. package/runtime/scripts/gates/test-world-run.mjs +254 -0
  103. package/runtime/scripts/gates/type-impact.mjs +277 -0
  104. package/runtime/scripts/gates/type-program.mjs +113 -0
  105. package/runtime/scripts/guards/assigned-command.mjs +12 -0
  106. package/runtime/scripts/guards/call-rights.mjs +47 -0
  107. package/runtime/scripts/guards/command-guard.mjs +468 -0
  108. package/runtime/scripts/guards/command-policy.mjs +342 -0
  109. package/runtime/scripts/guards/critic-reach.mjs +82 -0
  110. package/runtime/scripts/guards/deps-guard.mjs +150 -0
  111. package/runtime/scripts/guards/env-dump-verdict.mjs +89 -0
  112. package/runtime/scripts/guards/git-pathspec.mjs +225 -0
  113. package/runtime/scripts/guards/git-policy.mjs +355 -0
  114. package/runtime/scripts/guards/guards-root.mjs +27 -0
  115. package/runtime/scripts/guards/hook-install.mjs +343 -0
  116. package/runtime/scripts/guards/hook-io.mjs +76 -0
  117. package/runtime/scripts/guards/hook-verdict.mjs +12 -0
  118. package/runtime/scripts/guards/install-verdict.mjs +41 -0
  119. package/runtime/scripts/guards/kernel-seat.mjs +63 -0
  120. package/runtime/scripts/guards/launch-verdict.mjs +74 -0
  121. package/runtime/scripts/guards/op-starci.mjs +16 -0
  122. package/runtime/scripts/guards/op-write-scope.mjs +27 -0
  123. package/runtime/scripts/guards/orca-self-lifecycle.mjs +39 -0
  124. package/runtime/scripts/guards/process-kill-verdict.mjs +106 -0
  125. package/runtime/scripts/guards/protected-zone.mjs +56 -0
  126. package/runtime/scripts/guards/refusals.mjs +27 -0
  127. package/runtime/scripts/guards/release-definition.mjs +185 -0
  128. package/runtime/scripts/guards/release-push-gate.mjs +72 -0
  129. package/runtime/scripts/guards/release-record.mjs +58 -0
  130. package/runtime/scripts/guards/release-suite-mode.mjs +15 -0
  131. package/runtime/scripts/guards/rights.mjs +268 -0
  132. package/runtime/scripts/guards/seat-tools.mjs +47 -0
  133. package/runtime/scripts/guards/shell-commands.mjs +328 -0
  134. package/runtime/scripts/guards/supervisor-seat.mjs +80 -0
  135. package/runtime/scripts/guards/verdict-process.mjs +81 -0
  136. package/runtime/scripts/hfs/architecture/dead-exports.mjs +10 -10
  137. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +1 -1
  138. package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +53 -0
  139. package/runtime/scripts/hfs/architecture/reachability.mjs +1 -1
  140. package/runtime/scripts/hfs/architecture/sql-owner.mjs +17 -6
  141. package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +105 -0
  142. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +38 -52
  143. package/runtime/scripts/hfs/architecture/supabase-keys.mjs +261 -0
  144. package/runtime/scripts/hfs/architecture/transport-owner.mjs +49 -3
  145. package/runtime/scripts/hfs/architecture/typescript.mjs +1 -1
  146. package/runtime/scripts/hfs/check.mjs +6 -4
  147. package/runtime/scripts/hfs/coverage-scope.mjs +28 -1
  148. package/runtime/scripts/hfs/db-restamp-verb.mjs +54 -14
  149. package/runtime/scripts/hfs/declaration-shape.mjs +4 -4
  150. package/runtime/scripts/hfs/declaration-slots.mjs +14 -3
  151. package/runtime/scripts/hfs/manifest-shape.mjs +10 -6
  152. package/runtime/scripts/hfs/rule-params-shape.mjs +18 -6
  153. package/runtime/scripts/hfs/rules/a15-findings.mjs +4 -1
  154. package/runtime/scripts/hfs/rules/bypass-markers.mjs +22 -7
  155. package/runtime/scripts/hfs/rules/database-migrations.mjs +69 -14
  156. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +9 -7
  157. package/runtime/scripts/hfs/rules/database-queues.mjs +68 -18
  158. package/runtime/scripts/hfs/rules/edition.mjs +56 -12
  159. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -10
  160. package/runtime/scripts/hfs/rules/fe-stack.mjs +12 -7
  161. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +1 -1
  162. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +75 -9
  163. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +1 -1
  164. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +15 -4
  165. package/runtime/scripts/hfs/rules/text-integrity.mjs +73 -0
  166. package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +74 -0
  167. package/runtime/scripts/hfs/runtime-rules/release-notes.mjs +44 -0
  168. package/runtime/scripts/hfs/sealed-members.mjs +3 -3
  169. package/runtime/scripts/hfs/slot-side-problems.mjs +3 -3
  170. package/runtime/scripts/hfs/slots.mjs +2 -1
  171. package/runtime/scripts/hfs/stack-deploy-config.mjs +22 -5
  172. package/runtime/scripts/hfs/stacks-gate.mjs +28 -7
  173. package/runtime/scripts/hfs/view.mjs +2 -1
  174. package/runtime/scripts/lib/app-data.mjs +11 -0
  175. package/runtime/scripts/lib/bypass-markers.mjs +272 -75
  176. package/runtime/scripts/lib/check-format.mjs +7 -0
  177. package/runtime/scripts/lib/cli-arg.mjs +120 -0
  178. package/runtime/scripts/lib/css-rules.mjs +220 -0
  179. package/runtime/scripts/lib/dom-tree.mjs +4 -0
  180. package/runtime/scripts/lib/escape.mjs +16 -0
  181. package/runtime/scripts/lib/event-items.mjs +22 -0
  182. package/runtime/scripts/lib/failure-code-template-errors.mjs +161 -0
  183. package/runtime/scripts/lib/git-dir.mjs +120 -0
  184. package/runtime/scripts/lib/grammar-vendor-parity.mjs +377 -0
  185. package/runtime/scripts/lib/guard-command.mjs +24 -0
  186. package/runtime/scripts/lib/hook-limits.mjs +5 -0
  187. package/runtime/scripts/lib/interrupt-cleanup.mjs +38 -0
  188. package/runtime/scripts/lib/js-lex.mjs +200 -0
  189. package/runtime/scripts/lib/json-formats.mjs +0 -1
  190. package/runtime/scripts/lib/language.mjs +94 -1
  191. package/runtime/scripts/lib/loosening-budgets.mjs +44 -0
  192. package/runtime/scripts/lib/loosening-config.mjs +361 -0
  193. package/runtime/scripts/lib/loosening-state.mjs +223 -0
  194. package/runtime/scripts/lib/menu-parts.mjs +11 -0
  195. package/runtime/scripts/lib/normalize.mjs +25 -0
  196. package/runtime/scripts/lib/op-gate.mjs +15 -0
  197. package/runtime/scripts/lib/redact.mjs +208 -0
  198. package/runtime/scripts/lib/runtime-location.mjs +21 -0
  199. package/runtime/scripts/lib/runtime-package-inventory.mjs +21 -0
  200. package/runtime/scripts/lib/runtime-package-manifests.mjs +84 -0
  201. package/runtime/scripts/lib/runtime-publication-tags.mjs +99 -0
  202. package/runtime/scripts/lib/seat-env.mjs +43 -0
  203. package/runtime/scripts/lib/secret-patterns.mjs +58 -9
  204. package/runtime/scripts/lib/shell-source.mjs +4 -0
  205. package/runtime/scripts/lib/source-phrases.mjs +34 -0
  206. package/runtime/scripts/lib/sqlite.mjs +21 -0
  207. package/runtime/scripts/lib/swarm-stack.mjs +2 -1
  208. package/runtime/scripts/lib/tar-files.mjs +39 -0
  209. package/runtime/scripts/lib/ts-analysis.mjs +10 -0
  210. package/runtime/scripts/lib/worktree-exclude.mjs +35 -0
  211. package/runtime/scripts/lib/worktree-record.mjs +14 -0
  212. package/runtime/scripts/machine/artifact-hold.mjs +52 -0
  213. package/runtime/scripts/machine/ask-receipts.mjs +113 -0
  214. package/runtime/scripts/machine/evidence-store.mjs +223 -0
  215. package/runtime/scripts/machine/home.mjs +216 -0
  216. package/runtime/scripts/machine/host-lock.mjs +184 -0
  217. package/runtime/scripts/machine/lock-recovery.mjs +55 -0
  218. package/runtime/scripts/machine/roles-contract.mjs +90 -0
  219. package/runtime/scripts/machine/runtime-change.mjs +20 -0
  220. package/runtime/scripts/machine/workflow-tree.mjs +122 -0
  221. package/runtime/scripts/reconciler/checklist-items.mjs +7 -0
  222. package/runtime/scripts/reconciler/guard-row.mjs +25 -0
  223. package/runtime/scripts/work/asset-slot.mjs +284 -0
  224. package/runtime/scripts/work/brand/brand-colour.mjs +125 -0
  225. package/runtime/scripts/work/brand/brand-tokens.mjs +167 -0
  226. package/runtime/scripts/work/brand/brand-value-source.mjs +106 -0
  227. package/runtime/scripts/work/brand/brand.mjs +892 -0
  228. package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +60 -0
  229. package/runtime/scripts/work/draw/draw-dna-findings.mjs +256 -0
  230. package/runtime/scripts/work/draw/draw-dna-selector.mjs +38 -0
  231. package/runtime/scripts/work/draw/draw-dna.mjs +483 -0
  232. package/runtime/scripts/work/draw/draw-layer.mjs +305 -0
  233. package/runtime/scripts/work/draw/draw-rationale-page.mjs +237 -0
  234. package/runtime/scripts/work/draw/draw-rationale-ref.mjs +14 -0
  235. package/runtime/scripts/work/draw/draw-rationale.mjs +353 -0
  236. package/runtime/scripts/work/draw/draw-source.mjs +412 -0
  237. package/runtime/scripts/work/draw/draw-taste.mjs +278 -0
  238. package/runtime/scripts/work/draw/draw-typecheck.mjs +72 -0
  239. package/runtime/scripts/work/draw/rationale-findings.mjs +205 -0
  240. package/runtime/scripts/work/draw-grammar.mjs +175 -0
  241. package/runtime/scripts/work/draw-render-page.mjs +188 -0
  242. package/runtime/scripts/work/draw-render.mjs +626 -0
  243. package/runtime/scripts/work/grammar-proposal.mjs +279 -0
  244. package/runtime/scripts/work/png.mjs +237 -0
  245. package/runtime/scripts/work/render-tools.mjs +64 -0
  246. package/runtime/scripts/work/work-io.mjs +119 -0
  247. package/scaffold/add-inbox-lite.mjs +2 -2
  248. package/scaffold/add-plan.mjs +40 -13
  249. package/scaffold/add-queue-lite.mjs +372 -0
  250. package/scaffold/add.mjs +5 -2
  251. package/scaffold/app.mjs +1 -2
  252. package/scaffold/edition-gate.mjs +11 -7
  253. package/scaffold/lite-exports.mjs +10 -0
  254. package/scaffold/pattern-file-slots.mjs +33 -0
  255. package/scaffold/test-world.mjs +1 -1
  256. package/scaffold/yaml-block-edit.mjs +117 -0
  257. package/src/main.mjs +5 -4
  258. package/stack/deploy.mjs +7 -3
  259. package/stack/post-deploy.mjs +48 -0
  260. package/sync/index.mjs +10 -0
  261. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +1 -1
  262. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +1 -1
  263. package/templates/app/codegen-graphql.mjs.partial +59 -12
  264. package/templates/app/hooks/husky/pre-commit +2 -2
  265. package/templates/app/hooks/husky/pre-push +10 -3
  266. package/templates/app/package-scripts/redis-dev.json +3 -0
  267. package/templates/app/package-scripts-lite/package.json +1 -0
  268. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +36 -8
  269. package/templates/app/skeleton/.starcistacks/dev/README.md +1 -1
  270. package/templates/app/skeleton/.starcistacks/production/README.md +10 -9
  271. package/templates/app/skeleton/.starcistacks/production/infra/swarm/postgres-init.sh +21 -0
  272. package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +1 -1
  273. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +1 -0
  274. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +47 -5
  275. package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +5 -3
  276. package/templates/app/skeleton/README.md +2 -0
  277. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +5 -0
  278. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +2 -2
  279. package/templates/app/skeleton-lite/.starcistacks/production/README.md +1 -1
  280. package/templates/app/skeleton-lite/README.md +1 -1
  281. package/templates/be/inbox/platform/client-operation-outcomes.sql.tpl +18 -0
  282. package/templates/be/inbox/platform/event-bus/errors/event-bus.error.ts.tpl +16 -0
  283. package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +10 -0
  284. package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +1 -1
  285. package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +15 -3
  286. package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +11 -3
  287. package/templates/be/inbox/platform/event-bus/index.ts.tpl +2 -0
  288. package/templates/be/inbox/platform/event-bus/messages/event-bus.messages.ts.tpl +7 -0
  289. package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +2 -2
  290. package/templates/be/inbox/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
  291. package/templates/be/inbox/platform/idempotency/index.ts.tpl +2 -0
  292. package/templates/be/inbox/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
  293. package/templates/be/inbox/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
  294. package/templates/be/inbox/platform/inbox/client-operation.policy.ts.tpl +17 -0
  295. package/templates/be/inbox/platform/inbox/errors/inbox.error.ts.tpl +22 -0
  296. package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +21 -0
  297. package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +334 -11
  298. package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +55 -4
  299. package/templates/be/inbox/platform/inbox/index.ts.tpl +2 -0
  300. package/templates/be/inbox/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
  301. package/templates/be/inbox/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
  302. package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +26 -0
  303. package/templates/be/inbox/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
  304. package/templates/be/inbox/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
  305. package/templates/be/inbox/platform/migration.sql.tpl +1 -0
  306. package/templates/be/inbox/service.spec.ts.tpl +4 -4
  307. package/templates/be/inbox/service.ts.tpl +3 -1
  308. package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -2
  309. package/templates/be/inbox/webhook-door.ts.tpl +3 -2
  310. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +3 -0
  311. package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +10 -0
  312. package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +2 -2
  313. package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +16 -3
  314. package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +11 -3
  315. package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +139 -50
  316. package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +25 -2
  317. package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +139 -78
  318. package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +10 -6
  319. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +262 -159
  320. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +32 -5
  321. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +2 -0
  322. package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +5 -1
  323. package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +2 -0
  324. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -4
  325. package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +1 -0
  326. package/templates/be/patterns/idempotency/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
  327. package/templates/be/patterns/idempotency/platform/idempotency/index.ts.tpl +2 -0
  328. package/templates/be/patterns/idempotency/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
  329. package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
  330. package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.ts.tpl +17 -0
  331. package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.spec.ts.tpl +66 -0
  332. package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.ts.tpl +24 -0
  333. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.spec.ts.tpl +60 -0
  334. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.ts.tpl +12 -0
  335. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.spec.ts.tpl +49 -0
  336. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.ts.tpl +12 -0
  337. package/templates/be/patterns/idempotency/platform/inbox/effectful.decorators.ts.tpl +7 -0
  338. package/templates/be/patterns/idempotency/platform/inbox/errors/inbox.error.ts.tpl +22 -0
  339. package/templates/be/patterns/idempotency/platform/inbox/inbox.decorators.ts.tpl +17 -0
  340. package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +7 -0
  341. package/templates/be/patterns/idempotency/platform/inbox/inbox.module.ts.tpl +28 -0
  342. package/templates/be/patterns/idempotency/platform/inbox/inbox.options.ts.tpl +7 -0
  343. package/templates/be/patterns/idempotency/platform/inbox/inbox.port.ts.tpl +44 -0
  344. package/templates/be/patterns/idempotency/platform/inbox/inbox.service.spec.ts.tpl +388 -0
  345. package/templates/be/patterns/idempotency/platform/inbox/inbox.service.ts.tpl +77 -0
  346. package/templates/be/patterns/idempotency/platform/inbox/index.ts.tpl +10 -0
  347. package/templates/be/patterns/idempotency/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
  348. package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +9 -0
  349. package/templates/be/patterns/idempotency/platform/inbox/persistence/entities/inbox-claim.entity.ts.tpl +25 -0
  350. package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
  351. package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +39 -0
  352. package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1789800005000-create-inbox-claims.ts.tpl +21 -0
  353. package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1792800000000-client-operation-outcomes.ts.tpl +21 -0
  354. package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
  355. package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
  356. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +1 -0
  357. package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +4 -3
  358. package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +2 -0
  359. package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +20 -2
  360. package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +1 -0
  361. package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +4 -0
  362. package/templates/be/patterns/jobs/platform-lite/jobs.index.ts.tpl +10 -0
  363. package/templates/be/patterns/jobs/platform-lite/jobs.sql.tpl +44 -0
  364. package/templates/be/patterns/jobs/platform-lite/persistence.jobs.sql.ts.tpl +33 -0
  365. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +203 -18
  366. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +78 -22
  367. package/templates/be/patterns/queues/platform/errors/queue.error.ts.tpl +19 -0
  368. package/templates/be/patterns/queues/platform/messages/queue.messages.ts.tpl +13 -0
  369. package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +1 -0
  370. package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +4 -0
  371. package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +2 -0
  372. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -4
  373. package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +14 -2
  374. package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +5 -2
  375. package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +72 -74
  376. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +1 -1
  377. package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +23 -8
  378. package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +29 -8
  379. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +7 -3
  380. package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +5 -0
  381. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +2 -0
  382. package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +4 -0
  383. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +2 -1
  384. package/templates/be/patterns/queues/platform/queue.port.ts.tpl +1 -1
  385. package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.spec.ts.tpl +417 -0
  386. package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.ts.tpl +172 -0
  387. package/templates/be/patterns/queues/platform-lite/persistence.queue.sql.ts.tpl +12 -0
  388. package/templates/be/patterns/queues/platform-lite/queue-outbox.sql.tpl +32 -0
  389. package/templates/be/patterns/queues/platform-lite/queue-relay.service.spec.ts.tpl +148 -0
  390. package/templates/be/patterns/queues/platform-lite/queue-relay.service.ts.tpl +51 -0
  391. package/templates/be/patterns/queues/platform-lite/queue-transport.port.ts.tpl +163 -0
  392. package/templates/be/patterns/queues/platform-lite/queue.config.ts.tpl +17 -0
  393. package/templates/be/patterns/queues/platform-lite/queue.error.ts.tpl +25 -0
  394. package/templates/be/patterns/queues/platform-lite/queue.index.ts.tpl +8 -0
  395. package/templates/be/patterns/queues/platform-lite/queue.messages.ts.tpl +21 -0
  396. package/templates/be/patterns/queues/platform-lite/queue.module.ts.tpl +74 -0
  397. package/templates/be/patterns/queues/platform-lite/queue.options.ts.tpl +38 -0
  398. package/templates/be/patterns/queues/queue.queue.ts.tpl +2 -2
  399. package/templates/be/patterns/reactors/application.contracts.ts.tpl +2 -0
  400. package/templates/be/patterns/reactors/consumer.ts.tpl +5 -1
  401. package/templates/be/platform/graphql/graphql.contracts.ts +2 -1
  402. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +1 -1
  403. package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +6 -1
  404. package/templates/be/skeleton/apps/__app__/src/main.ts +7 -4
  405. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +4 -0
  406. package/templates/be/skeleton/apps/cli/src/main.ts +7 -4
  407. package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -1
  408. package/templates/be/skeleton/src/modules/platform/http/errors/http.error.ts +22 -0
  409. package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.spec.ts +235 -0
  410. package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.ts +85 -0
  411. package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.spec.ts +92 -0
  412. package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.ts +52 -0
  413. package/templates/be/skeleton/src/modules/platform/http/http.decorators.ts +9 -0
  414. package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +7 -0
  415. package/templates/be/skeleton/src/modules/platform/http/http.module.ts +19 -0
  416. package/templates/be/skeleton/src/modules/platform/http/http.options.ts +2 -0
  417. package/templates/be/skeleton/src/modules/platform/http/http.port.ts +40 -0
  418. package/templates/be/skeleton/src/modules/platform/http/index.ts +7 -0
  419. package/templates/be/skeleton/src/modules/platform/http/messages/http.messages.ts +15 -0
  420. package/templates/be/skeleton/src/modules/platform/ids/ids.decorators.ts +9 -0
  421. package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +7 -0
  422. package/templates/be/skeleton/src/modules/platform/ids/ids.module.ts +19 -0
  423. package/templates/be/skeleton/src/modules/platform/ids/ids.options.ts +2 -0
  424. package/templates/be/skeleton/src/modules/platform/ids/ids.port.ts +5 -0
  425. package/templates/be/skeleton/src/modules/platform/ids/index.ts +4 -0
  426. package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.spec.ts +15 -0
  427. package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.ts +10 -0
  428. package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.spec.ts +59 -0
  429. package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.ts +21 -0
  430. package/templates/be/skeleton/src/modules/platform/logging/correlation.contracts.ts +16 -0
  431. package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.spec.ts +150 -0
  432. package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.ts +42 -0
  433. package/templates/be/skeleton/src/modules/platform/logging/correlation.port.ts +15 -0
  434. package/templates/be/skeleton/src/modules/platform/logging/correlation.service.spec.ts +23 -0
  435. package/templates/be/skeleton/src/modules/platform/logging/correlation.service.ts +28 -0
  436. package/templates/be/skeleton/src/modules/platform/logging/index.ts +14 -1
  437. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +39 -6
  438. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +15 -3
  439. package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +17 -2
  440. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +13 -1
  441. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +13 -3
  442. package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.spec.ts +69 -0
  443. package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.ts +48 -0
  444. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  445. package/templates/be/skeleton/src/modules/platform/primitives/record.policy.spec.ts +12 -0
  446. package/templates/be/skeleton/src/modules/platform/primitives/record.policy.ts +3 -0
  447. package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +6 -1
  448. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +4 -0
  449. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
  450. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +14 -1
  451. package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +12 -0
  452. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  453. package/templates/be/test-world/world/test-apps.options.ts.tpl +2 -0
  454. package/templates/be/test-world/world/test-world.config.ts.tpl +1 -0
  455. package/upgrade/check.mjs +12 -135
  456. package/upgrade/file-state.mjs +1 -16
  457. package/upgrade/index.mjs +34 -361
  458. package/runtime/scripts/lib/check-code-loosening.mjs +0 -128
  459. package/runtime/scripts/lib/gate-loosening.mjs +0 -292
  460. package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +0 -89
  461. package/templates/app/upgrade-full/.starcistacks/dev/README.md +0 -16
  462. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +0 -13
  463. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +0 -3
  464. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +0 -12
  465. package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +0 -7
  466. package/templates/be/skeleton-lite/apps/api/src/main.ts +0 -26
  467. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +0 -12
  468. package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +0 -7
  469. package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +0 -5
  470. package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +0 -9
  471. package/templates/be/upgrade-full/apps/cli/src/main.ts +0 -29
  472. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +0 -7
  473. package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +0 -23
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+ // draw-grammar.mjs — which @starci/grammar a drawing renders against (owner ruling 2026-09-27: the draw must render
3
+ // against the grammar version the product will SHIP).
4
+ //
5
+ // Candidates, in order:
6
+ // product the grammar the product has installed (resolved from the product app dir, as its bundler would)
7
+ // claude-dist the runtime-built package (.claude/packages/grammar/dist, the grammar main carries - possibly
8
+ // newer than the product's install, e.g. Meter segments 0.5.2 against an installed 0.5.0)
9
+ // The product's install wins when it SATISFIES what the draw needs, and "satisfies" is decided by TypeScript: the
10
+ // draw file type-checks against that candidate's types (scripts/work/draw/draw-source.mjs typecheckDraw). When only
11
+ // claude-dist satisfies, the drawing renders against it through an alias in the draw bundle ONLY (draw-render.mjs
12
+ // --grammar-root), the provenance says so (grammarSource claude-dist@<v>) and an upgrade of the product is OWED
13
+ // (upgradeOwed {status: owed, from, to, range, inRange}) - interface.implement bumps the product's dependency; the
14
+ // draw never touches the product's package.json. When neither satisfies: DRAW_TYPECHECK_FAILED.
15
+ //
16
+ // starci work draw-grammar --product <app dir> [--file <X.draw.tsx>] [--grammar auto|product|claude-dist]
17
+ // [--grammar-dist <package root>] (default .claude/packages/grammar; env STARCI_GRAMMAR_DIST; a lane checkout
18
+ // without a built dist points it at the live checkout's)
19
+ import path from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { revParseQuery } from '../api/git/rev-parse-query.mjs';
22
+ import { findPackage } from '../lib/package-at.mjs';
23
+ import { readJsonFile } from '../lib/json.mjs';
24
+ import { GRAMMAR_PACKAGE, typecheckDraw } from './draw/draw-typecheck.mjs';
25
+ import { grammarDistStatus } from '../gates/grammar-dist.mjs';
26
+ import { isFile } from './work-io.mjs';
27
+ import { isMain } from '../lib/is-main.mjs';
28
+
29
+ const GRAMMAR_SOURCES = Object.freeze(['product', 'claude-dist']);
30
+ export const PREFERENCES = Object.freeze(['auto', ...GRAMMAR_SOURCES]);
31
+ const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
32
+
33
+ /** The product's declared range for the grammar (dependencies/devDependencies/peerDependencies), or null. */
34
+ function productRangeOf(productDir) {
35
+ const pkg = readJsonFile(path.join(productDir, 'package.json'));
36
+ return pkg?.dependencies?.[GRAMMAR_PACKAGE] ?? pkg?.devDependencies?.[GRAMMAR_PACKAGE] ?? pkg?.peerDependencies?.[GRAMMAR_PACKAGE] ?? null;
37
+ }
38
+
39
+ /** A built grammar package root: its package.json plus dist/<family>/index.d.ts and .js. */
40
+ const builtGrammar = (root) => isFile(path.join(root, 'package.json')) && isFile(path.join(root, 'dist', 'core', 'index.d.ts')) && isFile(path.join(root, 'dist', 'core', 'index.js'));
41
+
42
+ /**
43
+ * The main worktree of the git checkout `dir` lives in (a lane worktree's live runtime), or null: a lane checkout never
44
+ * builds packages/grammar/dist (dist/ is untracked), so its drawings resolve the live runtime's build.
45
+ */
46
+ function mainWorktreeOf(dir) {
47
+ try {
48
+ const r = revParseQuery(['--path-format=absolute', '--git-common-dir'], { dir, timeout: 10_000 });
49
+ const common = r.status === 0 ? r.stdout.trim() : '';
50
+ if (!common || path.basename(common) !== '.git') return null;
51
+ const main = path.dirname(common);
52
+ return path.resolve(main) === path.resolve(dir) ? null : main;
53
+ } catch { return null; }
54
+ }
55
+
56
+ /**
57
+ * The claude-dist roots, best first: --grammar-dist / STARCI_GRAMMAR_DIST when given (used as is), else the runtime's
58
+ * packages/grammar when its build is FRESH (grammar-dist.mjs: stamped from the current source), else the live main
59
+ * worktree's fresh build (a lane checkout), else the runtime's built but stale dist - named stale, never silently.
60
+ */
61
+ function claudeDistRoots({ skillRoot = SKILL_ROOT, grammarDist = null, env = process.env, status = grammarDistStatus, mainOf = mainWorktreeOf } = {}) {
62
+ const pinned = grammarDist ?? env.STARCI_GRAMMAR_DIST ?? null;
63
+ if (pinned) return [{ root: path.resolve(pinned), status: null, via: grammarDist ? '--grammar-dist' : 'STARCI_GRAMMAR_DIST' }];
64
+ const own = path.join(skillRoot, 'packages', 'grammar');
65
+ const main = mainOf(skillRoot);
66
+ const tried = [{ root: own, via: 'runtime' }, ...(main ? [{ root: path.join(main, 'packages', 'grammar'), via: 'main-worktree' }] : [])]
67
+ .filter((c) => builtGrammar(c.root)).map((c) => ({ ...c, status: status(c.root) }));
68
+ const fresh = tried.filter((c) => c.status?.ok);
69
+ return fresh.length ? [fresh[0]] : tried.slice(0, 1);
70
+ }
71
+
72
+ function grammarCandidates({ productDir, skillRoot = SKILL_ROOT, grammarDist = null, claudeRoots = null }) {
73
+ const out = [];
74
+ const installed = productDir ? findPackage([productDir], [GRAMMAR_PACKAGE]) : null;
75
+ if (installed && builtGrammar(installed.root)) out.push({ source: 'product', root: installed.root, version: installed.version });
76
+ for (const c of claudeRoots ?? claudeDistRoots({ skillRoot, grammarDist })) {
77
+ if (!builtGrammar(c.root)) continue;
78
+ out.push({ source: 'claude-dist', root: c.root, version: readJsonFile(path.join(c.root, 'package.json'))?.version ?? null, via: c.via,
79
+ ...(c.status ? { dist: { state: c.status.state, ok: c.status.ok, detail: c.status.detail } } : {}) });
80
+ }
81
+ return out;
82
+ }
83
+
84
+ const parse = (v) => { const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(v ?? '').trim()); return m ? m.slice(1, 4).map(Number) : null; };
85
+ const cmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
86
+ /** Minimal semver range test: exact, ^, ~, >=, * (enough for a dependency line); null when unreadable. */
87
+ function satisfiesRange(version, range) {
88
+ const v = parse(version);
89
+ if (!v || range == null) return null;
90
+ const r = String(range).trim();
91
+ if (r === '*' || r === 'latest' || r === '') return true;
92
+ const m = /^(\^|~|>=|=)?\s*v?(\d+\.\d+\.\d+)/.exec(r);
93
+ if (!m) return null;
94
+ const base = parse(m[2]);
95
+ if (cmp(v, base) < 0) return false;
96
+ if (m[1] === '>=') return true;
97
+ if (!m[1] || m[1] === '=') return cmp(v, base) === 0;
98
+ if (m[1] === '~') return v[0] === base[0] && v[1] === base[1];
99
+ // caret: the left-most non-zero part is fixed
100
+ if (base[0] > 0) return v[0] === base[0];
101
+ if (base[1] > 0) return v[0] === 0 && v[1] === base[1];
102
+ return v[0] === 0 && v[1] === 0 && v[2] === base[2];
103
+ }
104
+
105
+ function tryGrammarCandidates(candidates, { file, productDir, typecheck }) {
106
+ const attempts = [];
107
+ let pick = null;
108
+ for (const c of candidates) {
109
+ if (!file) { pick = c; break; }
110
+ const r = typecheck({ file, productDir, grammarRoot: c.root });
111
+ attempts.push({ source: c.source, version: c.version, root: c.root, ok: r.ok, errors: r.errors.slice(0, 20), typescript: r.typescript ?? null, ...(c.dist ? { dist: c.dist } : {}) });
112
+ if (r.ok) { pick = c; break; }
113
+ }
114
+ return { attempts, pick };
115
+ }
116
+
117
+ function upgradeOwedFor(pick, product, productRange) {
118
+ return pick?.source === 'claude-dist' ? { status: 'owed', package: GRAMMAR_PACKAGE, from: product?.version ?? null, to: pick.version, range: productRange,
119
+ inRange: satisfiesRange(pick.version, productRange), why: product ? `the product's installed ${GRAMMAR_PACKAGE}@${product.version} does not satisfy the drawing (it fails to type-check); ${pick.version} does` : `the product has no built ${GRAMMAR_PACKAGE} install` } : null;
120
+ }
121
+
122
+ function missingGrammarError(candidates, skillRoot) {
123
+ return candidates.length
124
+ ? `no grammar candidate type-checks the drawing (${candidates.map((candidate) => candidate.source + '@' + candidate.version + (candidate.dist && !candidate.dist.ok ? ' - its dist is ' + candidate.dist.state + ': ' + candidate.dist.detail + '; run npm run build in packages/grammar' : '')).join(', ')})`
125
+ : `no built ${GRAMMAR_PACKAGE} (product install, ${path.join(skillRoot, 'packages', 'grammar', 'dist')} or the main worktree's; run npm run build in packages/grammar, or pass --grammar-dist)`;
126
+ }
127
+
128
+ /**
129
+ * Resolve the grammar a draw file renders against. `typecheck` is injectable (tests). Without `file` the first
130
+ * candidate of the preference wins untested. Returns {ok, pick, grammarSource, productVersion, productRange,
131
+ * upgradeOwed, attempts:[{source, version, root, ok, errors}], error?}.
132
+ */
133
+ export function resolveDrawGrammar({ file = null, productDir, skillRoot = SKILL_ROOT, grammarDist = null, prefer = 'auto', typecheck = typecheckDraw, claudeRoots = null }) {
134
+ if (!PREFERENCES.includes(prefer)) throw new Error(`--grammar must be one of ${PREFERENCES.join('|')}`);
135
+ const all = grammarCandidates({ productDir, skillRoot, grammarDist, claudeRoots });
136
+ const product = all.find((c) => c.source === 'product') ?? null;
137
+ const productRange = productRangeOf(productDir);
138
+ const candidates = prefer === 'auto' ? all : all.filter((c) => c.source === prefer);
139
+ const { attempts, pick } = tryGrammarCandidates(candidates, { file, productDir, typecheck });
140
+ const upgradeOwed = upgradeOwedFor(pick, product, productRange);
141
+ return {
142
+ ok: Boolean(pick), pick, grammarSource: pick ? `${pick.source}@${pick.version}` : null, productVersion: product?.version ?? null, productRange, upgradeOwed, attempts,
143
+ ...(pick ? {} : { error: missingGrammarError(candidates, skillRoot) }),
144
+ };
145
+ }
146
+
147
+ /** The file a grammar subpath import resolves to inside a grammar root (package exports; `import` for JS). */
148
+ export function grammarEntry(root, subpath = '') {
149
+ const pkg = readJsonFile(path.join(root, 'package.json'));
150
+ const key = subpath ? `./${subpath.replace(/^\//, '')}` : '.';
151
+ const mapped = pkg?.exports?.[key];
152
+ const target = typeof mapped === 'string' ? mapped : mapped?.import ?? mapped?.default ?? mapped?.style ?? null;
153
+ if (target) return path.join(root, target);
154
+ if (!subpath) return path.join(root, 'dist', 'core', 'index.js');
155
+ return path.join(root, subpath);
156
+ }
157
+
158
+ async function main(argv) {
159
+ const val = (k) => { const i = argv.indexOf(k); return i >= 0 ? argv[i + 1] : null; };
160
+ const productDir = val('--product');
161
+ if (!productDir) { process.stderr.write('use: starci work draw-grammar --product <app dir> [--file <X.draw.tsx>] [--grammar auto|product|claude-dist] [--json]\n'); return 2; }
162
+ const r = resolveDrawGrammar({ file: val('--file') ? path.resolve(val('--file')) : null, productDir: path.resolve(productDir), prefer: val('--grammar') ?? 'auto', grammarDist: val('--grammar-dist') });
163
+ if (argv.includes('--json')) process.stdout.write(`${JSON.stringify(r, null, 2)}\n`);
164
+ else {
165
+ const summary = r.ok ? 'grammar ' + r.grammarSource : 'UNRESOLVED: ' + r.error;
166
+ const upgrade = r.upgradeOwed ? `; product upgrade owed ${r.upgradeOwed.from} -> ${r.upgradeOwed.to} (range ${r.upgradeOwed.range}, in range ${r.upgradeOwed.inRange})` : '';
167
+ const attempts = r.attempts.map((a) => ' ' + a.source + '@' + a.version + ': ' + (a.ok ? 'type-checks' : a.errors.length + ' error(s): ' + a.errors.slice(0, 3).map((e) => e.code + ' ' + e.message).join(' | '))).join('\n');
168
+ process.stdout.write(`${summary}${upgrade}\n${attempts}\n`);
169
+ }
170
+ return r.ok ? 0 : 1;
171
+ }
172
+
173
+ if (isMain(import.meta.url)) {
174
+ try { process.exitCode = await main(process.argv.slice(2)); } catch (e) { process.stderr.write(`draw-grammar: ${e?.stack ?? e}\n`); process.exitCode = 2; }
175
+ }
@@ -0,0 +1,188 @@
1
+ // draw-render-page.mjs - the in-page half of draw-render.mjs. A page function is serialized to a string, so it cannot
2
+ // reach the module scope: measureInPage assembles one script from the source of every function below (each is a
3
+ // top-level declaration that reads only the page's globals and its own parameters) and runs it in the page.
4
+
5
+ function measurePage({ generic, exemptSelector, layoutAttr = 'data-draw-layout' }) {
6
+ const faces = [...document.fonts].map((face) => ({ family: face.family.replace(/^(["'])(.*)\1$/, '$2'), weight: face.weight, style: face.style, status: face.status }));
7
+ const fontData = measureFonts(generic);
8
+ const de = document.documentElement;
9
+ const pageWidth = de.clientWidth;
10
+ const scrollWidth = Math.max(de.scrollWidth, document.body?.scrollWidth ?? 0);
11
+ const overflowing = measureOverflow(pageWidth, scrollWidth);
12
+ const root = document.getElementById('root');
13
+ const accentExempt = measureAccentExempt(exemptSelector);
14
+ const artwork = measureArtwork();
15
+ const anatomy = measureAnatomy();
16
+ const { ownership, dom } = measureOwnership(root, layoutAttr);
17
+ return { ownership, dom, anatomy, accentExempt, artwork, faces, stacks: [...fontData.stacks], local: fontData.local, pageWidth, innerWidth, scrollWidth, overflowing, documentHeight: de.scrollHeight,
18
+ rendered: document.documentElement.dataset.drawHarness === 'component' ? Boolean(root && root.childElementCount) : null };
19
+ }
20
+
21
+ function visible(el) {
22
+ const style = getComputedStyle(el);
23
+ return style.display !== 'none' && style.visibility !== 'hidden';
24
+ }
25
+
26
+ function measureFonts(genericFamilies) {
27
+ const stacks = new Set();
28
+ const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT);
29
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
30
+ const el = node.parentElement;
31
+ if (el && node.textContent.trim() && !['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName) && visible(el)) { stacks.add(getComputedStyle(el).fontFamily); }
32
+ }
33
+ for (const el of document.querySelectorAll('input,textarea,select,button')) {
34
+ if (visible(el)) { stacks.add(getComputedStyle(el).fontFamily); }
35
+ }
36
+ const ctx = document.createElement('canvas').getContext('2d');
37
+ const sample = 'mmmmmmmmmmlli10WQ@#';
38
+ const width = (font) => { ctx.font = `72px ${font}`; return ctx.measureText(sample).width; };
39
+ const families = new Set([...stacks].flatMap((stack) => stack.split(',').map((family) => family.trim().replace(/^(["'])(.*)\1$/, '$2'))).filter((family) => family && !genericFamilies.includes(family.toLowerCase())));
40
+ const local = [...families].filter((family) => ['monospace', 'serif', 'sans-serif'].some((base) => width(`"${family}", ${base}`) !== width(base)));
41
+ return { stacks, local };
42
+ }
43
+
44
+ function measureOverflow(width, scroll) {
45
+ if (scroll > width) {
46
+ return [...document.querySelectorAll('body *')].filter((el) => el.getBoundingClientRect().right > width + 0.5)
47
+ .slice(0, 10).map((el) => el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + (typeof el.className === 'string' && el.className ? '.' + el.className.trim().split(/\s+/).slice(0, 3).join('.') : ''));
48
+ }
49
+ return [];
50
+ }
51
+
52
+ function measureAccentExempt(exemptSelector) {
53
+ try {
54
+ return [...document.querySelectorAll(exemptSelector)].map((el) => el.getBoundingClientRect())
55
+ .filter((rect) => rect.width > 0 && rect.height > 0).map((rect) => ({ x: rect.left + scrollX, y: rect.top + scrollY, width: rect.width, height: rect.height }));
56
+ } catch { return []; }
57
+ }
58
+
59
+ function measureArtwork() {
60
+ try {
61
+ return [...document.querySelectorAll('img')].filter((el) => visible(el) && (el.currentSrc || el.src)).map((el) => {
62
+ const rect = el.getBoundingClientRect();
63
+ let x0 = rect.left, y0 = rect.top, x1 = rect.right, y1 = rect.bottom;
64
+ for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
65
+ const style = getComputedStyle(ancestor);
66
+ if (style.overflowX !== 'visible' || style.overflowY !== 'visible') {
67
+ const clip = ancestor.getBoundingClientRect();
68
+ x0 = Math.max(x0, clip.left); y0 = Math.max(y0, clip.top); x1 = Math.min(x1, clip.right); y1 = Math.min(y1, clip.bottom);
69
+ }
70
+ }
71
+ return { src: el.currentSrc || el.src, slot: el.closest('[data-artwork-slot]')?.dataset.artworkSlot ?? null,
72
+ x: x0 + scrollX, y: y0 + scrollY, width: Math.max(0, x1 - x0), height: Math.max(0, y1 - y0) };
73
+ }).filter((art) => art.width > 0 && art.height > 0);
74
+ } catch { return []; }
75
+ }
76
+
77
+ function tagOf(el) {
78
+ return el.tagName.toLowerCase() + (el.id ? '#' + el.id : '');
79
+ }
80
+
81
+ function partIn(rootEl, names) {
82
+ return [...rootEl.querySelectorAll('[data-grammar-part]')].find((part) => names.includes(part.dataset.grammarPart.trim()));
83
+ }
84
+
85
+ function measureAnatomy() {
86
+ const anatomy = { alerts: [], meters: [] };
87
+ try {
88
+ const probe = document.createElement('div');
89
+ probe.style.cssText = 'position:absolute;visibility:hidden;background-color:var(--surface)';
90
+ document.body.appendChild(probe);
91
+ const surface = getComputedStyle(probe).backgroundColor;
92
+ probe.remove();
93
+ for (const el of document.querySelectorAll('[data-grammar-component="Alert"],[data-component="Alert"]')) {
94
+ const alert = measureAlert(el, surface);
95
+ if (alert) { anatomy.alerts.push(alert); }
96
+ }
97
+ for (const el of document.querySelectorAll('[data-grammar-component="Meter"],[data-component="Meter"]')) {
98
+ const meter = measureMeter(el);
99
+ if (meter) { anatomy.meters.push(meter); }
100
+ }
101
+ } catch { /* the anatomy is advisory measurement; the static gate still runs */ }
102
+ return anatomy;
103
+ }
104
+
105
+ function measureAlert(el, surface) {
106
+ if ('grammarPart' in el.dataset) { return null; }
107
+ const indicator = partIn(el, ['alert-indicator', 'indicator']) ?? el.querySelector('.alert__indicator');
108
+ const title = partIn(el, ['alert-title', 'title']) ?? el.querySelector('.alert__title');
109
+ const box = indicator?.getBoundingClientRect();
110
+ return { desc: tagOf(el), background: getComputedStyle(el).backgroundColor, surface,
111
+ indicator: box ? { width: box.width, height: box.height } : null, tile: Boolean(el.querySelector('[data-grammar-component="IconTile"],[data-component="IconTile"]')),
112
+ indicatorColor: indicator ? getComputedStyle(indicator.querySelector('svg') ?? indicator).color : null, titleColor: title ? getComputedStyle(title).color : null };
113
+ }
114
+
115
+ function measureMeter(el) {
116
+ if ('grammarPart' in el.dataset) { return null; }
117
+ const track = partIn(el, ['meter-track', 'track']) ?? el.querySelector('.meter__track');
118
+ const band = el.parentElement;
119
+ if (!track || !band) { return null; }
120
+ const style = getComputedStyle(band);
121
+ const trackRect = track.getBoundingClientRect();
122
+ const segments = [...el.querySelectorAll('[data-grammar-part*="segment"]:not([data-grammar-part$="segments"]), .starci-core-meter-segment, [data-grammar-meter-segment]')]
123
+ .map((part) => part.getBoundingClientRect()).map((rect) => ({ x: rect.left, width: rect.width }));
124
+ const segmented = 'grammarMeterSegments' in el.dataset || 'segments' in el.dataset || segments.length > 1;
125
+ return { desc: tagOf(el), segmented, track: { width: trackRect.width, height: trackRect.height },
126
+ band: { width: band.clientWidth - Number.parseFloat(style.paddingLeft || '0') - Number.parseFloat(style.paddingRight || '0') }, segments };
127
+ }
128
+
129
+ function marked(el) {
130
+ return 'component' in el.dataset || [...el.attributes].some((attr) => attr.name.startsWith('data-grammar-')) || [...el.classList].some((name) => name.startsWith('starci-core-'));
131
+ }
132
+
133
+ function paints(el) {
134
+ const style = getComputedStyle(el);
135
+ if (style.display === 'none' || style.visibility === 'hidden') { return false; }
136
+ if (['img', 'svg', 'canvas', 'video', 'picture'].includes(el.tagName.toLowerCase())) { return true; }
137
+ if ([...el.childNodes].some((node) => node.nodeType === 3 && node.textContent.trim())) { return true; }
138
+ if (style.backgroundColor && !/^(?:transparent|rgba\(0, 0, 0, 0\))$/.test(style.backgroundColor)) { return true; }
139
+ if (style.backgroundImage && style.backgroundImage !== 'none') { return true; }
140
+ if (['Top', 'Right', 'Bottom', 'Left'].some((side) => Number.parseFloat(style[`border${side}Width`]) > 0 && style[`border${side}Style`] !== 'none' && !/rgba\(0, 0, 0, 0\)/.test(style[`border${side}Color`]))) { return true; }
141
+ return Boolean(style.boxShadow && style.boxShadow !== 'none');
142
+ }
143
+
144
+ function measureOwnership(rootEl, layoutAttribute) {
145
+ let ownership = null, dom = null;
146
+ if (document.documentElement.dataset.drawHarness !== 'component' || !rootEl) { return { ownership, dom }; }
147
+ const components = new Set();
148
+ const unowned = [];
149
+ let layoutElements = 0;
150
+ for (const el of rootEl.querySelectorAll('*')) {
151
+ if ('component' in el.dataset) { components.add(el.dataset.component); }
152
+ layoutElements += Number(el.hasAttribute(layoutAttribute));
153
+ if ((el.closest('svg') && el.tagName.toLowerCase() !== 'svg') || !paints(el)) { continue; }
154
+ const owner = ownerOf(el, rootEl, layoutAttribute);
155
+ if (owner !== 'grammar') { unowned.push(ownershipLabel(el, owner)); }
156
+ }
157
+ ownership = { components: [...components].sort((left, right) => { if (left < right) { return -1; } if (left > right) { return 1; } return 0; }), layoutElements, unownedCount: unowned.length, unowned: unowned.slice(0, 20) };
158
+ const clone = document.documentElement.cloneNode(true);
159
+ for (const el of clone.querySelectorAll('[data-component]')) {
160
+ if (!('grammarComponent' in el.dataset)) { el.dataset.grammarComponent = el.dataset.component; }
161
+ }
162
+ for (const el of clone.querySelectorAll('script')) { el.remove(); }
163
+ dom = `<!doctype html>
164
+ ${clone.outerHTML}`;
165
+ return { ownership, dom };
166
+ }
167
+
168
+ function ownerOf(el, rootEl, layoutAttribute) {
169
+ for (let ancestor = el; ancestor && ancestor !== rootEl; ancestor = ancestor.parentElement) {
170
+ if (marked(ancestor)) { return 'grammar'; }
171
+ if (ancestor.hasAttribute(layoutAttribute)) { return 'layout'; }
172
+ }
173
+ return null;
174
+ }
175
+
176
+ function ownershipLabel(el, owner) {
177
+ return el.tagName.toLowerCase() + (typeof el.className === 'string' && el.className ? '.' + el.className.trim().split(/\s+/).slice(0, 3).join('.') : '') + (el.textContent.trim() ? ' "' + el.textContent.trim().slice(0, 40) + '"' : '') + ` (${owner ?? 'no owner'})`;
178
+ }
179
+
180
+ /** The expression that declares `functions` and returns what `entry` makes of `arg` (JSON data) in the page. */
181
+ function pageScript(entry, functions, arg) {
182
+ return `(() => {\n${functions.map(String).join('\n')}\nreturn ${entry.name}(${JSON.stringify(arg)});\n})()`;
183
+ }
184
+
185
+ /** What the page measures: `arg` is {generic, exemptSelector, layoutAttr}. */
186
+ export function measureInPage(page, arg) {
187
+ return page.evaluate(pageScript(measurePage, [measurePage, visible, measureFonts, measureOverflow, measureAccentExempt, measureArtwork, tagOf, partIn, measureAnatomy, measureAlert, measureMeter, marked, paints, measureOwnership, ownerOf, ownershipLabel], arg));
188
+ }