@starci/hfs 4.0.10 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/emit/operations.mjs +1 -1
  3. package/emit/type-schema.mjs +2 -1
  4. package/lint/run.mjs +3 -2
  5. package/package.json +1 -1
  6. package/report/order.mjs +2 -0
  7. package/runtime/config.example.yaml +187 -0
  8. package/runtime/engine/by-code-unit.mjs +12 -0
  9. package/runtime/engine/config.mjs +221 -239
  10. package/runtime/engine/invalid-config.mjs +5 -5
  11. package/runtime/engine/model-config.mjs +90 -0
  12. package/runtime/engine/orca-config.mjs +3 -1
  13. package/runtime/engine/removed-vocabulary.mjs +28 -0
  14. package/runtime/engine/resources-config.mjs +52 -0
  15. package/runtime/engine/runtime-root.mjs +17 -0
  16. package/runtime/engine/secrets.mjs +35 -31
  17. package/runtime/engine/sonar-config.mjs +21 -0
  18. package/runtime/engine/temp-root.mjs +32 -0
  19. package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
  20. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
  21. package/runtime/knowledge/hfs/rules.yaml +98 -65
  22. package/runtime/knowledge/hfs/slots.yaml +11 -14
  23. package/runtime/knowledge/patterns/be/api.yaml +7 -24
  24. package/runtime/knowledge/patterns/be/cli.yaml +3 -12
  25. package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
  26. package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
  27. package/runtime/modules/kernel/failure-codes.yaml +1 -41
  28. package/runtime/modules/kernel/removed-vocabulary.yaml +168 -0
  29. package/runtime/modules/models/registry.yaml +25 -359
  30. package/runtime/modules/models/runtimes.yaml +92 -256
  31. package/runtime/modules/models/tiers.yaml +65 -0
  32. package/runtime/scripts/api/fs/claim-file.mjs +30 -0
  33. package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
  34. package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
  35. package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
  36. package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
  37. package/runtime/scripts/api/git/lib.mjs +34 -1
  38. package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
  39. package/runtime/scripts/api/process/run-program.mjs +2 -1
  40. package/runtime/scripts/api/sops/lib.mjs +198 -120
  41. package/runtime/scripts/hfs/allows.mjs +3 -3
  42. package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
  43. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
  44. package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
  45. package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
  46. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
  47. package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
  48. package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
  49. package/runtime/scripts/hfs/architecture/config.mjs +95 -80
  50. package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
  51. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
  52. package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
  53. package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
  54. package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
  55. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
  56. package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
  57. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
  58. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
  59. package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
  60. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
  61. package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
  62. package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
  63. package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
  64. package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
  65. package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
  66. package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
  67. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
  68. package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
  69. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  70. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
  71. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
  72. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
  73. package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
  74. package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
  75. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
  76. package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
  77. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
  78. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
  79. package/runtime/scripts/hfs/architecture/index.mjs +158 -102
  80. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
  81. package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
  82. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
  83. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
  84. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
  85. package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
  86. package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
  87. package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
  88. package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
  89. package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
  90. package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
  91. package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
  92. package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
  93. package/runtime/scripts/hfs/architecture/required-files.mjs +100 -68
  94. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
  95. package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
  96. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
  97. package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
  98. package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
  99. package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
  100. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
  101. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
  102. package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
  103. package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
  104. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
  105. package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
  106. package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
  107. package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
  108. package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
  109. package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
  110. package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
  111. package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
  112. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
  113. package/runtime/scripts/hfs/architecture.mjs +3 -4
  114. package/runtime/scripts/hfs/check.mjs +61 -70
  115. package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
  116. package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
  117. package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
  118. package/runtime/scripts/hfs/edition-slots.mjs +5 -3
  119. package/runtime/scripts/hfs/linear-text.mjs +30 -0
  120. package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
  121. package/runtime/scripts/hfs/path-findings.mjs +58 -38
  122. package/runtime/scripts/hfs/pin-findings.mjs +31 -0
  123. package/runtime/scripts/hfs/repo-identity.mjs +5 -2
  124. package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
  125. package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
  126. package/runtime/scripts/hfs/rules/cli.mjs +5 -1
  127. package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
  128. package/runtime/scripts/hfs/rules/contract.mjs +35 -27
  129. package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
  130. package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
  131. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
  132. package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
  133. package/runtime/scripts/hfs/rules/database.mjs +36 -23
  134. package/runtime/scripts/hfs/rules/deps.mjs +64 -32
  135. package/runtime/scripts/hfs/rules/docker.mjs +113 -65
  136. package/runtime/scripts/hfs/rules/edition.mjs +118 -111
  137. package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
  138. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
  139. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
  140. package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
  141. package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
  142. package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
  143. package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
  144. package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
  145. package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
  146. package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
  147. package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
  148. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
  149. package/runtime/scripts/hfs/rules/saga.mjs +128 -61
  150. package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
  151. package/runtime/scripts/hfs/rules/services.mjs +34 -16
  152. package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
  153. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
  154. package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
  155. package/runtime/scripts/hfs/secret.mjs +59 -48
  156. package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
  157. package/runtime/scripts/hfs/slot-classify.mjs +65 -0
  158. package/runtime/scripts/hfs/slot-errors.mjs +11 -0
  159. package/runtime/scripts/hfs/slot-imports.mjs +76 -0
  160. package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
  161. package/runtime/scripts/hfs/slot-match.mjs +153 -0
  162. package/runtime/scripts/hfs/slot-path.mjs +8 -0
  163. package/runtime/scripts/hfs/slot-required.mjs +76 -0
  164. package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
  165. package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
  166. package/runtime/scripts/hfs/slots.mjs +72 -693
  167. package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
  168. package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
  169. package/runtime/scripts/hfs/tree.mjs +19 -14
  170. package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
  171. package/runtime/scripts/hfs/view.mjs +1 -1
  172. package/runtime/scripts/lib/dockerfile.mjs +56 -33
  173. package/runtime/scripts/lib/event-contract.mjs +17 -13
  174. package/runtime/scripts/lib/git.mjs +2 -2
  175. package/runtime/scripts/lib/graphql-contract.mjs +158 -322
  176. package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
  177. package/runtime/scripts/lib/i18n.mjs +3 -2
  178. package/runtime/scripts/lib/in-order.mjs +71 -0
  179. package/runtime/scripts/lib/language.mjs +3 -3
  180. package/runtime/scripts/lib/list.mjs +8 -1
  181. package/runtime/scripts/lib/path-key.mjs +38 -5
  182. package/runtime/scripts/lib/pid-alive.mjs +7 -0
  183. package/runtime/scripts/lib/regex.mjs +1 -1
  184. package/runtime/scripts/lib/same-text.mjs +1 -1
  185. package/runtime/scripts/lib/secret-patterns.mjs +6 -2
  186. package/runtime/scripts/lib/sleep-sync.mjs +2 -2
  187. package/runtime/scripts/lib/sops-envelope.mjs +95 -48
  188. package/runtime/scripts/lib/stack-services.mjs +1 -2
  189. package/runtime/scripts/lib/ts-ast.mjs +9 -0
  190. package/runtime/scripts/lib/walk.mjs +6 -1
  191. package/scaffold/add-cli-lite.mjs +2 -1
  192. package/scaffold/add-table.mjs +2 -2
  193. package/scaffold/add.mjs +3 -2
  194. package/scaffold/app.mjs +4 -2
  195. package/scaffold/edition-gate.mjs +25 -25
  196. package/scaffold/lite-exports.mjs +2 -1
  197. package/scaffold/service.mjs +5 -4
  198. package/sync/hygiene.mjs +15 -9
  199. package/sync/index.mjs +7 -3
  200. package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
  201. package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
  202. package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
  203. package/templates/app/ci-workflows/sonar-steps.yml +2 -2
  204. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
  205. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  206. package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
  207. package/templates/app/starciwork.gitignore +1 -1
  208. package/templates/be/image/api/Dockerfile +1 -1
  209. package/templates/be/image/cli/Dockerfile +1 -1
  210. package/templates/be/image/worker/Dockerfile +1 -1
  211. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
  212. package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
  213. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
  214. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
  215. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
  216. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
  217. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
  218. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
  219. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
  220. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
  221. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
  222. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
  223. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
  224. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
  225. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
  226. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  227. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
  228. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
  229. package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
  230. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  231. package/templates/fe/image/next/Dockerfile +2 -1
  232. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
  233. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
  234. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
  235. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
  236. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
  237. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
  238. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
  239. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
  240. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
  241. package/upgrade/index.mjs +9 -2
  242. package/runtime/engine/admission.mjs +0 -308
  243. package/runtime/engine/canonical-json.mjs +0 -10
  244. package/runtime/engine/db/blob.mjs +0 -315
  245. package/runtime/engine/db/ledger-paths.mjs +0 -83
  246. package/runtime/engine/db/ledger.mjs +0 -1118
  247. package/runtime/engine/db/machine-connection.mjs +0 -135
  248. package/runtime/engine/db/machine-schema.mjs +0 -84
  249. package/runtime/engine/db/machine.mjs +0 -1367
  250. package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
  251. package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
  252. package/runtime/engine/db/provider-reservations.mjs +0 -101
  253. package/runtime/engine/digest.mjs +0 -16
  254. package/runtime/engine/refuse.mjs +0 -11
  255. package/runtime/modules/ops/_labels.yaml +0 -53
  256. package/runtime/scripts/api/node/lib.mjs +0 -14
  257. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  258. package/runtime/scripts/api/process/lib.mjs +0 -111
  259. package/runtime/scripts/api/process/owned-process.mjs +0 -78
  260. package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
  261. package/runtime/scripts/connectors/lib.mjs +0 -488
  262. package/runtime/scripts/lib/clip.mjs +0 -19
  263. package/runtime/scripts/lib/display-names.mjs +0 -259
  264. package/runtime/scripts/lib/example-refs.mjs +0 -158
  265. package/runtime/scripts/lib/json-schema.mjs +0 -52
  266. package/runtime/scripts/lib/process-identity.mjs +0 -6
  267. package/runtime/scripts/lib/read-yaml.mjs +0 -18
  268. package/runtime/scripts/lib/redact.mjs +0 -161
  269. package/runtime/scripts/lib/sleep.mjs +0 -7
  270. package/runtime/scripts/lib/source-phrases.mjs +0 -34
  271. package/runtime/scripts/lib/sqlite.mjs +0 -21
