@starci/hfs 4.7.32 → 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 +9 -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,355 @@
1
+ // git-policy.mjs — which git commands an op worker may run in a checkout it
2
+ // shares with other workflows. Pure: argv in, verdict out; scripts/guards/command-guard.mjs
3
+ // (a PreToolUse hook) applies it to the agent's shell command before it runs, and the
4
+ // reference-transaction hook git itself runs backs it (scripts/guards/hook-install.mjs).
5
+ //
6
+ // Several workflows of one product ledger build in ONE checkout on ONE branch.
7
+ // Their workers damaged each other with commands that are harmless alone:
8
+ // - a worker ran `git reset --soft HEAD~1` over a peer's landed commit
9
+ // and re-committed it under its own message;
10
+ // - a commit swept a foreign file a hook had re-staged;
11
+ // - `git stash`, `git clean -fd`, `git checkout -- <path>` and `git restore`
12
+ // discard every peer's uncommitted work under the paths they name.
13
+ // The rule (modules/ops/_common.yaml "Evidence, completion and commits",
14
+ // modules/kernel/api.yaml conventions.sharedCheckout): the shared branch is
15
+ // append-only, a worker discards and stages only its own paths, and a commit
16
+ // names its owned paths explicitly. A wrong commit is undone with `git revert`.
17
+ //
18
+ // The argv/pathspec machinery (parseGitArgv, literalPathspec, pathspecsWithinOwned,
19
+ // --pathspec-from-file handling, literalAppRouterArgv) lives in ./git-pathspec.mjs;
20
+ // it is re-exported here so existing importers keep working.
21
+ import { trimTrailingSlashes } from '../lib/path-key.mjs';
22
+ import {
23
+ parseGitArgv, pathspecsWithinOwned, parsePathspecList, takePathspecFile,
24
+ readPathspecFile, PATHSPEC_FILE, PATHSPEC_FILE_SUBS, VALUE_OPTIONS,
25
+ } from './git-pathspec.mjs';
26
+ export {
27
+ parseGitArgv, literalPathspec, pathspecsWithinOwned, parsePathspecList,
28
+ literalAppRouterArgv, pathspecListOnStdin,
29
+ } from './git-pathspec.mjs';
30
+
31
+ /** The long option refused by every git subcommand whose hooks protect the checkout. */
32
+ export const HOOK_BYPASS_FLAG = '--no-verify';
33
+ const CONFIG_BYPASS = /^core\.hookspath=/i;
34
+ // Commands whose allowed forms run repository hooks: switching hooks off for them skips the guard and the secrets
35
+ // hooks. (Every `branch` form the policy allows only reads; an allowed `stash` writes only refs/stash, which the
36
+ // history hook leaves alone.) Any other command may carry `-c core.hooksPath=...` - the Codex CLI harness does on
37
+ // every status probe.
38
+ const HOOKED_WRITES = new Set(['commit', 'merge', 'push', 'am', 'rebase', 'cherry-pick', 'revert', 'pull', 'checkout', 'switch', 'reset', 'update-ref']);
39
+ const PRIVATE_INDEX_SAFE = new Set(['add', 'rm', 'read-tree', 'write-tree', 'update-index', 'ls-files', 'diff']);
40
+
41
+ const refusal = (code, reason, remedy) => ({ allow: false, code, reason, remedy });
42
+ const ALLOW = Object.freeze({ allow: true });
43
+
44
+ // Options and pathspecs of a subcommand. Everything after a bare `--` is a
45
+ // pathspec; before it, non-option words are revisions or pathspecs (git
46
+ // itself disambiguates; the policy treats them as both where it matters).
47
+ const splitRest = (rest) => {
48
+ const dd = rest.indexOf('--');
49
+ const before = dd === -1 ? rest : rest.slice(0, dd);
50
+ const after = dd === -1 ? [] : rest.slice(dd + 1);
51
+ return {
52
+ dashDash: dd !== -1,
53
+ options: before.filter((a) => a.startsWith('-')),
54
+ words: before.filter((a) => !a.startsWith('-')),
55
+ paths: after,
56
+ };
57
+ };
58
+ const has = (options, ...names) => options.some((o) => names.some((n) => o === n || o.startsWith(`${n}=`)));
59
+ // A combined short-flag cluster (-fd, -fdx) carries every letter it names.
60
+ const hasShort = (options, letter) => options.some((o) => /^-[A-Za-z]+$/.test(o) && o.slice(1).includes(letter));
61
+ const shortFlags = (options) => options.filter((o) => !o.startsWith('--'));
62
+
63
+ // Config git reads from the environment, as `key=value` entries: GIT_CONFIG_PARAMETERS (how git hands `-c` to its
64
+ // children, sq-quoted: `'k'='v'` or `'k=v'`) and GIT_CONFIG_COUNT with GIT_CONFIG_KEY_<n>/GIT_CONFIG_VALUE_<n>.
65
+ export function envConfig(env) {
66
+ const out = [];
67
+ const params = String(env?.GIT_CONFIG_PARAMETERS ?? '');
68
+ let i = 0;
69
+ const quoted = () => {
70
+ if (params[i] !== "'") return null;
71
+ let s = '';
72
+ for (i += 1; i < params.length; i += 1) {
73
+ if (params[i] !== "'") { s += params[i]; continue; }
74
+ if (params.startsWith(String.raw`'\''`, i)) { s += "'"; i += 3; continue; }
75
+ i += 1;
76
+ return s;
77
+ }
78
+ return s;
79
+ };
80
+ while (i < params.length) {
81
+ if (/\s/.test(params[i])) { i += 1; continue; }
82
+ const key = quoted();
83
+ if (key == null) break;
84
+ if (params[i] === '=') { i += 1; out.push(`${key}=${quoted() ?? ''}`); } else out.push(key);
85
+ }
86
+ const count = Number.parseInt(env?.GIT_CONFIG_COUNT ?? '', 10);
87
+ for (let n = 0; Number.isInteger(count) && n < count && n < 1000; n += 1) {
88
+ const key = env[`GIT_CONFIG_KEY_${n}`], value = env[`GIT_CONFIG_VALUE_${n}`] ?? '';
89
+ if (key) out.push(`${key}=${value}`);
90
+ }
91
+ return out;
92
+ }
93
+
94
+ // A push names its remote, never a URL or a path, and no command-line config redirects that remote.
95
+ const REMOTE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
96
+ const REMOTE_KEY = new RegExp([
97
+ String.raw`^(remote\.`,
98
+ String.raw`.+\.(url|pushurl|receivepack)|url\.`,
99
+ String.raw`.+\.(insteadof|pushinsteadof))$`,
100
+ ].join(''), 'i');
101
+ const redirectsRemote = (entry) => REMOTE_KEY.test(String(entry).split('=')[0]);
102
+ const PUSH_VALUE_OPTIONS = new Set(['-o', '--push-option', '--repo', '--receive-pack', '--exec']);
103
+ function pushTarget(rest) {
104
+ for (let i = 0; i < rest.length; i += 1) {
105
+ const a = rest[i];
106
+ if (a === '--') return rest[i + 1] ?? null;
107
+ if (a === '--repo' && i + 1 < rest.length) return rest[i + 1];
108
+ if (a.startsWith('--repo=')) return a.slice('--repo='.length);
109
+ if (PUSH_VALUE_OPTIONS.has(a)) { i += 1; continue; }
110
+ if (!a.startsWith('-')) return a;
111
+ }
112
+ return null;
113
+ }
114
+
115
+ // `git config` writes that switch off the hooks or redirect a remote for every workflow of the checkout. Setting
116
+ // core.hooksPath to the value it already has passes: husky's install (`prepare`) does exactly that on every npm install.
117
+ const CONFIG_VALUE_OPTIONS = new Set(['-f', '--file', '--blob', '--type', '--default', '--comment', '--value']);
118
+ const CONFIG_READS = new Set(['--get', '--get-all', '--get-regexp', '--get-urlmatch', '--get-color', '--get-colorbool', '--list', '-l']);
119
+ const GUARDED_SECTION = /^(core|remote|url)(\.|$)/i;
120
+ const hooksPathOf = (v) => (v == null ? null : trimTrailingSlashes(String(v).trim().replaceAll('\\', '/')));
121
+ // The write a `git config` argv performs: {key, value?} | {section} | {edit}, or null for a read.
122
+ const configWriteOf = (sub, words, options) => {
123
+ if (['set', 'unset', 'edit', 'rename-section', 'remove-section'].includes(sub)) {
124
+ if (sub === 'set') return { key: words[1], value: words[2] };
125
+ if (sub === 'unset') return { key: words[1] };
126
+ if (sub === 'edit') return { edit: true };
127
+ return { section: words[1] };
128
+ }
129
+ if (['get', 'list'].includes(sub)) return null;
130
+ if (has(options, '--edit') || hasShort(options, 'e')) return { edit: true };
131
+ if (has(options, '--rename-section', '--remove-section')) return { section: words[0] };
132
+ if (has(options, '--unset', '--unset-all')) return { key: words[0] };
133
+ if (has(options, '--add', '--replace-all') || (words.length >= 2 && !options.some((o) => CONFIG_READS.has(o.split('=')[0])))) return { key: words[0], value: words[1] };
134
+ return null;
135
+ };
136
+ function classifyConfig(rest, currentConfig) {
137
+ const { words, options } = configWordsAndOptions(rest);
138
+ const write = configWriteOf(words[0], words, options);
139
+ return configWriteVerdict(write, currentConfig);
140
+ }
141
+ const configWordsAndOptions = (rest) => {
142
+ const words = [], options = [];
143
+ for (let i = 0; i < rest.length; i += 1) {
144
+ const a = rest[i];
145
+ if (a === '--') { words.push(...rest.slice(i + 1)); break; }
146
+ if (a.startsWith('-')) { options.push(a); if (CONFIG_VALUE_OPTIONS.has(a)) { i += 1; } continue; }
147
+ words.push(a);
148
+ }
149
+ return { words, options };
150
+ };
151
+ const configWriteVerdict = (write, currentConfig) => {
152
+ if (!write) return ALLOW;
153
+ const refused = (what) => refusal('CONFIG_GUARDED', `git config ${what} changes the hooks or the remotes of the checkout every workflow shares`,
154
+ 'leave git config alone; report a need for a different hook or remote');
155
+ if (write.edit) return refused('--edit');
156
+ if (write.section != null) return GUARDED_SECTION.test(String(write.section)) ? refused(`on section ${write.section}`) : ALLOW;
157
+ const key = String(write.key ?? '');
158
+ if (/^core\.hookspath$/i.test(key)) {
159
+ const current = hooksPathOf(currentConfig('core.hooksPath'));
160
+ const unchanged = write.value !== undefined ? hooksPathOf(write.value) === current : current == null;
161
+ return unchanged ? ALLOW : refused(key);
162
+ }
163
+ return REMOTE_KEY.test(key) ? refused(key) : ALLOW;
164
+ };
165
+
166
+ const REVERT = 'undo a wrong commit with `git revert <sha>` (a new commit); never move the shared branch back';
167
+ const OWNED_DISCARD = 'discard only your own files: `git restore --source=HEAD --staged --worktree -- <owned paths>`';
168
+
169
+ // The verdict of each path-scoped or history-writing subcommand. Every helper takes the shared
170
+ // ctx ({sub, rest, options, words, paths, dashDash, fileSpecs, config, currentConfig, scoped}) and
171
+ // returns what the old switch case returned, in the same order of checks.
172
+ const resetVerdict = ({ options, dashDash, fileSpecs, words, paths, scoped }) => {
173
+ if (has(options, '--soft', '--hard', '--mixed', '--keep', '--merge'))
174
+ return refusal('HISTORY_REWRITE', `git reset ${options.join(' ')} moves or discards the shared branch other workflows commit on`, REVERT);
175
+ if ((!dashDash && fileSpecs === null) || words.some((w) => w !== 'HEAD'))
176
+ return refusal('HISTORY_REWRITE', 'git reset without `-- <paths>` resets the whole shared index (or moves the branch)', 'unstage your own files with `git restore --staged -- <owned paths>`');
177
+ return scoped([...paths, ...(fileSpecs ?? [])], 'git reset');
178
+ };
179
+ const rebaseVerdict = ({ options }) => (has(options, '--abort', '--quit', '--show-current-patch')
180
+ ? ALLOW
181
+ : refusal('HISTORY_REWRITE', 'git rebase rewrites commits of the shared branch', REVERT));
182
+ const pullVerdict = ({ options }) => {
183
+ if (has(options, '--rebase', '-r') && !has(options, '--rebase=false', '--no-rebase'))
184
+ return refusal('HISTORY_REWRITE', 'git pull --rebase rewrites local commits, including other workflows\' unpushed commits', 'use `git pull --ff-only` (or a merge); never rebase the shared branch');
185
+ return ALLOW;
186
+ };
187
+ const commitVerdict = ({ options, dashDash, fileSpecs, words, paths, scoped, rest }) => {
188
+ if (has(options, '--amend') || options.some((o) => /^--fixup=(amend|reword):/.test(o)))
189
+ return refusal('HISTORY_REWRITE', 'git commit --amend replaces HEAD, which may be another workflow\'s commit', 'make a new commit; a wrong one is undone with `git revert`');
190
+ const shorts = shortFlags(options);
191
+ if (has(options, HOOK_BYPASS_FLAG) || hasShort(shorts, 'n'))
192
+ return refusal('HOOKS_BYPASS', `git commit ${HOOK_BYPASS_FLAG} skips the repository hooks`, 'fix what the hook reports and commit again');
193
+ if (has(options, '--all', '--include', '--interactive', '--patch') || hasShort(shorts, 'a') || hasShort(shorts, 'i'))
194
+ return refusal('COMMIT_NOT_SCOPED', 'git commit -a/--include commits whatever is staged or modified, including other workflows\' files',
195
+ 'commit with explicit owned pathspecs: `git commit -m "<msg>" -- <owned paths>`');
196
+ const specs = [...paths, ...(dashDash ? [] : words.filter((w, i) => !optionValue(rest, w, i))), ...(fileSpecs ?? [])];
197
+ if (!specs.length) {
198
+ const pathspecDetails = fileSpecs ? ` (its ${PATHSPEC_FILE} list is empty)` : '';
199
+ return refusal('COMMIT_NOT_SCOPED', `git commit without pathspecs${pathspecDetails} commits the whole shared index, including files other workflows staged`,
200
+ `commit with explicit owned pathspecs: \`git commit -m "<msg>" -- <owned paths>\` (a long list: \`git commit -m "<msg>" ${PATHSPEC_FILE}=<list>\`)`);
201
+ }
202
+ return scoped(specs, 'git commit');
203
+ };
204
+ const stashVerdict = ({ words }) => {
205
+ // lint-staged's pre-commit backup is `stash create` + `stash store`, dropped
206
+ // after a clean run: it copies, it never sweeps the worktree. push/save/pop/apply/clear do.
207
+ if (['list', 'show', 'create', 'store', 'drop'].includes(words[0])) return ALLOW;
208
+ return refusal('SHARED_WORKTREE_DISCARD', 'git stash sweeps every workflow\'s uncommitted changes out of the shared checkout', 'leave other files alone; commit or restore only your owned paths');
209
+ };
210
+ const cleanVerdict = ({ options, words, paths, scoped }) => {
211
+ if (has(options, '--dry-run') || hasShort(shortFlags(options), 'n')) return ALLOW;
212
+ if (has(options, '--force') || hasShort(shortFlags(options), 'f')) {
213
+ if (!paths.length && !words.length) return refusal('SHARED_WORKTREE_DISCARD', 'git clean -f deletes every workflow\'s untracked files', 'delete only files you created under your owned paths');
214
+ return scoped([...paths, ...words], 'git clean');
215
+ }
216
+ return ALLOW;
217
+ };
218
+ const checkoutVerdict = ({ options, dashDash, fileSpecs, paths, scoped }) => {
219
+ if (dashDash || fileSpecs) {
220
+ const specs = [...paths, ...(fileSpecs ?? [])];
221
+ if (!specs.length) return ALLOW;
222
+ return scoped(specs, 'git checkout -- <paths>');
223
+ }
224
+ if (has(options, '--help')) return ALLOW;
225
+ return refusal('SHARED_HEAD_MOVE', 'git checkout <branch|commit|path> without `--` switches the shared checkout for every workflow or discards files',
226
+ `stay on the branch; ${OWNED_DISCARD}`);
227
+ };
228
+ const switchVerdict = ({ options }) => {
229
+ if (has(options, '--help')) return ALLOW;
230
+ return refusal('SHARED_HEAD_MOVE', 'git switch moves HEAD of the checkout every workflow shares', 'stay on the current branch');
231
+ };
232
+ const restoreVerdict = ({ words, paths, fileSpecs, scoped }) => {
233
+ const specs = [...paths, ...words, ...(fileSpecs ?? [])];
234
+ if (!specs.length) return ALLOW;
235
+ // --staged alone rewrites only the shared index, the worktree form discards
236
+ // files: either way it touches only the paths it names, which must be yours.
237
+ return scoped(specs, 'git restore');
238
+ };
239
+ const addVerdict = ({ options, words, paths, fileSpecs, scoped }) => {
240
+ if (has(options, '--all', '-A', '--update', '-u') || hasShort(shortFlags(options), 'A') || hasShort(shortFlags(options), 'u'))
241
+ return refusal('COMMIT_NOT_SCOPED', 'git add -A/-u stages every workflow\'s changes', 'stage only your owned paths: `git add -- <owned paths>`');
242
+ const specs = [...paths, ...words, ...(fileSpecs ?? [])];
243
+ return specs.length ? scoped(specs, 'git add') : ALLOW;
244
+ };
245
+ const scopedSubVerdict = ({ sub, words, paths, fileSpecs, scoped }) => {
246
+ const specs = [...paths, ...words, ...(fileSpecs ?? [])];
247
+ return specs.length ? scoped(specs, `git ${sub}`) : ALLOW;
248
+ };
249
+ const branchVerdict = ({ options, words }) => {
250
+ if (has(options, '--delete', '--move', '--copy', '--force', '--set-upstream-to', '--unset-upstream') || [...'dDmMcCfu'].some((l) => hasShort(options, l)))
251
+ return refusal('HISTORY_REWRITE', `git branch ${options.join(' ')} rewrites or deletes branches of the shared repository`, 'leave branches alone; the kernel lands work on the current branch');
252
+ // in list mode the words are patterns or the values of --contains/--merged/--points-at
253
+ if (has(options, '--list', '--contains', '--no-contains', '--merged', '--no-merged', '--points-at', '--all', '--remotes') || [...'lar'].some((l) => hasShort(options, l))) return ALLOW;
254
+ return words.length ? refusal('SHARED_HEAD_MOVE', 'creating branches in the shared repository is not an op effect', 'commit on the current branch') : ALLOW;
255
+ };
256
+ const pushVerdict = ({ options, words, rest, config }) => {
257
+ if (has(options, '--force', '--force-with-lease', '--force-if-includes', '--mirror', '--delete', '--prune') || hasShort(options, 'f') || hasShort(options, 'd')
258
+ || words.some((w) => w.startsWith('+') || w.startsWith(':')))
259
+ return refusal('HISTORY_REWRITE', 'a forced or deleting push rewrites the shared remote branch', 'push fast-forward only; integrate with a merge, never a force');
260
+ if (has(options, HOOK_BYPASS_FLAG)) return refusal('HOOKS_BYPASS', `git push ${HOOK_BYPASS_FLAG} skips the pre-push gate`, 'fix what the gate reports and push again');
261
+ const target = pushTarget(rest);
262
+ if ((target != null && !REMOTE_NAME.test(target)) || config.some(redirectsRemote)) {
263
+ const targetDetails = target ? ` ${target}` : '';
264
+ return refusal('PUSH_REMOTE_NOT_CONFIGURED', `git push${targetDetails} names a URL, a path or a remote redirected on the command line`, 'push to the configured remote by name (`git push origin <branch>`)');
265
+ }
266
+ return ALLOW;
267
+ };
268
+ const remoteVerdict = ({ words }) => {
269
+ if (['add', 'set-url', 'rename', 'remove', 'rm'].includes(words[0]))
270
+ return refusal('REMOTE_REWRITE', `git remote ${words[0]} changes where every workflow of this checkout pushes`, 'push to the configured remote; report a need for another remote');
271
+ return ALLOW;
272
+ };
273
+ const refVerdict = ({ sub, options, words }) => {
274
+ if (sub === 'symbolic-ref' && words.length <= 1 && !has(options, '-d', '--delete')) return ALLOW;
275
+ if (sub === 'update-ref' && has(options, '--help')) return ALLOW;
276
+ return refusal('HISTORY_REWRITE', `git ${sub} writes refs directly`, REVERT);
277
+ };
278
+ const historyVerdict = ({ sub }) => refusal('HISTORY_REWRITE', `git ${sub} rewrites history`, REVERT);
279
+ const reflogVerdict = ({ words }) => {
280
+ if (['expire', 'delete'].includes(words[0])) return refusal('HISTORY_REWRITE', 'git reflog expire/delete destroys the recovery record', 'leave the reflog alone');
281
+ return ALLOW;
282
+ };
283
+ const worktreeVerdict = ({ words }) => {
284
+ // A Devin op worker once added its own worktree beside a live repository, junctioned the live
285
+ // node_modules into it, and `git worktree remove --force` followed the junctions and deleted 674 live files.
286
+ // An op works in the checkout it was dispatched to; it never creates, moves or removes a worktree.
287
+ if (['add', 'move', 'remove'].includes(words[0]))
288
+ return refusal('WORKTREE_NOT_OPS', `git worktree ${words[0]}: an op worker never creates, moves or removes a git worktree - it works in the checkout it was dispatched to (a private worktree with links into the live repository deleted live files)`,
289
+ 'work in your dispatched checkout; a worktree is created and removed only by the runtime worktree API (scripts/machine/worktree-git.mjs createScratchWorktree, removeScratchWorktree and safeRemoveWorktree, scripts/machine/worktree-orca.mjs createOrcaWorktree and removeOrcaWorktree) - a build or measurement that needs another revision is reported as a need (report blocked environment), never done in a worktree of your own, and never with a junction or symlink');
290
+ return ALLOW;
291
+ };
292
+ const mergeLikeVerdict = ({ sub, options }) => {
293
+ if (has(options, HOOK_BYPASS_FLAG)) return refusal('HOOKS_BYPASS', `git ${sub} ${HOOK_BYPASS_FLAG} skips the repository hooks`, `run it without ${HOOK_BYPASS_FLAG}`);
294
+ return ALLOW;
295
+ };
296
+ const SUB_VERDICTS = {
297
+ reset: resetVerdict, rebase: rebaseVerdict, pull: pullVerdict, commit: commitVerdict,
298
+ stash: stashVerdict, clean: cleanVerdict, checkout: checkoutVerdict, switch: switchVerdict,
299
+ restore: restoreVerdict, add: addVerdict, rm: scopedSubVerdict, mv: scopedSubVerdict,
300
+ branch: branchVerdict, push: pushVerdict, remote: remoteVerdict,
301
+ 'update-ref': refVerdict, 'symbolic-ref': refVerdict,
302
+ 'filter-branch': historyVerdict, 'filter-repo': historyVerdict, replace: historyVerdict,
303
+ reflog: reflogVerdict, worktree: worktreeVerdict,
304
+ merge: mergeLikeVerdict, 'cherry-pick': mergeLikeVerdict, revert: mergeLikeVerdict,
305
+ };
306
+
307
+ /**
308
+ * classifyGit(argv, {cwd, owned, top}) -> {allow} | {allow:false, code, reason, remedy}
309
+ * `owned` is the op's owned paths as absolute paths (the job guard file); when
310
+ * it is null the path-scoped rules refuse what they cannot prove owned.
311
+ */
312
+ export function classifyGit(argv, { cwd = process.cwd(), owned = null, top = null, env = process.env, stdin = null, currentConfig = () => null } = {}) {
313
+ const parsed = parseGitArgv(argv, cwd);
314
+ const { cwd: dir, sub, rest: argRest } = parsed;
315
+ const config = [...parsed.config, ...envConfig(env)];
316
+ if (config.some((c) => CONFIG_BYPASS.test(c)) && HOOKED_WRITES.has(sub))
317
+ return refusal('HOOKS_BYPASS', 'git -c core.hooksPath=... switches off the repository hooks and the history guard', 'run git without overriding core.hooksPath');
318
+ if (!sub) return ALLOW;
319
+ // A private index (GIT_INDEX_FILE) is not the shared one: staging into it touches nobody.
320
+ if (env?.GIT_INDEX_FILE && PRIVATE_INDEX_SAFE.has(sub)) return ALLOW;
321
+ // A pathspec file's entries are pathspecs like the ones named after `--`; a list the guard
322
+ // cannot read is refused, since nothing proves its paths owned.
323
+ const pathspecFile = PATHSPEC_FILE_SUBS.has(sub) ? takePathspecFile(argRest) : { rest: argRest, file: null, nul: false };
324
+ const rest = pathspecFile.rest;
325
+ let fileSpecs = null;
326
+ if (pathspecFile.file != null) {
327
+ const text = readPathspecFile(pathspecFile.file, dir, stdin);
328
+ if (text == null)
329
+ return refusal('PATHSPEC_FILE_UNREADABLE', `git ${sub} ${PATHSPEC_FILE}=${pathspecFile.file}: the guard cannot read that list, so nothing proves its paths are yours`,
330
+ `write the list (one owned path per line, relative to where you run git) to a readable file and pass ${PATHSPEC_FILE}=<file>, or name the paths after \`--\``);
331
+ fileSpecs = parsePathspecList(text, pathspecFile.nul);
332
+ }
333
+ const { dashDash, options, words, paths } = splitRest(rest);
334
+ // Path-scoped verbs fail closed: without the job's owned paths (no readable guard file) nothing proves a path yours.
335
+ const scoped = (specs, what) => {
336
+ if (owned == null) return refusal('PATH_NOT_OWNED', `${what} names paths and your owned paths are unknown (no readable guard file)`, 'report blocked environment: your guard file (STARCI_GUARD_FILE) is missing or unreadable');
337
+ const within = pathspecsWithinOwned(specs, { cwd: dir, owned, top });
338
+ return within.ok ? ALLOW : refusal('PATH_NOT_OWNED', `${what} names paths outside your owned_paths: ${within.outside.join(', ')}`,
339
+ 'name only your owned paths after `--`; files other workflows changed are theirs');
340
+ };
341
+ const ctx = { sub, rest, options, words, paths, dashDash, fileSpecs, config, currentConfig, scoped };
342
+ if (sub === 'config') return classifyConfig(rest, currentConfig);
343
+ return SUB_VERDICTS[sub]?.(ctx) ?? ALLOW;
344
+ }
345
+
346
+ function optionValue(rest, word, index) {
347
+ // index is the position within `words`; find the word's real position in rest
348
+ let seen = -1;
349
+ for (let i = 0; i < rest.length; i += 1) {
350
+ if (rest[i] === '--') break;
351
+ if (!rest[i].startsWith('-')) seen += 1;
352
+ if (seen === index && rest[i] === word) return i > 0 && VALUE_OPTIONS.has(rest[i - 1]);
353
+ }
354
+ return false;
355
+ }
@@ -0,0 +1,27 @@
1
+ // guards-root.mjs - the one home of the op guard directory (jobs/, terminals/, seats/, refusals.jsonl).
2
+ //
3
+ // The runtime's own guards live in the host state root, never in a tracked path: <starciLocalRoot>/guards
4
+ // (<runtime root>/.runtime/guards, or STARCI_LOCAL_ROOT/guards). STARCI_GUARDS_ROOT relocates THAT directory only - a
5
+ // caller that names another root (a fixture's skill root) keeps <root>/runtime/guards. The node --test preload
6
+ // (e2e/setup/isolated-temp.mjs) gives every spec process its own STARCI_GUARDS_ROOT, so two spec files that bind the
7
+ // same fake Orca handle (fake-terminal-1) never read each other's binding, and no spec writes the live runtime's guards.
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { starciLocalRoot } from '../../engine/db/machine.mjs';
11
+ import { isSpecRun } from '../lib/env.mjs';
12
+ import { tempRoot } from '../../engine/temp-root.mjs';
13
+
14
+ export const GUARDS_ROOT_ENV = 'STARCI_GUARDS_ROOT';
15
+ export const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
16
+ const key = (p) => { const r = path.resolve(p); return process.platform === 'win32' ? r.toLowerCase() : r; };
17
+
18
+ /**
19
+ * The guard directory of `skillRoot`: the runtime's own lives in the host state root, a fixture root keeps its own. A spec
20
+ * run with no STARCI_GUARDS_ROOT and no STARCI_LOCAL_ROOT uses an OS-temp directory, so a spec never writes the host's guards.
21
+ */
22
+ export function guardsRoot(skillRoot = SKILL_ROOT, env = process.env) {
23
+ if (key(skillRoot) !== key(SKILL_ROOT)) return path.join(skillRoot, 'runtime', 'guards');
24
+ if (env[GUARDS_ROOT_ENV]) return path.resolve(env[GUARDS_ROOT_ENV]);
25
+ if (isSpecRun(env) && !env.STARCI_LOCAL_ROOT) return path.join(tempRoot({ env }), 'starci-test-guards');
26
+ return path.join(starciLocalRoot(env), 'guards');
27
+ }