@@ -2,20 +2,14 @@ schema: starci/runtimes@1
2
2
  purpose: |-
3
3
  Every runtime is a provider-window pool with real parallel slots and a cooldown: the pool boundary IS the
4
4
  quota-window boundary, so one pool never spans two independently metered windows and one window never splits
5
- into two pools. The workflow-bound profile applies adaptive capacity from config.yaml: role, tier, tools,
6
- qualification, independent review, fresh provider quota, shared admitted load and recent observed service
7
- filter and score provider families before one runtime is handed to the typed launcher. The preference/tier
8
- lists below are within-family suitability and a compatibility policy for direct low-level consumers; they are
9
- not a provider fallback chain in a workflow. A runtime that fails is classified and parked for the cooldown of
10
- that failure kind, doubling per repeat. Auth health is provider-wide rather than pool-wide: a confirmed
11
- launch, worker-start or attestation auth rejection parks every runtime sharing that provider credential. A
12
- stale-token preflight may make one real refresh attempt; a typed no-effect rejection falls through on the
13
- same logical operation attempt, while partial or unknown effects require reconciliation before fallback.
14
-
15
- Models are pinned inside the pool per DIFFICULTY (`models`) and attested at launch - never inferred from the
16
- pool name. The pools themselves — membership, `provider`, `roles`, `maxParallel`, per-difficulty `models`
17
- and `effort` pins, `defaultModel` — live in modules/models/registry.yaml `pools`, the ONE model catalog;
18
- this file keeps only the operational allocation policy and runtime tuning that is not model data.
5
+ into two pools. Which model takes which work is modules/models/tiers.yaml; the pools themselves live in
6
+ modules/models/registry.yaml `pools`. This file keeps the operational allocation policy and runtime tuning that is
7
+ not model data. A runtime that fails is classified and parked for the cooldown of that failure kind, doubling per
8
+ repeat and capped (cooldownMs, circuitBackoff, maxCooldownMs: every park is time-bounded and expires by itself).
9
+ Auth health is provider-wide rather than pool-wide: a confirmed launch, worker-start or attestation auth rejection
10
+ parks every runtime sharing that provider credential. A stale provider token is not admitted (the owner
11
+ re-authenticates in Orca); a typed no-effect rejection falls through on the same logical operation attempt, while
12
+ partial or unknown effects require reconciliation before fallback.
19
13
  # `maxParallelOps` is the worker-wide ceiling across every workflow; `budgets.maxOps` in the owner's
20
14
  # config.yaml is the per-workflow one. The lower of the two admits: engine/admission.mjs
21
15
  # opSlotCeiling takes min(budgets.maxOps, maxParallelOps) and scripts/kernel/cli.mjs refuses
@@ -26,181 +20,21 @@ allocation:
26
20
  admission:
27
21
  schema: starci/agent-admission-policy@1
28
22
  version: 1
29
- reservePercent: 90
30
- exhaustedPercent: 100
23
+ # Age limit of evidence the runtime observes itself at admission time: owner grants and direct probes (Devin).
31
24
  maxAgeMs: 300000
25
+ # Age limit of provider windows the host polls (Orca account entries). Orca refreshes usage while its window is
26
+ # focused and after agent sessions end; the runtime cannot request a refresh. A launch against an exhausted window
27
+ # is refused by the provider and classified by the provider circuit. Never below maxAgeMs.
28
+ hostPolledMaxAgeMs: 86400000
32
29
  qualityOrder: [economy, standard, frontier]
33
- ownerBiasRoles: [op]
30
+ ownerBiasRoles: [op, kernel, supervisor, worker, critic]
34
31
  eligibilityModes: [qualified, probation, kernel-function, scoped-control-plane, operation-policy]
35
32
  roles:
36
33
  kernel: {qualityFloor: standard}
37
- op:
38
- qualityFloor: economy
39
- difficultyFloors: {easy: economy, medium: economy, hard: frontier, insane: frontier}
34
+ op: {qualityFloor: economy}
40
35
  supervisor: {qualityFloor: standard}
41
36
  worker: {qualityFloor: standard}
42
37
  critic: {qualityFloor: standard, independence: provider}
43
- # The default policy. config.yaml allocation.policy overrides it for `starci kernel route` (engine/config.mjs
44
- # configuredAllocationPolicy): prefer-then-overflow takes the first eligible pool of the tier order below;
45
- # balanced walks the eligible pools of that order and takes the FIRST one still below its config.yaml
46
- # allocation.shares target of the recent dispatches (scripts/agent/balance.mjs counts them; scripts/agent/
47
- # models.mjs selectPool picks), so the evidence order ranks and the share caps; when every eligible pool
48
- # is at or above its share, the one furthest below (least over) takes it and the order breaks ties. Under
49
- # both, every gate (role, difficulty pin, owner grant, host tools, quota/auth, circuit, maxParallel)
50
- # applies first.
51
- policy: prefer-then-overflow
52
- # Under balanced, a pool listed here for a work class and tier is taken only when no other pool of the
53
- # order is eligible: Opus stays hands-on overflow below insane, so its share is spent on think work; at
54
- # insane, where only Opus and Sol pin a model, the two balance.
55
- balanced:
56
- overflowOnly:
57
- hands-on:
58
- easy: [claude-agent]
59
- medium: [claude-agent]
60
- hard: [claude-agent]
61
- # Owner decision 2026-09-25 (review-hands, amending routing-by-evidence): a pool an order lists here is taken
62
- # only when no other pool of that order is eligible, under EITHER policy - neither the balanced share nor the
63
- # owner's goal routing_bias prefer moves it ahead. Opus and Sol are the review order's overflow: they take a review only when
64
- # Devin is unavailable (circuit open, quota dead, full, or excluded by the cross-family rule).
65
- overflowByOrder:
66
- review: [claude-agent, codex-agent]
67
- # Cross-family audit: a verify kind whose records overlap the output of the latest settled non-verify op of
68
- # the workflow (scripts/kernel/cli.mjs cmdRoute finds that author with scripts/agent/balance.mjs
69
- # auditAuthorOf) routes to an eligible pool of another audit family - the reviewer's provider differs from
70
- # the implementer's. The audit families are the pools of `frontier` and `hands`: Opus or Sol review what
71
- # Devin implemented; Sol reviews Opus's records and Opus reviews Sol's. The author's
72
- # own family is taken only when no pool of another family is eligible (crossFamily.applied false).
73
- # thinkAuditCrossFamily keeps its name from when only think verdicts were audited.
74
- thinkAuditCrossFamily: true
75
- # The frontier think group: Claude Opus 5.5 and GPT-6.1 Sol. It is what the kernel's own model calls route
76
- # over (scripts/route/route-model.mjs decisionFlow kernel-function) and two families of the cross-family
77
- # audit above.
78
- frontier: [claude-agent, codex-agent]
79
- # The hands group: Devin (SWE-2 Max). It implements and, since the owner decision of 2026-09-25
80
- # (review-hands), reviews - the third audit family.
81
- hands: [devin-agent]
82
- # Owner decision 2026-09-25 ("ok"): route by the 72h evidence of scripts/agent/model-scorecard.mjs (1389 op jobs over the product ledgers of that window).
83
- # Those historical Sol scores describe the pre-migration model; they are not GPT-6.1 Sol qualification evidence.
84
- # Each kind walks ONE order, named by its roleOfKind `order` (default: `think` for think work, else its role),
85
- # at its difficulty tier:
86
- # implement, write, verify (hands-on implementation: backend.implement, interface.implement, code.refactor,
87
- # test.author, the hands-on verify kinds, release/scope finish): Devin first, then Codex and Claude -
88
- # Devin passed 70% of backend.implement and 68% of interface.implement over the 72h scorecard.
89
- # scaffold (scaffold, docs, content, grammar.update - small work - and every cut slice of a fan-out, which
90
- # scripts/kernel/cli.mjs cmdRoute routes on this order): Devin first - backend.scaffold passed 73% on Devin,
91
- # 43% on Codex over the 72h scorecard.
92
- # think, decide, plan (every think kind: decide, review and author ops): Claude Opus first, GPT-6.1 Sol second
93
- # and nothing else - Opus passed 72-73% of business/architecture.decide, Sol 60-64%. Think kinds route at a hard floor, where codex-agent pins Sol; the
94
- # easy and medium keys (Claude only, never Luna) exist for direct role consumers that bring no kind.
95
- # draw (interface.draw; owner ruling 2026-09-27 draw-devin-brand-claude, modules/kernel/owner-rulings.yaml):
96
- # Devin draws first (SWE-2 Max; token-render HTML + Playwright in the product checkout - its card carries
97
- # browser-dom), Codex (GPT-6.1 Sol at effort high) is the fallback. The raster regions a drawing needs are
98
- # artworkSlots for interface.asset, never painted by the drawer. Devin pins no easy or insane model, so the easy
99
- # and insane keys are Codex alone. The draw loop's independent critic stays a DIFFERENT model from the drawer
100
- # (allocation.drawLoop.critic, criticWhenDrawer).
101
- # asset (interface.asset): Codex only - the one agent with the image tool (image_gen.imagegen).
102
- # brand (brand.decide; owner ruling 2026-09-27 draw-devin-brand-claude): Claude Opus decides the brand and its
103
- # direction, GPT-6.1 Sol is the fallback when the Claude pool cannot serve; Codex is also the cross-family second
104
- # opinion on think work.
105
- # review (owner decision 2026-09-25 review-hands, amending the think order above: "codex/claude only
106
- # decide (that is, set the strategy), review must be done by the hands"): every verify kind -
107
- # review.verify, handover.review, security.verify, goal.validate, uat.assisted.verify, interface.audit and
108
- # the hands-on e2e/integration/perf/uat verify kinds - and work.author walk the review order: Devin,
109
- # then Opus and Sol as overflow only (overflowByOrder.review). The cross-family rule sends a review of
110
- # what Devin implemented to Opus or Sol. An experiment, measured in
111
- # benchmark/findings/2026-09-25-review-hands.md.
112
- # Claude and Codex keep strategy: request.analyze, scope.define, business/architecture/goal decide and
113
- # revise, decision.prepare and implementation.plan (the cut) stay on think/decide/plan (Opus, Sol); brand.decide
114
- # walks its own brand order (Opus, then Sol).
115
- # interface.audit needs the browser-dom host tool: Devin and Codex carry it, Claude does not, so the
116
- # host-tool gate passes Claude over (Devin audited it at 11% pass, 83% fail over the 72h scorecard -
117
- # the experiment re-measures it).
118
- # Devin (SWE-2-max) pins no easy or insane model, so easy hands-on work starts at Codex (Luna) and insane
119
- # hands-on work at the frontier pools; Opus stays hands-on overflow below insane.
120
- # Owner routing 2026-09-26: Opus brainstorms and decides (think/decide/plan orders); Sol draws, audits the UI
121
- # and verifies e2e/security/assisted UAT (order ui: Devin as fallback when the Codex quota circuit
122
- # opens); Devin implements, run the kernel and do the mechanical ops (provision.ask, workspace.manage,
123
- # task.execute, knowledge.repair walk implement); the kernel's own model calls walk sol-think (Sol, Opus overflow).
124
- tiers:
125
- hard:
126
- default: [devin-agent, codex-agent, claude-agent]
127
- implement: [devin-agent, codex-agent, claude-agent]
128
- write: [devin-agent, codex-agent, claude-agent]
129
- verify: [devin-agent, codex-agent, claude-agent]
130
- scaffold: [devin-agent, codex-agent, claude-agent]
131
- think: [claude-agent, codex-agent]
132
- decide: [claude-agent, codex-agent]
133
- plan: [claude-agent, codex-agent]
134
- draw: [devin-agent, codex-agent]
135
- asset: [codex-agent]
136
- brand: [claude-agent, codex-agent]
137
- review: [devin-agent, claude-agent, codex-agent]
138
- ui: [codex-agent, devin-agent]
139
- sol-think: [codex-agent, claude-agent]
140
- medium:
141
- default: [devin-agent, codex-agent, claude-agent]
142
- implement: [devin-agent, codex-agent, claude-agent]
143
- write: [devin-agent, codex-agent, claude-agent]
144
- verify: [devin-agent, codex-agent, claude-agent]
145
- scaffold: [devin-agent, codex-agent, claude-agent]
146
- think: [claude-agent]
147
- decide: [claude-agent]
148
- plan: [claude-agent]
149
- draw: [devin-agent, codex-agent]
150
- asset: [codex-agent]
151
- brand: [claude-agent, codex-agent]
152
- review: [devin-agent, claude-agent, codex-agent]
153
- ui: [codex-agent, devin-agent]
154
- sol-think: [codex-agent, claude-agent]
155
- easy:
156
- default: [codex-agent, claude-agent]
157
- implement: [codex-agent, claude-agent]
158
- write: [codex-agent, claude-agent]
159
- verify: [codex-agent, claude-agent]
160
- scaffold: [codex-agent, claude-agent]
161
- think: [claude-agent]
162
- decide: [claude-agent]
163
- plan: [claude-agent]
164
- draw: [codex-agent]
165
- asset: [codex-agent]
166
- brand: [claude-agent, codex-agent]
167
- review: [claude-agent, codex-agent]
168
- ui: [codex-agent]
169
- sol-think: [codex-agent, claude-agent]
170
- # Insane tops the ladder: only pools that declare an insane model can serve it. Devin carries no insane
171
- # pin and is ineligible here by the models gate, so it is absent rather than listed-and-rejected (an insane
172
- # review is Opus's, with Sol as overflow).
173
- insane:
174
- default: [claude-agent, codex-agent]
175
- implement: [claude-agent, codex-agent]
176
- write: [claude-agent, codex-agent]
177
- verify: [claude-agent, codex-agent]
178
- scaffold: [claude-agent, codex-agent]
179
- think: [claude-agent, codex-agent]
180
- decide: [claude-agent, codex-agent]
181
- plan: [claude-agent, codex-agent]
182
- draw: [codex-agent]
183
- asset: [codex-agent]
184
- brand: [claude-agent, codex-agent]
185
- review: [claude-agent, codex-agent]
186
- ui: [codex-agent]
187
- sol-think: [codex-agent, claude-agent]
188
- # config.yaml allocation.preferredProvider is a bias within the tier's eligible pools and never moves think
189
- # work to a pool outside `think` (scripts/route/route-model.mjs).
190
- preference:
191
- implement: [devin-agent, codex-agent, claude-agent]
192
- verify: [devin-agent, codex-agent, claude-agent]
193
- write: [devin-agent, codex-agent, claude-agent]
194
- scaffold: [devin-agent, codex-agent, claude-agent]
195
- think: [claude-agent, codex-agent]
196
- decide: [claude-agent, codex-agent]
197
- plan: [claude-agent, codex-agent]
198
- draw: [devin-agent, codex-agent]
199
- asset: [codex-agent]
200
- brand: [claude-agent, codex-agent]
201
- review: [devin-agent, claude-agent, codex-agent]
202
- ui: [codex-agent, devin-agent]
203
- sol-think: [codex-agent, claude-agent]
204
38
  # Heavy work fans out: a big node is cut into children with disjoint write scopes, the seam every other child
205
39
  # would touch is built first and alone, then the rest run at once - bounded here so one parent never takes
206
40
  # every slot of the pool and leaves the rest of the tree waiting behind it.
@@ -212,10 +46,8 @@ allocation:
212
46
  # bounds in kinds.yaml (12 files / 8 assertions / 3 components) are a coarse screen over the same
213
47
  # measure — the estimate is the authority. When N > 1 slices are admitted together the kernel
214
48
  # routes them one after the other with plain starci kernel route (a Kernel --prefer/--avoid is ignored, owner
215
- # decision 2026-09-25) so the workers spread over the lanes instead of piling onto the first pool. A
216
- # hands-on slice (payload.cut) walks the scaffold order, Devin first (owner decision 2026-09-25): each
217
- # routed slice is already counted before the next one is routed, and a pool that reaches its share
218
- # passes the next slice down the order.
49
+ # decision 2026-09-25) so the workers spread over the lanes instead of piling onto the first pool. Each
50
+ # routed slice is recorded as a pick before the next one is routed, so the balance step spreads the slices.
219
51
  # A gear never raises a pool's maxParallel, maxParallelOps or budgets.maxOps: it raises only what
220
52
  # `starci kernel estimate` requests, and admission still clamps it.
221
53
  slicing:
@@ -228,8 +60,7 @@ allocation:
228
60
  # `l.from.files: 12` is ledger-derived: of 52 settled implement/refactor/author jobs in the
229
61
  # measured product ledger, closures at or above 12 authored files ran a median 46 min against 12 min below
230
62
  # it, the first cutoff where the majority exceeds one targetMinutes[1] window
231
- # (benchmark/findings/2026-09-23-parallel-gear.md). Every other `from` bound is the
232
- # benchmark/findings/fable.md sketch value carried as an ASSUMPTION: that ledger holds exactly one job over three
63
+ #. Every other `from` bound is a sketch value carried as an ASSUMPTION: that ledger holds exactly one job over three
233
64
  # windows and no assertion or component measurement at all, so `xl.from` and both `assertions`
234
65
  # bounds have no observation behind them and are re-derived when estimate inputs are recorded.
235
66
  size:
@@ -240,6 +71,9 @@ allocation:
240
71
  # and nowhere else.
241
72
  dispatchLeaseTtlMs: 1800000
242
73
  watchdogCadenceMs: 300000
74
+ # usageEveryMs: the token meter's sweep interval (host controller duty host/usage, scripts/kernel/usage-record.mjs). An attempt that ended less
75
+ # than this ago is still being metered: the autopilot budget does not count its missing usage as unknown (scripts/kernel/autopilot-budget.mjs).
76
+ usageEveryMs: 300000
243
77
  observeCadenceMs: 180000
244
78
  # activeStaleMs: an `active` screen (spinner/"Working" row) is trusted only while the terminal still
245
79
  # prints; a spinner re-renders every second, so output older than this is a frozen frame. The watchdog
@@ -275,9 +109,8 @@ allocation:
275
109
  # start waits for an Orca that does not answer, and how long after a responding Orca calls a kernel dead
276
110
  # it is asked again.
277
111
  hostOutage: {waitMs: 90000, deathSettleMs: 10000}
278
- # supervisorTick: the numbers of the retired supervisor tick (2026-09-28) that the reconciler controllers still read
279
- # (host thresholds, orca probes, statusApp port, owner digest cadence); everyMs is unused. The tick ran every
280
- # everyMs (whole minutes: schtasks repetition). host: above maxNode node.exe or maxGit git.exe processes the host
112
+ # supervisorTick: the numbers the reconciler controllers read
113
+ # (host thresholds, orca probes, statusApp port, owner digest cadence). host: above maxNode node.exe or maxGit git.exe processes the host
281
114
  # controller logs the counts and the parents holding the most. orca: terminal-list is probed up to probes times, probeTimeoutMs each, gapMs apart; when every
282
115
  # probe fails the Orca app (never its terminal daemon) gets closeWaitMs to close before it is forced, is launched
283
116
  # again, and restart-all runs. deadKernelMs: a watchdog whose trailing ticks read tick-failed or restart-failed
@@ -290,7 +123,6 @@ allocation:
290
123
  # actions.mjs) no supervisor action touched this long is an SLA breach, alerted into the Supervisor's inbox.
291
124
  # ownerDigestMs: the Owner Notifier's periodic owner digest goes out at most this often.
292
125
  supervisorTick:
293
- everyMs: 1800000
294
126
  host: {maxNode: 300, maxGit: 100}
295
127
  orca: {probeTimeoutMs: 30000, probes: 5, gapMs: 30000, closeWaitMs: 20000}
296
128
  deadKernelMs: 900000
@@ -344,16 +176,17 @@ allocation:
344
176
  releaseWindowMs: 86400000
345
177
  tail: {retryMs: 120000, maxAttempts: 5}
346
178
  # redesign (starci kernel redesign; owner 2026-09-28: heavy redesign is dispatched to the op that owns it, and the Kernel stays on
347
- # Devin): these ops, enqueued from an RCA, route as `routeAs` (implementation.plan: the plan order - Opus, then Sol) at
348
- # >= minDifficulty at `effort` whatever their usual order (work.author walks the review order, Devin first, otherwise).
179
+ # Devin): these ops, enqueued from an RCA, route as `routeAs` (implementation.plan) at
180
+ # >= minDifficulty at `effort`.
349
181
  redesign:
350
182
  ops: [work.author, scope.define, goal.revise]
351
183
  routeAs: implementation.plan
352
184
  minDifficulty: hard
353
185
  effort: high
354
186
  # selfReload (scripts/machine/self-reload.mjs): a watchdog loop re-execs at most once per minIntervalMs; its
355
- # replacement takes over the loop's lock within handoverMs or is stopped.
356
- selfReload: {minIntervalMs: 300000, handoverMs: 30000}
187
+ # replacement takes over the loop's lock within handoverMs or is stopped. The reconciler engine checks every checkMs; an engine whose
188
+ # revision still differs from the live one that long past minIntervalMs + checkMs + handoverMs is a drift row (scripts/reconciler/drift.mjs).
189
+ selfReload: {minIntervalMs: 300000, handoverMs: 30000, checkMs: 60000, driftGraceMs: 120000}
357
190
  # footprint (scripts/guards/footprint-scan.mjs): one host-wide scan at most every everyMs; a crashed
358
191
  # tick's claim lock is cleared after lockStaleMs.
359
192
  footprint: {everyMs: 600000, lockStaleMs: 60000}
@@ -365,7 +198,17 @@ allocation:
365
198
  # workerClose (scripts/machine/worker-close.mjs; owner rule: a finished worker is closed COMPLETELY): after worker-release and the terminal
366
199
  # close the runtime waits verifyMs for every process of that terminal's shell tree to end (polled every pollMs); a survivor proven to belong
367
200
  # to the tree is stopped and given stopVerifyMs to end before the finding is raised.
368
- workerClose: {verifyMs: 10000, pollMs: 1000, stopVerifyMs: 5000}
201
+ # A finished [Worker] job whose terminal has no recorded closure is closed again by the Job controller's worker sweep (scripts/supervisor/supervisor-watchdog.mjs):
202
+ # every leftoverRetryMs (doubling to leftoverRetryMaxMs) for at most leftoverRetryAttempts attempts, each failure recorded with its reason on the job.
203
+ workerClose: {verifyMs: 10000, pollMs: 1000, stopVerifyMs: 5000, leftoverRetryMs: 60000, leftoverRetryMaxMs: 900000, leftoverRetryAttempts: 12}
204
+ # providerReservation (scripts/machine/provider-reservation-reap.mjs): an admission whose receipt stayed `reserved` this long never reached
205
+ # worker-start (the consume moves it to `launching` first), so its launcher died and the slot is released. A receipt with no terminal handle and no
206
+ # process is released once the host restarted after its last update (scripts/machine/provider-reservation-restart.mjs); restartToleranceMs is the
207
+ # margin the restart instant must clear that update by. A handle-less receipt no proof reaches opens the PROVIDER_RESERVATION_HELD clock after unknownHeldMs.
208
+ providerReservation: {reservedStaleMs: 900000, unknownHeldMs: 1800000, restartToleranceMs: 5000}
209
+ # hostLock (scripts/machine/verb-lock.mjs): a verb that finds the host lock held (npm ci, npm install) retries on the retry budget
210
+ # of scripts/lib/retry-budget.mjs (the interval doubles to retryMaxIntervalMs) until retryDeadlineMs, then refuses with every attempt named.
211
+ hostLock: {retryIntervalMs: 5000, retryMaxIntervalMs: 30000, retryDeadlineMs: 180000}
369
212
  # waiterPriority (scripts/kernel/waiter-priority.mjs): a queued job that has blocked another
370
213
  # workflow this long earns its Kernel one heads-up per newly waiting workflow.
371
214
  waiterPriority: {blockingHeadsUpMs: 900000}
@@ -404,9 +247,10 @@ allocation:
404
247
  # context-free Orca worker that scores beauty and hierarchy against the product's brand.direction rubric: started
405
248
  # through orchestration worker-start --agent <provider> --model --effort (scripts/agent/lib.mjs startAgent) on a
406
249
  # runtime worktree detached at the empty tree (draw-critic.mjs criticWorkspace; Orca refuses a bare temp dir) holding only the PNGs, the HTML and the rubric; it writes verdict.json and reports worker_done; a
407
- # critic with no worker_done within timeoutMs is stopped and released (outcome timeout, no beauty). The critic is a DIFFERENT model from the drawer (owner ruling 2026-09-27
408
- # draw-devin-brand-claude): Devin draws, Codex critiques; when the drawer (the op's provider, bound to its
409
- # terminal - scripts/guards/op-context.mjs - or draw-loop.mjs round --drawer) is the critic's provider - Codex drawing as the draw order's fallback - the round is
250
+ # critic with no worker_done within timeoutMs is stopped and released (outcome timeout, no beauty). The critic is a DIFFERENT model from the drawer: a Codex drawer (the high tier's second member)
251
+ # is judged by criticWhenDrawer.codex; for any other drawer the critic is Codex. The images a drawer asks of the imagegen call
252
+ # (starci work imagegen) do not make the call's model the drawer. When the drawer (the op's provider, bound to its
253
+ # terminal - scripts/guards/op-context.mjs - or draw-loop.mjs round --drawer) is the critic's provider the round is
410
254
  # judged by criticWhenDrawer.<drawer> instead, and a round with no independent critic has no beauty. directionPrerequisite: interface.draw needs an ACCEPTED brand.direction
411
255
  # archetype for its ui record's ui.archetype - brand.mjs checkDirection evidence.ready, the owner's receipt
412
256
  # (scripts/kernel/prerequisites.mjs direction-unaccepted; the Kernel enqueues brand.decide --param
@@ -439,7 +283,8 @@ allocation:
439
283
  # need it wait for the one end-of-flow credential checklist (provision.ask subject handover-credentials);
440
284
  # - a retry cap or an owner-gate that is really a runtime/process issue is a supervisor-gate incident (the
441
285
  # Supervisor fixes or decides the retry), supervisorExtraBudget gates per node group, then the leg is
442
- # deferred; an unresolved supervisor-gate older than supervisorGateTimeoutMs defers what it holds;
286
+ # deferred; an unresolved supervisor-gate older than supervisorGateTimeoutMs defers what it holds once the owner was told
287
+ # (its Supervisor item reached the owner level: modules/kernel/op-incident-policy.yaml gate.deferAfter);
443
288
  # - budgets caps one workflow (non-kernel jobs, token total recorded on jobs, wall time since start): past
444
289
  # one, a supervisor-gate holds new dispatch until the Supervisor extends the budget.
445
290
  autopilot:
@@ -472,7 +317,7 @@ allocation:
472
317
  # archiveMaxAgeMs are deleted outright. lanesRoot is where lane worktrees sit; a landed lane is
473
318
  # removed only once its branch reflog, HEAD reflog and directory stayed unchanged for laneGraceMs
474
319
  # (a fresh lane with no commit yet reads as landed to git cherry); unset, no lane is removed.
475
- # logMaxAgeMs caps every jsonl and log under %LOCALAPPDATA%/StarCi and ~/.starci.
320
+ # logMaxAgeMs caps every jsonl and log under the state base (<runtime root>/.runtime) and ~/.starci.
476
321
  # gitIndexLockStaleMs: a product checkout's .git/index.lock older than this, with no git process on
477
322
  # that repository (one naming no repository counts as on it), is removed by
478
323
  # scripts/api/git/index-lock.mjs - from the op worker's git shim before git runs, and by the
@@ -488,12 +333,16 @@ allocation:
488
333
  work-acceptance-, work-brand-test-, work-core-test-, work-custody-test-, work-external-,
489
334
  work-index-md-, work-module-spec-, example-critique-, example-derive-, example-evidence-, example-verify-,
490
335
  example-work-gate-, work-artifacts-, work-consistency-, work-surfaces-,
491
- grammar-land-, kernel-replace-close-, nm-link-, rc-job-, rc-health-, release-gate-, seat-titles-, slice-typecheck-, unit-roles-, spec-budget-]
336
+ grammar-land-, kernel-replace-close-, nm-link-, rc-job-, rc-health-, release-gate-, seat-titles-, slice-typecheck-, unit-roles-, spec-budget-,
337
+ model-hotload-, price-hotload-, owner-trust-, fs-kind-contract-, private-publication-, private-create-, private-lease-, private-partial-,
338
+ private-unknown-, private-inode-, private-bound-, private-link-, private-prefix-, check-entry-, core-tree-, runtime-public-docs-, blob-publication-,
339
+ core-debug-schema-, ledger-native-case-, sbx-spec-, runtime-artifact-, gc-archive-proof-, machine-retention-, zip-resource-,
340
+ core-seat-, core-diag-]
492
341
  sessionArchiveAfterMs: 259200000
493
342
  claudeTranscriptArchiveAfterMs: 604800000
494
343
  archiveMaxAgeMs: 2592000000
495
344
  # archiveRoot and lanesRoot are unset on purpose: no host location lives in a tracked file. The defaults are
496
- # <starciLocalRoot>/archive and <starciLocalRoot>/lanes (scripts/machine/home.mjs archiveRoot, lanesRoot); the owner
345
+ # <starciLocalRoot>/archive (<runtime root>/.runtime/archive) and a per-user lanes directory outside the checkout (scripts/machine/home.mjs archiveRoot, lanesRoot); the owner
497
346
  # relocates them with STARCI_ARCHIVE_ROOT and STARCI_LANES_ROOT or by setting these keys on the host.
498
347
  laneGraceMs: 86400000
499
348
  logMaxAgeMs: 1209600000
@@ -519,10 +368,12 @@ allocation:
519
368
  # heartbeatStaleMs; one starci kernel status snapshot per workflow is shared for statusCacheMs; a failing key backs off
520
369
  # backoff.minMs doubling to backoff.maxMs; more than crashLoop.max engine starts in crashLoop.windowMs start it
521
370
  # --safe (read-only) and push the owner one urgent Telegram (boot.mjs).
522
- reconciler: {pollMs: 2000, leaseMs: 30000, renewMs: 10000, heartbeatStaleMs: 60000, statusCacheMs: 20000, backoff: {minMs: 1000, maxMs: 300000}, crashLoop: {max: 3, windowMs: 1800000}}
523
- # resources: the host-floor guards the provider-health / admission path reads. Below minFreeDiskGb
371
+ reconciler: {pollMs: 2000, leaseMs: 30000, renewMs: 10000, heartbeatStaleMs: 60000, statusCacheMs: 20000, backoff: {minMs: 1000, maxMs: 300000, maxAttempts: 24}, crashLoop: {max: 3, windowMs: 1800000}, modeDriftMs: 60000}
372
+ # resources: the host-floor guards the provider-health / admission path reads. These are the shipped defaults; the owner's
373
+ # config.yaml `resources` (minFreeDiskGb, minFreeDiskPct, minFreeRamPct) overrides them key by key and code holds no number of its own.
374
+ # Below the disk floor (minFreeDiskGb, or minFreeDiskPct of the drive's size when the owner sets it - the larger applies)
524
375
  # `starci kernel dispatch --spawn` refuses host-resources-low until there is room again - measured on the
525
- # drive that holds %TEMP% and the repos. RAM goes through the RAM-aware, priority-aware throttle
376
+ # drives that hold the configured temp root (config.yaml roots.temp, else the OS temp directory) and the repos. RAM goes through the RAM-aware, priority-aware throttle
526
377
  # (scripts/machine/ram-throttle.mjs; owner ruling 2026-09-28 "Parallelism ceiling follows real RAM: free RAM below 15%
527
378
  # then reduce the number of parallel ops, avoid freezing the machine and killing ops mid-run"): at every dispatch the effective cap
528
379
  # is min(maxParallelOps, running + the ops that fit in free RAM above hardFloorPct of total), counted across
@@ -534,7 +385,7 @@ allocation:
534
385
  # same way until it falls below cpuHeavyResumeBelow. Running ops are never stopped. Owner ruling 2026-09-28
535
386
  # ("15→10, 10→2.5"): the heavy floor 10% resuming at 15%, critical 2.5% resuming at 5%, the fit floor 2.5%.
536
387
  resources:
537
- minFreeDiskGb: 20
388
+ minFreeDiskGb: 5
538
389
  minFreeRamPct: 10
539
390
  ramThrottle:
540
391
  heavyResumeAbovePct: 15
@@ -605,9 +456,6 @@ allocation:
605
456
  # readiness loop does not starve the pool (scripts/kernel/cli.mjs poolLoadOf, shared by starci kernel route and starci kernel status).
606
457
  # Running, leased and answering jobs always hold theirs. Re-routing refreshes the hold.
607
458
  routeHoldMs: 900000
608
- verifyAvoidsImplementRuntime: true
609
- onLaunchFailure: next-available-runtime
610
- tieBreak: round-robin
611
459
  # worker-start is a managed launch the host refused without classifying it. Two minutes is long
612
460
  # enough for the kernel to reroute the attempt to a different pool, short enough that a one-off
613
461
  # host hiccup does not park a healthy pool for the rest of the leg. prompt-delivery-stalled is its
@@ -629,67 +477,55 @@ allocation:
629
477
  # kinds.yaml role. `work` is `think` when the output is a canonical record (SRS, SDS, scope, goal, decision,
630
478
  # brand, ui, Work, workspace, rule) or a verdict about quality, and `hands-on` when the op builds, runs or
631
479
  # measures under a record someone else settled. `floor` is the least difficulty the kind routes at:
632
- # scripts/agent/models.mjs selectPool raises a measured difficulty to it and never lowers one. Every think
633
- # kind's floor is hard, the tier where every think pool pins a frontier model. The kernel's own model
634
- # calls (selection.yaml kernelFunctionKinds) are think work at the same floor. `order` names the allocation
635
- # tiers/preference key the kind walks when it is not the default (`think` for think work, else the role):
636
- # `scaffold` (Devin first) for scaffold, docs, content and grammar work, `draw` (Devin first, Codex Sol at effort high
637
- # the fallback) for interface.draw, `asset` (Codex only, the image tool) for interface.asset, `brand` (Opus first, Sol
638
- # the fallback) for brand.decide, `review` (Devin, Opus and Sol overflow) for the remaining verify kinds and
639
- # work.author (owner decision 2026-09-25 review-hands), `ui` (Codex first, Devin as fallback) for
640
- # interface.audit, e2e.verify, security.verify and uat.assisted.verify, `implement` for the mechanical
641
- # think ops the hands take whatever their kind role - provision.ask, workspace.manage, task.execute and
642
- # knowledge.repair - and `sol-think` (Sol first, Opus overflow) for the kernel's own model calls, verify
643
- # calls included (owner routing 2026-09-26). work.author authors Work records - the write role, as in
644
- # modules/models/kinds.yaml - so the hands pools, which carry no plan role, can take it; its cut form,
645
- # implementation.plan, stays plan work on the think order.
480
+ # the router raises a measured difficulty to it and never lowers one; the difficulty then names the tier
481
+ # (modules/models/tiers.yaml difficulty), and a kind named under tiers.yaml kindTiers takes that tier instead.
646
482
  roleOfKind:
647
483
  request.analyze: {role: plan, work: think, floor: hard}
648
- task.execute: {role: plan, work: think, floor: hard, order: implement}
484
+ task.execute: {role: plan, work: think, floor: hard}
649
485
  backend.implement: {role: implement, work: hands-on, floor: medium}
650
- backend.scaffold: {role: implement, work: hands-on, floor: easy, order: scaffold}
486
+ backend.scaffold: {role: implement, work: hands-on, floor: easy}
651
487
  interface.implement: {role: implement, work: hands-on, floor: medium}
652
- interface.audit: {role: verify, work: think, floor: hard, order: ui}
653
- interface.scaffold: {role: implement, work: hands-on, floor: easy, order: scaffold}
654
- package.scaffold: {role: implement, work: hands-on, floor: easy, order: scaffold}
488
+ interface.audit: {role: verify, work: think, floor: hard}
489
+ interface.scaffold: {role: implement, work: hands-on, floor: easy}
490
+ package.scaffold: {role: implement, work: hands-on, floor: easy}
655
491
  scope.define: {role: decide, work: think, floor: hard}
656
492
  code.refactor: {role: implement, work: hands-on, floor: easy}
657
493
  test.author: {role: implement, work: hands-on, floor: easy}
658
- grammar.update: {role: implement, work: hands-on, floor: hard, order: scaffold}
494
+ grammar.update: {role: implement, work: hands-on, floor: hard}
659
495
  architecture.revise: {role: decide, work: think, floor: hard}
660
496
  business.revise: {role: decide, work: think, floor: hard}
661
- content.generate: {role: write, work: hands-on, floor: easy, order: scaffold}
662
- docs.author: {role: write, work: hands-on, floor: easy, order: scaffold}
663
- knowledge.repair: {role: write, work: think, floor: hard, order: implement}
497
+ content.generate: {role: write, work: hands-on, floor: easy}
498
+ docs.author: {role: write, work: hands-on, floor: easy}
499
+ knowledge.repair: {role: write, work: think, floor: hard}
664
500
  release.deliver: {role: implement, work: hands-on, floor: hard}
665
501
  scope.finish: {role: implement, work: hands-on, floor: hard}
666
502
  runtime.operate: {role: implement, work: hands-on, floor: medium}
667
- review.verify: {role: verify, work: think, floor: hard, order: review}
668
- handover.review: {role: verify, work: think, floor: hard, order: review}
669
- e2e.verify: {role: verify, work: hands-on, floor: medium, order: ui}
670
- integration.verify: {role: verify, work: hands-on, floor: hard, order: review}
671
- security.verify: {role: verify, work: think, floor: hard, order: ui}
672
- perf.verify: {role: verify, work: hands-on, floor: medium, order: review}
673
- unit.verify: {role: verify, work: hands-on, floor: medium, order: review}
674
- uat.verify: {role: verify, work: hands-on, floor: medium, order: review}
503
+ review.verify: {role: verify, work: think, floor: hard}
504
+ handover.review: {role: verify, work: think, floor: hard}
505
+ e2e.verify: {role: verify, work: hands-on, floor: medium}
506
+ integration.verify: {role: verify, work: hands-on, floor: hard}
507
+ security.verify: {role: verify, work: think, floor: hard}
508
+ perf.verify: {role: verify, work: hands-on, floor: medium}
509
+ unit.verify: {role: verify, work: hands-on, floor: medium}
510
+ uat.verify: {role: verify, work: hands-on, floor: medium}
675
511
  uat.assisted.prepare: {role: write, work: hands-on, floor: medium}
676
- uat.assisted.verify: {role: verify, work: think, floor: hard, order: ui}
677
- goal.validate: {role: verify, work: think, floor: hard, order: review}
512
+ uat.assisted.verify: {role: verify, work: think, floor: hard}
513
+ goal.validate: {role: verify, work: think, floor: hard}
678
514
  architecture.decide: {role: decide, work: think, floor: hard}
679
515
  business.decide: {role: decide, work: think, floor: hard}
680
516
  goal.revise: {role: decide, work: think, floor: hard}
681
517
  decision.prepare: {role: decide, work: think, floor: hard}
682
- provision.ask: {role: decide, work: think, floor: hard, order: implement}
683
- brand.decide: {role: write, work: think, floor: hard, order: brand}
684
- interface.draw: {role: write, work: think, floor: hard, order: draw}
685
- interface.asset: {role: write, work: hands-on, floor: medium, order: asset}
686
- work.author: {role: write, work: think, floor: hard, order: review}
518
+ provision.ask: {role: decide, work: think, floor: hard}
519
+ brand.decide: {role: write, work: think, floor: hard}
520
+ interface.draw: {role: write, work: think, floor: hard}
521
+ interface.asset: {role: write, work: hands-on, floor: hard}
522
+ work.author: {role: write, work: think, floor: hard}
687
523
  implementation.plan: {role: plan, work: think, floor: hard}
688
- workspace.manage: {role: plan, work: think, floor: hard, order: implement}
689
- model.assessGoal: {role: plan, work: think, floor: hard, order: sol-think}
690
- model.planOp: {role: plan, work: think, floor: hard, order: sol-think}
691
- model.decide: {role: decide, work: think, floor: hard, order: sol-think}
692
- model.manageWorkflow: {role: decide, work: think, floor: hard, order: sol-think}
693
- model.validateOp: {role: verify, work: think, floor: hard, order: sol-think}
694
- model.critiqueGoal: {role: verify, work: think, floor: hard, order: sol-think}
695
- judge: {role: verify, work: think, floor: hard, order: sol-think}
524
+ workspace.manage: {role: plan, work: think, floor: hard}
525
+ model.assessGoal: {role: plan, work: think, floor: hard}
526
+ model.planOp: {role: plan, work: think, floor: hard}
527
+ model.decide: {role: decide, work: think, floor: hard}
528
+ model.manageWorkflow: {role: decide, work: think, floor: hard}
529
+ model.validateOp: {role: verify, work: think, floor: hard}
530
+ model.critiqueGoal: {role: verify, work: think, floor: hard}
531
+ judge: {role: verify, work: think, floor: hard}
@@ -0,0 +1,65 @@
1
+ schema: starci/model-tiers@1
2
+ purpose: >-
3
+ The ONE owner of which model takes which work. A tier is an ordered chain of members (agent + model, optional
4
+ effort); the first member with tokens is taken. Tiers are named by capability. A model may sit in several tiers.
5
+ Adding a tier or a member is a line here (or in config.yaml models.tiers) and needs no code.
6
+ tiers:
7
+ frontier:
8
+ - {agent: claude, model: claude-opus-5-5}
9
+ - {agent: codex, model: gpt-6.1-sol, effort: max}
10
+ high:
11
+ - {agent: claude, model: claude-sonnet-5-5}
12
+ - {agent: codex, model: gpt-6.1-sol, effort: high}
13
+ medium:
14
+ - {agent: devin, model: swe-2-max}
15
+ - {agent: codex, model: gpt-6.1-sol, effort: medium}
16
+ low:
17
+ - {agent: devin, model: swe-2-max}
18
+ - {agent: codex, model: gpt-6-luna, effort: low}
19
+ imagegen:
20
+ - {agent: codex, model: gpt-6.1-sol, effort: high}
21
+ # Highest first: a provider with no frontier member takes its member of the first tier that has one.
22
+ tierOrder: [frontier, high, medium, low]
23
+ # Seat -> tier. A seat is a long-lived agent role; an op takes the tier of its difficulty.
24
+ seats:
25
+ supervisor: frontier
26
+ planner: frontier
27
+ validator: frontier
28
+ kernelManager: frontier
29
+ kernel: high
30
+ worker: high
31
+ # The kernel's own model calls (selection.yaml kernelFunctionKinds) take the tier of their seat.
32
+ kindSeats:
33
+ model.assessGoal: planner
34
+ model.planOp: planner
35
+ model.decide: kernelManager
36
+ model.manageWorkflow: kernelManager
37
+ model.validateOp: validator
38
+ model.critiqueGoal: validator
39
+ model.classifyScreen: kernelManager
40
+ model.presentOwnerQuestion: kernelManager
41
+ judge: validator
42
+ difficulty:
43
+ insane: frontier
44
+ hard: high
45
+ medium: medium
46
+ easy: low
47
+ # A kind named here takes that tier whatever its difficulty; the drawing ops take the tier of their difficulty like every core op.
48
+ kindTiers: {}
49
+ # A tier no seat and no op takes: only a headless call made by one op is made on it (`calls`). A tier not named here is a seat tier.
50
+ tierUse:
51
+ imagegen: call
52
+ # Headless calls: the call tier whose chain the call is picked from (the same picker: hard filter, bias, balance, token use),
53
+ # the longest one call may run, and the most images and reference images it takes.
54
+ calls:
55
+ imagegen: {tier: imagegen, timeoutMs: 600000, maxImages: 8, maxReferences: 6}
56
+ # Balance over the recent picks of one tier, running seats included.
57
+ balance:
58
+ maxStreak: 3
59
+ maxSharePercent: 70
60
+ # Token use of the chosen member, in percent of its most used quota window. Automatic picks skip a member at
61
+ # `reservePercent`; an owner bias naming the member keeps it usable up to `biasPercent`; `exhaustedPercent` is never used.
62
+ usage:
63
+ reservePercent: 90
64
+ biasPercent: 95
65
+ exhaustedPercent: 100
@@ -0,0 +1,30 @@
1
+ // claim-file.mjs — an exclusive writer claim on one file: a lock file beside it names the claiming process, so two processes never rewrite the same file at once
2
+ // (the sealed secret writer, scripts/hfs/secret.mjs). The claim of a process that no longer exists is replaced.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { pidAlive } from '../../lib/pid-alive.mjs';
6
+
7
+ const UNREADABLE_HELD_MS = 10_000;
8
+ const holderOf = (lock) => {
9
+ try { const pid = JSON.parse(fs.readFileSync(lock, 'utf8')).pid; return Number.isInteger(pid) ? pid : null; } catch { return null; }
10
+ };
11
+ /** A lock a claimant created but has not yet written is held for a moment: only an old unreadable lock is a crashed claim. */
12
+ const freshlyCreated = (lock) => { try { return Date.now() - fs.statSync(lock).mtimeMs < UNREADABLE_HELD_MS; } catch { return false; } };
13
+
14
+ /** {ok: true, release} once this process holds the claim of `file`; {ok: false, holder} while a live process (this one included) holds it. `pid` is the claiming process. */
15
+ export function claimFile(file, { pid = process.pid } = {}) {
16
+ const lock = path.join(path.dirname(file), `.${path.basename(file)}.lock`);
17
+ fs.mkdirSync(path.dirname(file), { recursive: true });
18
+ for (let attempt = 0; attempt < 2; attempt += 1) {
19
+ try {
20
+ fs.writeFileSync(lock, JSON.stringify({ pid }), { flag: 'wx', mode: 0o600 });
21
+ return { ok: true, release: () => { if (holderOf(lock) === pid) fs.rmSync(lock, { force: true }); } };
22
+ } catch (error) {
23
+ if (error?.code !== 'EEXIST') throw error;
24
+ const holder = holderOf(lock);
25
+ if (holder !== null ? pidAlive(holder) : freshlyCreated(lock)) return { ok: false, holder };
26
+ fs.rmSync(lock, { force: true });
27
+ }
28
+ }
29
+ return { ok: false, holder: null };
30
+ }
@@ -0,0 +1,27 @@
1
+ // ensure-temp-root.mjs — the temp root exists and takes writes before anything is placed in it or a child is started with TEMP/TMP/TMPDIR pointing at it.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { tempRoot, TEMP_ROOT_ENV } from '../../../engine/temp-root.mjs';
5
+
6
+ const TEMP_ROOT_UNUSABLE = 'TEMP_ROOT_UNUSABLE';
7
+ const writable = new Set();
8
+
9
+ const unusable = (root, reason) => Object.assign(new Error(`${TEMP_ROOT_UNUSABLE}: the temp root ${root} ${reason}; set roots.temp in config.yaml (or ${TEMP_ROOT_ENV}) to a directory the runtime can create and write - it is not replaced by the operating-system temp directory`), { code: TEMP_ROOT_UNUSABLE, root });
10
+
11
+ /** Writes and removes one probe file in `root`: a directory that exists can still refuse writes. */
12
+ function probeWritable(root) {
13
+ const probe = path.join(root, `.starci-write-probe-${process.pid}`);
14
+ fs.writeFileSync(probe, '');
15
+ fs.rmSync(probe, { force: true });
16
+ }
17
+
18
+ /** The temp root of `env`, created when it is missing. Throws an Error with code TEMP_ROOT_UNUSABLE when it cannot be created or written. */
19
+ export function ensureTempRoot({ env = process.env } = {}) {
20
+ const root = tempRoot({ env });
21
+ try {
22
+ fs.mkdirSync(root, { recursive: true });
23
+ if (!writable.has(root)) probeWritable(root);
24
+ } catch (error) { throw unusable(root, `cannot be created or written (${error.code ?? error.message})`); }
25
+ writable.add(root);
26
+ return root;
27
+ }