@starci/eslint-grammar-offset-pop 0.12.13 → 0.13.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 (43) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +4 -4
  3. package/config.mjs +2 -2
  4. package/index.d.ts +4 -4
  5. package/index.mjs +1 -1
  6. package/package.json +23 -15
  7. package/runtime/engine/by-code-unit.mjs +12 -0
  8. package/runtime/engine/config.mjs +461 -0
  9. package/runtime/engine/invalid-config.mjs +15 -0
  10. package/runtime/engine/model-config.mjs +90 -0
  11. package/runtime/engine/orca-config.mjs +29 -0
  12. package/runtime/engine/release-config.mjs +28 -0
  13. package/runtime/engine/removed-vocabulary.mjs +26 -0
  14. package/runtime/engine/resources-config.mjs +52 -0
  15. package/runtime/engine/secrets.mjs +134 -0
  16. package/runtime/engine/sonar-config.mjs +21 -0
  17. package/runtime/knowledge/hfs/canon-pins.yaml +19 -20
  18. package/runtime/modules/kernel/removed-vocabulary.yaml +458 -0
  19. package/runtime/modules/models/registry.yaml +221 -0
  20. package/runtime/modules/models/runtimes.yaml +556 -0
  21. package/runtime/modules/models/tiers.yaml +66 -0
  22. package/runtime/scripts/hfs/declaration-shape.mjs +157 -0
  23. package/runtime/scripts/hfs/declaration-slots.mjs +86 -0
  24. package/runtime/scripts/hfs/edition-slots.mjs +77 -0
  25. package/runtime/scripts/hfs/manifest-shape.mjs +435 -0
  26. package/runtime/scripts/hfs/param-names.mjs +18 -0
  27. package/runtime/scripts/hfs/rule-catalog.mjs +362 -0
  28. package/runtime/scripts/hfs/rule-params-shape.mjs +80 -0
  29. package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
  30. package/runtime/scripts/hfs/slot-classify.mjs +65 -0
  31. package/runtime/scripts/hfs/slot-errors.mjs +11 -0
  32. package/runtime/scripts/hfs/slot-imports.mjs +91 -0
  33. package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
  34. package/runtime/scripts/hfs/slot-match.mjs +153 -0
  35. package/runtime/scripts/hfs/slot-path.mjs +8 -0
  36. package/runtime/scripts/hfs/slot-required.mjs +89 -0
  37. package/runtime/scripts/hfs/slot-semantic-problems.mjs +149 -0
  38. package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
  39. package/runtime/scripts/lib/i18n.mjs +85 -0
  40. package/runtime/scripts/lib/json-clone.mjs +61 -0
  41. package/runtime/scripts/lib/list.mjs +31 -0
  42. package/runtime/scripts/lib/regex.mjs +4 -0
  43. package/runtime/scripts/lib/yaml-cached.mjs +14 -0
@@ -0,0 +1,556 @@
1
+ schema: starci/runtimes@1
2
+ purpose: |-
3
+ Every runtime is a provider-window pool with real parallel slots and a cooldown: the pool boundary IS the
4
+ quota-window boundary, so one pool never spans two independently metered windows and one window never splits
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.
13
+ # `maxParallelOps` is the worker-wide ceiling across every workflow; `budgets.maxOps` in the owner's
14
+ # config.yaml is the per-workflow one. The lower of the two admits: engine/admission.mjs
15
+ # opSlotCeiling takes min(budgets.maxOps, maxParallelOps) and scripts/kernel/cli.mjs refuses
16
+ # route/dispatch with `max-ops` at it.
17
+ maxParallelOps: 20
18
+ allocation:
19
+ # Common new-admission thresholds, consumed by the pure selector and runtime reservation adapter.
20
+ admission:
21
+ schema: starci/agent-admission-policy@1
22
+ version: 1
23
+ # Age limit of evidence the runtime observes itself at admission time: owner grants and direct probes (Devin).
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
29
+ qualityOrder: [economy, standard, frontier]
30
+ ownerBiasRoles: [op, kernel, supervisor, worker, critic]
31
+ eligibilityModes: [qualified, probation, kernel-function, scoped-control-plane, operation-policy]
32
+ roles:
33
+ kernel: {qualityFloor: standard}
34
+ op: {qualityFloor: economy}
35
+ supervisor: {qualityFloor: standard}
36
+ worker: {qualityFloor: standard}
37
+ critic: {qualityFloor: standard, independence: provider}
38
+ # Heavy work fans out: a big node is cut into children with disjoint write scopes, the seam every other child
39
+ # would touch is built first and alone, then the rest run at once - bounded here so one parent never takes
40
+ # every slot of the pool and leaves the rest of the tree waiting behind it.
41
+ fanOut: {seamFirst: true, maxPerGroup: 10}
42
+ # Deterministic same-op slicing — the kernel measures the write scope and calls `starci kernel estimate`;
43
+ # it never guesses an agent count. W = sum(weight * count) in agent-minutes places the closure in
44
+ # a `size` class, and the class plus the owner's `parallel.gear` (config.yaml) names how many
45
+ # agents the op asks for. `s` and `m` are always one agent; only `l` and `xl` fan out. The static
46
+ # bounds in kinds.yaml (12 files / 8 assertions / 3 components) are a coarse screen over the same
47
+ # measure — the estimate is the authority. When N > 1 slices are admitted together the kernel
48
+ # routes them one after the other with plain starci kernel route (a Kernel --prefer/--avoid is ignored, owner
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.
51
+ # A gear never raises a pool's maxParallel, maxParallelOps or budgets.maxOps: it raises only what
52
+ # `starci kernel estimate` requests, and admission still clamps it.
53
+ slicing:
54
+ weights: {file: 4, assertion: 3, component: 12, record: 2}
55
+ targetMinutes: [15, 30]
56
+ maxSlices: 15
57
+ gears: [1, 2]
58
+ # Size classes over the same measure. A closure is `xl` when any of its counts reaches an `xl`
59
+ # bound, `l` when any reaches an `l` bound, `s` when W is inside targetMinutes[0], else `m`.
60
+ # `l.from.files: 12` is ledger-derived: of 52 settled implement/refactor/author jobs in the
61
+ # measured product ledger, closures at or above 12 authored files ran a median 46 min against 12 min below
62
+ # it, the first cutoff where the majority exceeds one targetMinutes[1] window
63
+ #. Every other `from` bound is a sketch value carried as an ASSUMPTION: that ledger holds exactly one job over three
64
+ # windows and no assertion or component measurement at all, so `xl.from` and both `assertions`
65
+ # bounds have no observation behind them and are re-derived when estimate inputs are recorded.
66
+ size:
67
+ l: {from: {files: 12, assertions: 40}, agents: {1: 3, 2: 5}}
68
+ xl: {from: {files: 40, assertions: 150}, agents: {1: 6, 2: 10}}
69
+ # The workers' operating windows, in milliseconds. engine/config.mjs allocationMs() is how code reads
70
+ # them and kernel contracts cite the key rather than restating the number, so each one is changed here
71
+ # and nowhere else.
72
+ dispatchLeaseTtlMs: 1800000
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
77
+ observeCadenceMs: 180000
78
+ # activeStaleMs: an `active` screen (spinner/"Working" row) is trusted only while the terminal still
79
+ # prints; a spinner re-renders every second, so output older than this is a frozen frame. The watchdog
80
+ # then treats the Kernel as turn-idle (reason stale-active) and wakes it, and a worker reads turn-idle
81
+ # for the nudge frontier (scripts/lib/terminal-liveness.mjs staleAwareState).
82
+ # quietMs: a worker still at its idle prompt (or frozen) this long after a delivered nudge, with no
83
+ # output since, is quiet - dead to the frontier; the watchdog settles it failed-no-report and queues
84
+ # its retry (starci kernel reconcile --dead-worker --settle-failed). A provider card's liveness.activeStaleMs /
85
+ # liveness.quietMs overrides these per provider (modules/models/agents/<provider>.yaml).
86
+ # launchGraceMs: a managed worker-start returns before Orca injects the worker's Task, so for this
87
+ # long after its op-dispatched event a managed worker that still reads an idle prompt is `starting`
88
+ # - never nudge-ready, and starci kernel nudge skips it worker-starting.
89
+ # A command-terminal dispatch has already proven its prompt submitted and carries no grace; a
90
+ # staged paste is nudge-ready at any age, and a nudge since the dispatch ends the grace.
91
+ # kernelTurnBudgetMs / supervisorTurnBudgetMs: the longest ONE busy turn of a Kernel / the [Supervisor] seat (its
92
+ # spinner timer) before the reconciler's host controller sends the agent's own interrupt key and re-wakes it with
93
+ # the decision doorbell; still the same turn turnInterruptGraceMs later, the seat is replaced (KERNEL_TURN_OVERDUE,
94
+ # scripts/reconciler/controllers/host.mjs turnStep). A busy turn defers every doorbell and notice (kernel-busy).
95
+ liveness: {activeUnclassifiedMs: 120000, activeStaleMs: 600000, quietMs: 1200000, launchGraceMs: 90000,
96
+ kernelTurnBudgetMs: 1200000, supervisorTurnBudgetMs: 1800000, turnInterruptGraceMs: 300000,
97
+ # The watchdog's replacement of a Kernel seat runs start-workflow (worktree check, install, Orca launch, attestation): bounded here, under the Host controller's
98
+ # seats.kernel.timeoutMs that kills the whole watchdog pass. A start that outlives it is killed and recorded kernel-start-failed (start-timeout), never lost silently.
99
+ kernelStartTimeoutMs: 600000}
100
+ # one local git read over a job's owned paths (scripts/kernel/owned-path-effects.mjs ownedPathEffects, job-artifacts.mjs).
101
+ settleGit: {commandMs: 15000}
102
+ # prompt-file.mjs owns terminal delivery, wake bounds, file placement and cleanup.
103
+ promptFile: {maxChars: 2000, ttlMs: 86400000}
104
+ # opVerbs: the bound of the "your verbs" block of an [Op] prompt (scripts/kernel/op-prompt-verbs.mjs): the starci calls the op's contract names,
105
+ # each with the flags the contract uses and the required ones, one line per verb. A worker that has the exact call never runs --help
106
+ # (a measured op spent 15 of its 135 turns on it). `standing` verbs are printed in full by the prompt's own reporting and logging blocks;
107
+ # `omitGroups` are the verbs of the Kernel and above, which an op's contract names only to say who runs them.
108
+ opVerbs: {maxVerbs: 20, maxChars: 3000, lineChars: 190, standing: ["kernel report", "kernel log", "kernel op-contract"],
109
+ omitGroups: [kernel, supervisor, workflow, reconciler]}
110
+ # Typed logs (scripts/kernel/typed-logs.mjs, the ledger's logs table): perJobCap rows a job's own writes
111
+ # may store before one log.truncated row closes them (derived event rows are never capped); dataMaxBytes bounds
112
+ # one row's data after redaction. diff: the caps of a job patch's pre-structured <patch>.json
113
+ # (scripts/kernel/patch-json.mjs) - lines kept per file and in total, files, characters per line, and the size of
114
+ # an image literal decoded into <patch>.assets/.
115
+ logs:
116
+ perJobCap: 2000
117
+ dataMaxBytes: 4096
118
+ diff: {fileLines: 1500, totalLines: 20000, files: 400, lineChars: 2000, assetBytes: 5242880}
119
+ # Recovery windows. hostOutage (scripts/kernel/host-outage.mjs): how long a watchdog tick or a kernel
120
+ # start waits for an Orca that does not answer, and how long after a responding Orca calls a kernel dead
121
+ # it is asked again.
122
+ hostOutage: {waitMs: 90000, deathSettleMs: 10000}
123
+ # supervisorTick: the numbers the reconciler controllers read
124
+ # (host thresholds, orca probes, statusApp port, owner digest cadence). host: above maxNode node.exe or maxGit git.exe processes the host
125
+ # 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
126
+ # probe fails the Orca app (never its terminal daemon) gets closeWaitMs to close before it is forced, is launched
127
+ # again, and restart-all runs. deadKernelMs: a watchdog whose trailing ticks read tick-failed or restart-failed
128
+ # this long flags a dead kernel. orphanedFrontierMs: a workflow read orphaned-frontier on ticks this far apart is
129
+ # flagged. noProgressMs: a STALLED workflow idle this long is flagged. statusApp: the status
130
+ # UI's ports and scheduled task — port serves the built UI and its read-only API, devPort the Vite dev/preview
131
+ # server (ui/ports.mjs is the one reader; nothing else states either) — restarted when port does not answer
132
+ # within probeTimeoutMs. alertRepeatMs: one
133
+ # alert key reaches the inbox and Telegram at most once per window. actionSlaMs: an owed action (scripts/supervisor/
134
+ # actions.mjs) no supervisor action touched this long is an SLA breach, alerted into the Supervisor's inbox.
135
+ # ownerDigestMs: the Owner Notifier's periodic owner digest goes out at most this often.
136
+ supervisorTick:
137
+ host: {maxNode: 300, maxGit: 100}
138
+ orca: {probeTimeoutMs: 30000, probes: 5, gapMs: 30000, closeWaitMs: 20000}
139
+ deadKernelMs: 900000
140
+ orphanedFrontierMs: 1800000
141
+ noProgressMs: 21600000
142
+ statusApp: {port: 4547, devPort: 4545, task: 'StarCi Harness App', probeTimeoutMs: 5000}
143
+ alertRepeatMs: 21600000
144
+ actionSlaMs: 1800000
145
+ ownerDigestMs: 7200000
146
+ # supervisorLearning (scripts/machine/lessons.mjs, supervise.yaml selfLearning): a failure signature seen
147
+ # minRepeats times opens a hypothesis; a landed experiment is measured for measureMs (its signature recurring, a new
148
+ # signature naming a file it changed, or an op success rate lower by successDrop -> revert due); at most
149
+ # dailyAutoLandCap autonomous landings in 24 h; an owner-feedback lesson weighs ownerWeight (a self-derived one 1).
150
+ supervisorLearning: {minRepeats: 2, measureMs: 21600000, dailyAutoLandCap: 8, ownerWeight: 3, successDrop: 0.1}
151
+ # progress (scripts/kernel/progress-rca.mjs; owner 2026-09-28 "could the supervisor not think before?" and "why do
152
+ # workflows not coordinate themselves but wait for the supervisor"): the outcome SLA each Kernel reads first on every wake (starci kernel status
153
+ # progress + rca) and the Supervisor backstops. units/h over windowMs (only units that passed their gates), ETA over
154
+ # etaWindowMs. A priority workflow (highest ramThrottle.priorities weight) below minUnitsPerHour.priority for graceMs,
155
+ # or holding queued-ready work while running < its allowed parallelism, is a progress stall: the Kernel acts on the
156
+ # ranked rca.actions at once; a stall older than supervisorGraceMs is the Supervisor's (progress-stall). maxUnitsPerEdit
157
+ # bounds one light graph-edit; recutThreshold open cut-shaped failures (absent paths, grants too narrow) rank a heavy
158
+ # re-cut first. rca: failures of one op in one workflow >= minFailures inside windowMs open the RCA even without a stall.
159
+ # commandTimeoutMs: the window the tool-timeout action gives an op (op-override).
160
+ progress:
161
+ windowMs: 3600000
162
+ etaWindowMs: 21600000
163
+ graceMs: 1800000
164
+ supervisorGraceMs: 3600000
165
+ minUnitsPerHour: {priority: 3, default: 0}
166
+ maxUnitsPerEdit: 3
167
+ recutThreshold: 3
168
+ commandTimeoutMs: 600000
169
+ # settleBacklog: SETTLE-FIRST - starci kernel route/dispatch refuse settle-backlog while >= max filed reports older than ageMs
170
+ # wait unconsumed (a finished [Op] keeps its terminal and slot until its settle closes it).
171
+ # The runtime settles green reports itself (settler below); this refusal counts only the reports waiting on the Kernel
172
+ # (non-green outcomes and done reports the settler handed over as needs-kernel-decision), consumed or not.
173
+ settleBacklog: {max: 3, ageMs: 120000}
174
+ rca: {minFailures: 3, windowMs: 86400000, examples: 4}
175
+ # settler (scripts/kernel/settle/job-settle.mjs; owner ruling settle-runtime-service 2026-09-28): the runtime settles every
176
+ # filed done report whose declared checks re-verify green through the same `starci kernel record-checks` + `starci kernel settle` path, from each
177
+ # workflow watchdog loop every everyMs and right after `starci kernel report`. rerunTimeoutMs bounds one declared check re-run and
178
+ # itemBudgetMs one report. invariantMaxAgeMs: the Supervisor tick asserts no filed/consumed report of a live job stays
179
+ # unsettled and un-handed-over longer than this (settle-unsettled-report, a runtime bug). releaseWindowMs: settled jobs
180
+ # this recent whose worker close is unproven are closed and verified. tail: the async settle tail (artifact indexing,
181
+ # evidence copy, session retention, input baselines, rev drift) retries a failed run after retryMs, up to maxAttempts.
182
+ settler:
183
+ everyMs: 60000
184
+ rerunTimeoutMs: 300000
185
+ itemBudgetMs: 900000
186
+ invariantMaxAgeMs: 180000
187
+ releaseWindowMs: 86400000
188
+ tail: {retryMs: 120000, maxAttempts: 5}
189
+ # redesign (starci kernel redesign; owner 2026-09-28: heavy redesign is dispatched to the op that owns it, and the Kernel stays on
190
+ # Devin): these ops, enqueued from an RCA, route as `routeAs` (implementation.plan) at
191
+ # >= minDifficulty at `effort`.
192
+ redesign:
193
+ ops: [work.author, scope.define, goal.revise]
194
+ routeAs: implementation.plan
195
+ minDifficulty: hard
196
+ effort: high
197
+ # selfReload (scripts/machine/self-reload.mjs): a watchdog loop re-execs at most once per minIntervalMs; its
198
+ # replacement takes over the loop's lock within handoverMs or is stopped. The reconciler engine checks every checkMs; an engine whose
199
+ # revision still differs from the live one that long past minIntervalMs + checkMs + handoverMs is a drift row (scripts/reconciler/drift.mjs).
200
+ selfReload: {minIntervalMs: 300000, handoverMs: 30000, checkMs: 60000, driftGraceMs: 120000}
201
+ # deploy (scripts/reconciler/runtime-deploy.mjs): a deploy waits up to waitMs, polling every pollMs, for the settles, Critic runs and prepared
202
+ # decisions in flight to finish; after the engine restart it waits up to verifyMs for the new leader's fresh heartbeat on the new revision.
203
+ deploy: {waitMs: 1800000, pollMs: 5000, verifyMs: 120000}
204
+ # dispatchRefusal (scripts/kernel/dispatch-refusal-memo.mjs): a ready job whose dispatch is refused for the same cause is tried again after baseMs, then twice as long each time (the one retry budget), up to capMs; a change of
205
+ # the cause's fingerprint (runtime revision, a ruling, a resource the refusal names) tries it at once.
206
+ dispatchRefusal: {baseMs: 60000, capMs: 900000}
207
+ # footprint (scripts/guards/footprint-scan.mjs): one host-wide scan at most every everyMs; a crashed
208
+ # tick's claim lock is cleared after lockStaleMs.
209
+ footprint: {everyMs: 600000, lockStaleMs: 60000}
210
+ # jobGuard (scripts/guards/hook-install.mjs): a guard file older than ttlMs is pruned.
211
+ jobGuard: {ttlMs: 604800000}
212
+ # landGate (scripts/supervisor/land.mjs): a land waits waitMs for its turn at the gate.
213
+ landGate: {waitMs: 1800000}
214
+ # workerClose (scripts/machine/worker-close.mjs; owner rule: a finished worker is closed COMPLETELY): after worker-release and the terminal
215
+ # close the runtime waits verifyMs for every process of that terminal's shell tree to end (polled every pollMs); a survivor proven to belong
216
+ # to the tree is stopped and given stopVerifyMs to end before the finding is raised.
217
+ # 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):
218
+ # every leftoverRetryMs (doubling to leftoverRetryMaxMs) for at most leftoverRetryAttempts attempts, each failure recorded with its reason on the job.
219
+ # uatSlot (scripts/uat/slot-collect.mjs, slot-lessee.mjs): a held UAT run checks its lessee (its attempt, the process that launched it) every watchMs and ends with it;
220
+ # the Host pass ends a slot whose lessee is gone, and a slot that recorded no lessee at all once it has been held unknownHoldMs (3 hours: a UAT run is minutes).
221
+ uatSlot: {watchMs: 5000, unknownHoldMs: 10800000}
222
+ workerClose: {verifyMs: 10000, pollMs: 1000, stopVerifyMs: 5000, leftoverRetryMs: 60000, leftoverRetryMaxMs: 900000, leftoverRetryAttempts: 12}
223
+ # providerReservation (scripts/machine/provider-reservation-reap.mjs): an admission whose receipt stayed `reserved` this long never reached
224
+ # 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
225
+ # process is released once the host restarted after its last update (scripts/machine/provider-reservation-restart.mjs); restartToleranceMs is the
226
+ # margin the restart instant must clear that update by. A handle-less receipt no proof reaches opens the PROVIDER_RESERVATION_HELD clock after unknownHeldMs.
227
+ providerReservation: {reservedStaleMs: 900000, unknownHeldMs: 1800000, restartToleranceMs: 5000}
228
+ # hostLock (scripts/machine/verb-lock.mjs): a verb that finds the host lock held (npm ci, npm install) retries on the retry budget
229
+ # of scripts/lib/retry-budget.mjs (the interval doubles to retryMaxIntervalMs) until retryDeadlineMs, then refuses with every attempt named.
230
+ hostLock: {retryIntervalMs: 5000, retryMaxIntervalMs: 30000, retryDeadlineMs: 180000}
231
+ # waiterPriority (scripts/kernel/waiter-priority.mjs): a queued job that has blocked another
232
+ # workflow this long earns its Kernel one heads-up per newly waiting workflow.
233
+ waiterPriority: {blockingHeadsUpMs: 900000}
234
+ # cutSeam (scripts/kernel/seam-policy.mjs; owner ruling 2026-09-28 "a seam must not block the whole chain"): a cut
235
+ # sibling ordinal waits on its seam at most maxSiblingWaitMs (30 min), then runs on a stub and owes one
236
+ # cut-seam-reconcile once the seam lands; a published seam interface, a seam whose latest attempt
237
+ # settled failed/blocked, or recutAfterFailures failed seam attempts release it at once (and owe a re-cut).
238
+ cutSeam: {maxSiblingWaitMs: 1800000, recutAfterFailures: 2}
239
+ # opTelemetry (scripts/machine/op-metrics.mjs): op health per op and per workflow over the last windowMs; the
240
+ # supervisor tick records one supervisor-op-metrics snapshot per run and the owner digest's trend line compares the
241
+ # newest with the one closest to trendMs earlier. stuckSla: every wait a running workflow holds (starci kernel status stuck[])
242
+ # is severity warn past warnMs and critical past criticalMs - either is a Supervisor owed action; the tick alerts
243
+ # every critical one.
244
+ opTelemetry:
245
+ windowMs: 86400000
246
+ trendMs: 86400000
247
+ stuckSla:
248
+ owner-gate: {warnMs: 14400000, criticalMs: 86400000}
249
+ peer-wait: {warnMs: 3600000, criticalMs: 14400000}
250
+ dependency: {warnMs: 14400000, criticalMs: 86400000}
251
+ retry-cap: {warnMs: 1800000, criticalMs: 14400000}
252
+ deferred-settle: {warnMs: 1800000, criticalMs: 7200000}
253
+ queued-ready: {warnMs: 900000, criticalMs: 3600000}
254
+ throttled: {warnMs: 3600000, criticalMs: 14400000}
255
+ # quitAgent (scripts/kernel/quit-agent.mjs): after the agent's own quit input is typed, settle waits
256
+ # this long for the terminal to disconnect before the release/close proceeds anyway.
257
+ quitAgent: {waitMs: 6000}
258
+ # drawRender (scripts/work/draw-render.mjs): a capture waits settleMs after document.fonts.ready.
259
+ drawRender: {settleMs: 500}
260
+ # layoutRender (scripts/work/layout-render.mjs): the runtime serves the product app for one capture; readyMs is how long the dev server may take to answer
261
+ # its first request (a cold Next compile), navigateMs the page load, settleMs the wait after fonts are ready, stopMs the grace before a still-running server is killed.
262
+ # publicOriginKeys are the scaffold's public-origin variables (its README: the product app's own address and the product app the landing hands over to); each gets the render's origin.
263
+ layoutRender: {readyMs: 180000, navigateMs: 90000, settleMs: 500, stopMs: 5000, publicOriginKeys: [NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_APP_URL]}
264
+ # drawLoop (scripts/work/draw-loop.mjs, scripts/work/draw/draw-taste.mjs; owner rulings 2026-09-27): interface.draw
265
+ # draws -> shoots -> evaluates -> fixes in rounds. A loop stops when every machine metric passes and the
266
+ # independent critic's beauty is at least beautyMin, after maxRounds, or after stallRounds rounds in a row that
267
+ # neither cut the failures nor raised the beauty; the best round is kept. Taste thresholds: accentBudget is the
268
+ # largest share of the render the accent colour may fill (the brand art band excluded), bandsPerCardMax the most
269
+ # hairline bands one card splits into, badgesPerEntityMax the most badges one entity carries. critic is the fresh,
270
+ # context-free Orca worker that scores beauty and hierarchy against the product's brand.direction rubric: picked through
271
+ # the tier picker from the Critic tier (modules/models/tiers.yaml seats.critic, modules/kernel/critic.yaml) with the
272
+ # independence rule as a hard filter, then started
273
+ # through orchestration worker-start --agent <provider> --model --effort (scripts/agent/lib.mjs startAgent) on a
274
+ # 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
275
+ # critic with no worker_done within criticTimeoutMs is stopped and released (outcome timeout, no beauty). The critic is a member of ANOTHER PROVIDER than
276
+ # the drawer: the members of its tier of that provider are removed before the pick, and a tier with no other provider's member refuses the critique with
277
+ # CRITIC_NO_INDEPENDENT_MEMBER (the drawer never judges itself). The images a drawer asks of the imagegen call
278
+ # (starci work imagegen) do not make the call's model the drawer. The drawer is the op's provider, bound to its
279
+ # terminal (scripts/guards/op-context.mjs) or named by draw-loop.mjs round --drawer; a round with no independent critic has no beauty. directionPrerequisite: interface.draw needs an ACCEPTED brand.direction
280
+ # archetype for its ui record's ui.archetype - brand.mjs checkDirection evidence.ready, the owner's receipt
281
+ # (scripts/kernel/prerequisites.mjs direction-unaccepted; the Kernel enqueues brand.decide --param
282
+ # directionArchetype=<archetype> first). false switches the gate off.
283
+ drawLoop:
284
+ maxRounds: 10
285
+ stallRounds: 2
286
+ beautyMin: 8
287
+ accentBudget: 0.10
288
+ bandsPerCardMax: 3
289
+ badgesPerEntityMax: 2
290
+ criticTimeoutMs: 900000
291
+ directionPrerequisite: true
292
+ # How long the workflow that claimed foundation `shell` (the layout chain an interface.draw draws) may hold it before a
293
+ # waiting draw opens a Supervisor Decision Item (scripts/kernel/shell-foundation.mjs; owner ruling 2026-09-29 draw-from-todo).
294
+ shellFoundationStallMs: 7200000
295
+ # autopilot (scripts/kernel/autopilot-run.mjs; owner ruling 2026-09-28 autopilot-run-to-finish, modules/kernel/
296
+ # owner-rulings.yaml): "run to the finish in one go, don't stop to ask the owner - not even UX/UI review; the owner
297
+ # reviews once at the end". enabled is the default for every running workflow; workflows.<workflowId>.enabled
298
+ # overrides it for one workflow (starci kernel autopilot --workflow <id> --set on|off records a ledger override that wins
299
+ # over both). Under autopilot:
300
+ # - a draw-review or brand-direction-review ask whose machine gates pass is answered PROVISIONALLY (answeredBy
301
+ # autopilot, receipt provisional:true + gate evidence); it never becomes golden and never counts as an owner
302
+ # answer (owner-claim.mjs). Failing gates answer redraw/revise with the gate findings, at most
303
+ # redrawBudget times per record, then the leg is deferred to the final review;
304
+ # - a credential, real-money, shared-external-system or owner-only ask is deferred-to-handover: nothing is
305
+ # sent or waited on, the leg proceeds on the sandbox/stub path the deferral names, the live-proof legs that
306
+ # need it wait for the one end-of-flow credential checklist (provision.ask subject handover-credentials);
307
+ # - a retry cap or an owner-gate that is really a runtime/process issue is a supervisor-gate incident (the
308
+ # Supervisor fixes or decides the retry), supervisorExtraBudget gates per node group, then the leg is
309
+ # deferred; an unresolved supervisor-gate older than supervisorGateTimeoutMs defers what it holds once the owner was told
310
+ # (its Supervisor item reached the owner level: modules/kernel/op-incident-policy.yaml gate.deferAfter);
311
+ # - budgets caps one workflow (non-kernel jobs, token total recorded on jobs, wall time since start): past
312
+ # one, a supervisor-gate holds new dispatch until the Supervisor extends the budget.
313
+ autopilot:
314
+ enabled: true
315
+ workflows: {}
316
+ redrawBudget: 2
317
+ supervisorExtraBudget: 2
318
+ supervisorGateTimeoutMs: 21600000
319
+ budgets: {attempts: 600, tokens: 400000000, wallMs: 1209600000}
320
+ # serveAsk (scripts/kernel/ask-server.mjs): the default lifetime of a served owner-ask link
321
+ # (--ttl <ms> overrides it per serve).
322
+ serveAsk: {ttlMs: 14400000}
323
+ # workerJobs (scripts/supervisor/workers.mjs takeLeases, scripts/supervisor/supervisor-watchdog.mjs
324
+ # filedReports): a contract worker's file leases expire leaseTtlMs after it takes them, so a dead
325
+ # worker's leases free themselves; the watchdog consumes worker reports filed in the last
326
+ # reportWindowMs.
327
+ workerJobs: {leaseTtlMs: 604800000, reportWindowMs: 604800000}
328
+ # draft (scripts/kernel/clear-draft.mjs): the beat between one Ctrl+U and the re-read that judges
329
+ # the input box's answer - a TUI repaints a beat later. clearDraft defaults to it and
330
+ # wake-delivery caps its runtime-owned draft clear interval at it.
331
+ draft: {intervalMs: 300}
332
+ # housekeeping (scripts/housekeeping/housekeeping.mjs): every retention window and root the host
333
+ # cleanup reads - no literal may live in the script. tmp: top-level %TEMP% entries whose name starts
334
+ # with one of tmpPrefixes (the prefixes StarCi, its ops lanes and its spec fixtures create) and
335
+ # whose age passes tmpMaxAgeMs are removed, never %TEMP%/claude and never a dir a live process
336
+ # holds. Agent session files (codex sessions and archived_sessions, devin
337
+ # transcripts and summaries, orca's own codex-runtime-home sessions/generated_images) older than
338
+ # sessionArchiveAfterMs move under archiveRoot keeping their relative path; Claude Code project
339
+ # transcripts get the longer claudeTranscriptArchiveAfterMs. Archive files older than
340
+ # archiveMaxAgeMs are deleted outright. lanesRoot is where lane worktrees sit; a landed lane is
341
+ # removed only once its branch reflog, HEAD reflog and directory stayed unchanged for laneGraceMs
342
+ # (a fresh lane with no commit yet reads as landed to git cherry); unset, no lane is removed.
343
+ # logMaxAgeMs caps every jsonl and log under the state base (<runtime root>/.runtime) and ~/.starci.
344
+ # gitIndexLockStaleMs: a product checkout's .git/index.lock older than this, with no git process on
345
+ # that repository (one naming no repository counts as on it), is removed by
346
+ # scripts/api/git/index-lock.mjs - from the op worker's git shim before git runs, and by the
347
+ # housekeeping area gitlocks - and recorded as a supervisor-ledger git-index-lock-removed event. A
348
+ # live git holds the lock only while it writes (a commit with hooks: minutes at most), and its
349
+ # process keeps the lock regardless of age.
350
+ housekeeping:
351
+ tmpMaxAgeMs: 172800000
352
+ tmpPrefixes: [coverage-generated-wire-, work-validate-lite-, example-refs-lite-, example-lite-, coverage-inventory-, policy-, rights-, l4-, starci, starci-w2, evidence, sup-k, work-v3, scoped-lint, si-, w1-, nivo-, orca-spec,
353
+ owner-bias-cli-, admission-runtime-, agent-launch-, starci-provider-budget-, starci-provider-schema-,
354
+ codex-, deps-, direction-part-, draw-layer-, footprint-, grammar-pin-, guard-, literal-argv-, module-yaml-,
355
+ doc-language-, guidance-, hk-, hfs-, install-link-, kernel-check-, kguard-, sup-orca, land-, op-manifest-, path-key-, pathspec-, push-scan-spec-, rename-over-, sbl-, serve-ask-, sup-chat, sup-repo, sup-src, sup-wd,
356
+ work-acceptance-, work-brand-test-, work-core-test-, work-custody-test-, work-external-,
357
+ work-index-md-, work-module-spec-, example-critique-, example-derive-, example-evidence-, example-verify-,
358
+ example-work-gate-, work-artifacts-, work-consistency-, work-surfaces-,
359
+ grammar-land-, kernel-replace-close-, nm-link-, rc-job-, rc-health-, release-gate-, seat-titles-, slice-typecheck-, unit-roles-, spec-budget-,
360
+ model-hotload-, price-hotload-, owner-trust-, fs-kind-contract-, private-publication-, private-create-, private-lease-, private-partial-,
361
+ private-unknown-, private-inode-, private-bound-, private-link-, private-prefix-, check-entry-, core-tree-, runtime-public-docs-, blob-publication-,
362
+ core-debug-schema-, ledger-native-case-, sbx-spec-, runtime-artifact-, gc-archive-proof-, machine-retention-, zip-resource-,
363
+ core-seat-, core-diag-]
364
+ sessionArchiveAfterMs: 259200000
365
+ claudeTranscriptArchiveAfterMs: 604800000
366
+ archiveMaxAgeMs: 2592000000
367
+ # archiveRoot and lanesRoot are unset on purpose: no host location lives in a tracked file. The defaults are
368
+ # <starciLocalRoot>/archive (<runtime root>/.runtime/archive) and a per-user lanes directory outside the checkout (scripts/machine/home.mjs archiveRoot, lanesRoot); the owner
369
+ # relocates them with STARCI_ARCHIVE_ROOT and STARCI_LANES_ROOT or by setting these keys on the host.
370
+ laneGraceMs: 86400000
371
+ logMaxAgeMs: 1209600000
372
+ gitIndexLockStaleMs: 300000
373
+ # gc: the Supervisor's garbage collection (scripts/supervisor/gc.mjs, every tick; owner 2026-09-28 "there must be
374
+ # garbage collection"). An idle bare shell or an unregistered runtime terminal of a live workflow is closed only once it has
375
+ # been seen for gcMinAgeMs (a dispatch binds its terminal well within that); a merged lane worktree only once idle
376
+ # for gcLaneGraceMs. GC never purges an ended workflow: housekeeping does (Q6: 30 days, zipped and verified first).
377
+ gcMinAgeMs: 600000
378
+ gcLaneGraceMs: 1800000
379
+ # gc: the cadence of the Supervisor's periodic sweep (scripts/supervisor/gc.mjs; owner 2026-09-28 "every 30 minutes, not
380
+ # every tick": the tick only checks whether it is due) and the tabs it never touches (keepTitles: regexes on the tab or
381
+ # pane title; an owner's hand-opened tab with recent output is reported, not closed, allowlisted or not).
382
+ gc:
383
+ sweepMs: 1800000
384
+ # laneCap: the most lane worktrees that stay registered under the lanes root (scripts/supervisor/gc.mjs collectLanes); past it the
385
+ # longest idle clean lanes go, landed or not (an unlanded lane keeps its branch). Owner rule: worktrees never accumulate.
386
+ laneCap: 40
387
+ keepTitles: []
388
+ # reconciler (scripts/reconciler/*; DESIGN 7.4-7.8, contract modules/reconciler/reconciler.yaml): the engine polls
389
+ # every ledger's events every pollMs; the leader row lasts leaseMs and is renewed (with the heartbeat row and file)
390
+ # every renewMs; boot.mjs ensure restarts an engine whose heartbeat is older than
391
+ # heartbeatStaleMs; one starci kernel status snapshot per workflow is shared for statusCacheMs; a failing key backs off
392
+ # backoff.minMs doubling to backoff.maxMs; more than crashLoop.max engine starts in crashLoop.windowMs start it
393
+ # --safe (read-only) and push the owner one urgent Telegram (boot.mjs).
394
+ 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}
395
+ # resources: the host-floor guards the provider-health / admission path reads. These are the shipped defaults; the owner's
396
+ # config.yaml `resources` (minFreeDiskGb, minFreeDiskPct, minFreeRamPct) overrides them key by key and code holds no number of its own.
397
+ # Below the disk floor (minFreeDiskGb, or minFreeDiskPct of the drive's size when the owner sets it - the larger applies)
398
+ # `starci kernel dispatch --spawn` refuses host-resources-low until there is room again - measured on the
399
+ # 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
400
+ # (scripts/machine/ram-throttle.mjs; owner ruling 2026-09-28 "Parallelism ceiling follows real RAM: free RAM below 15%
401
+ # then reduce the number of parallel ops, avoid freezing the machine and killing ops mid-run"): at every dispatch the effective cap
402
+ # is min(maxParallelOps, running + the ops that fit in free RAM above hardFloorPct of total), counted across
403
+ # every ledger of the host, and never above maxParallelOps. minFreeRamPct is the heavy floor: below it no NEW
404
+ # heavy op (opRam class heavy: Playwright / render / test / implement) starts from a workflow below the top
405
+ # priority, light ops still start; heavy resumes only above ramThrottle.heavyResumeAbovePct (hysteresis).
406
+ # Below landSpecPauseBelowPct no heavy op starts from any workflow and the land gate's spec runs pause until
407
+ # free RAM is back above landSpecResumeAbovePct. A CPU at or above cpuHeavyStopAbove pauses heavy ops the
408
+ # same way until it falls below cpuHeavyResumeBelow. Running ops are never stopped. Owner ruling 2026-09-28
409
+ # ("15→10, 10→2.5"): the heavy floor 10% resuming at 15%, critical 2.5% resuming at 5%, the fit floor 2.5%.
410
+ resources:
411
+ minFreeDiskGb: 5
412
+ minFreeRamPct: 10
413
+ ramThrottle:
414
+ heavyResumeAbovePct: 15
415
+ landSpecPauseBelowPct: 2.5
416
+ landSpecResumeAbovePct: 5
417
+ hardFloorPct: 2.5
418
+ cpuHeavyStopAbove: 0.95
419
+ cpuHeavyResumeBelow: 0.85
420
+ # The estimate history: the newest historySamples op-ram-footprint samples (the Resource controller records them);
421
+ # a kind's measured RAM replaces its opRam prior once it has historyMinObservations observations. kernelMb:
422
+ # the RAM one running Kernel's agent is assumed to hold, subtracted from the agent trees before sharing.
423
+ historySamples: 48
424
+ historyMinObservations: 3
425
+ kernelMb: 900
426
+ # priorities: {<workflowId>: {weight, reserve}}. Weight 1 is everyone's default. A workflow below the top
427
+ # weight leaves `reserve` slots (and the RAM its queued ops need) to the higher ones and loses its heavy
428
+ # ops first under the heavy pause; the top one keeps starting heavy ops while they fit, until critical.
429
+ # The Supervisor overrides per host: starci supervisor ram-cap prioritize --workflow <id>
430
+ # --weight <n> [--reserve <slots>]. Owner priority 2026-09-28: the prioritized FE refactor workflow ran
431
+ # up to 15 parallel code.refactor slices first; the other workflows ramped up in what was left.
432
+ priorities: {}
433
+ # opRam: the per-op RAM estimate table (MB for one running op: the agent CLI plus what it runs - the
434
+ # test runner, the browser, the dev server) and its class. heavy = Playwright / render / test /
435
+ # implement; light = reading, deciding, writing records. A kind not listed takes `default`.
436
+ opRam:
437
+ default: {mb: 1000, class: light}
438
+ backend.implement: {mb: 2500, class: heavy}
439
+ interface.implement: {mb: 3000, class: heavy}
440
+ interface.draw: {mb: 3500, class: heavy}
441
+ interface.asset: {mb: 2500, class: heavy}
442
+ interface.audit: {mb: 3000, class: heavy}
443
+ brand.decide: {mb: 3000, class: heavy}
444
+ code.refactor: {mb: 2000, class: heavy}
445
+ code.fix: {mb: 2000, class: heavy}
446
+ test.author: {mb: 2500, class: heavy}
447
+ task.execute: {mb: 2000, class: heavy}
448
+ backend.scaffold: {mb: 2000, class: heavy}
449
+ interface.scaffold: {mb: 2000, class: heavy}
450
+ package.scaffold: {mb: 2000, class: heavy}
451
+ uat.verify: {mb: 3500, class: heavy}
452
+ uat.assisted.verify: {mb: 3500, class: heavy}
453
+ e2e.verify: {mb: 3500, class: heavy}
454
+ perf.verify: {mb: 3500, class: heavy}
455
+ unit.verify: {mb: 2500, class: heavy}
456
+ integration.verify: {mb: 2500, class: heavy}
457
+ security.verify: {mb: 1500, class: light}
458
+ review.verify: {mb: 1000, class: light}
459
+ runtime.operate: {mb: 1500, class: light}
460
+ release.deliver: {mb: 1500, class: light}
461
+ architecture.decide: {mb: 800, class: light}
462
+ business.decide: {mb: 800, class: light}
463
+ decision.prepare: {mb: 800, class: light}
464
+ request.analyze: {mb: 800, class: light}
465
+ scope.define: {mb: 800, class: light}
466
+ scope.finish: {mb: 800, class: light}
467
+ goal.revise: {mb: 800, class: light}
468
+ docs.author: {mb: 800, class: light}
469
+ content.generate: {mb: 800, class: light}
470
+ work.author: {mb: 800, class: light}
471
+ grammar.update: {mb: 800, class: light}
472
+ knowledge.repair: {mb: 800, class: light}
473
+ handover.review: {mb: 800, class: light}
474
+ provision.ask: {mb: 800, class: light}
475
+ uat.assisted.prepare: {mb: 1000, class: light}
476
+ workspace.manage: {mb: 1000, class: light}
477
+ # A routed-but-queued job (payload.model set, status queued) holds its pool slot only this long after its latest route
478
+ # decision (payload.routedAt, else its newest route-decided event): long enough for one fan-out's sequential route
479
+ # calls to spread across pools, short enough that a job parked behind a gate, hold, peer-wait, dependency or
480
+ # readiness loop does not starve the pool (scripts/kernel/cli.mjs poolLoadOf, shared by starci kernel route and starci kernel status).
481
+ # Running, leased and answering jobs always hold theirs. Re-routing refreshes the hold.
482
+ routeHoldMs: 900000
483
+ # worker-start is a managed launch the host refused without classifying it. Two minutes is long
484
+ # enough for the kernel to reroute the attempt to a different pool, short enough that a one-off
485
+ # host hiccup does not park a healthy pool for the rest of the leg. prompt-delivery-stalled is its
486
+ # command-terminal twin: a prompt lost on a stalled send and on its one re-delivery (scripts/agent/lib.mjs).
487
+ # quota: the fallback only - a provider whose plan reset is configured holds its quota circuit until that reset
488
+ # plus one probe interval, cleared earlier by its recovery probe.
489
+ # capacity: the provider refused to serve the model for now (an agent card's capacityExhausted row).
490
+ cooldownMs: {rate-limited: 600000, quota: 3600000, capacity: 600000, auth: 86400000, other: 300000, worker-start: 120000, prompt-delivery-stalled: 120000}
491
+ # Observations of one failure kind before its circuit opens. A kind absent here opens on the first:
492
+ # a provider answering 401 is not a flake, a single refused launch can be.
493
+ # A circuit that reopens for the same failure inside windowMs waits factor
494
+ # times longer each time, capped at capMs (scripts/kernel/cli.mjs
495
+ # writeProviderCircuit). An un-onboarded CLI does not clear in two minutes.
496
+ circuitBackoff: {factor: 5, capMs: 3600000, windowMs: 3600000}
497
+ providerStrikes: {worker-start: 2, prompt-delivery-stalled: 2}
498
+ backoffFactor: 2
499
+ maxCooldownMs: 3600000
500
+ # The allocator's reading of each kind, from what its op manifest's goal and steps do. `role` is the
501
+ # kinds.yaml role. `work` is `think` when the output is a canonical record (SRS, SDS, scope, goal, decision,
502
+ # brand, ui, Work, workspace, rule) or a verdict about quality, and `hands-on` when the op builds, runs or
503
+ # measures under a record someone else settled. `floor` is the least difficulty the kind routes at:
504
+ # the router raises a measured difficulty to it and never lowers one; the difficulty then names the tier
505
+ # (modules/models/tiers.yaml difficulty), and a kind named under tiers.yaml kindTiers takes that tier instead.
506
+ roleOfKind:
507
+ request.analyze: {role: plan, work: think, floor: hard}
508
+ task.execute: {role: plan, work: think, floor: hard}
509
+ backend.implement: {role: implement, work: hands-on, floor: medium}
510
+ backend.scaffold: {role: implement, work: hands-on, floor: easy}
511
+ interface.implement: {role: implement, work: hands-on, floor: medium}
512
+ interface.audit: {role: verify, work: think, floor: hard}
513
+ interface.scaffold: {role: implement, work: hands-on, floor: easy}
514
+ package.scaffold: {role: implement, work: hands-on, floor: easy}
515
+ scope.define: {role: decide, work: think, floor: hard}
516
+ code.refactor: {role: implement, work: hands-on, floor: easy}
517
+ code.fix: {role: implement, work: hands-on, floor: medium}
518
+ test.author: {role: implement, work: hands-on, floor: easy}
519
+ grammar.update: {role: implement, work: hands-on, floor: hard}
520
+ architecture.revise: {role: decide, work: think, floor: hard}
521
+ business.revise: {role: decide, work: think, floor: hard}
522
+ content.generate: {role: write, work: hands-on, floor: easy}
523
+ docs.author: {role: write, work: hands-on, floor: easy}
524
+ knowledge.repair: {role: write, work: think, floor: hard}
525
+ release.deliver: {role: implement, work: hands-on, floor: hard}
526
+ scope.finish: {role: implement, work: hands-on, floor: hard}
527
+ runtime.operate: {role: implement, work: hands-on, floor: medium}
528
+ review.verify: {role: verify, work: think, floor: hard}
529
+ handover.review: {role: verify, work: think, floor: hard}
530
+ e2e.verify: {role: verify, work: hands-on, floor: medium}
531
+ integration.verify: {role: verify, work: hands-on, floor: hard}
532
+ security.verify: {role: verify, work: think, floor: hard}
533
+ perf.verify: {role: verify, work: hands-on, floor: medium}
534
+ unit.verify: {role: verify, work: hands-on, floor: medium}
535
+ uat.verify: {role: verify, work: hands-on, floor: medium}
536
+ uat.assisted.prepare: {role: write, work: hands-on, floor: medium}
537
+ uat.assisted.verify: {role: verify, work: think, floor: hard}
538
+ goal.validate: {role: verify, work: think, floor: hard}
539
+ architecture.decide: {role: decide, work: think, floor: hard}
540
+ business.decide: {role: decide, work: think, floor: hard}
541
+ goal.revise: {role: decide, work: think, floor: hard}
542
+ decision.prepare: {role: decide, work: think, floor: hard}
543
+ provision.ask: {role: decide, work: think, floor: hard}
544
+ brand.decide: {role: write, work: think, floor: hard}
545
+ interface.draw: {role: write, work: think, floor: hard}
546
+ interface.asset: {role: write, work: hands-on, floor: hard}
547
+ work.author: {role: write, work: think, floor: hard}
548
+ implementation.plan: {role: plan, work: think, floor: hard}
549
+ workspace.manage: {role: plan, work: think, floor: hard}
550
+ model.assessGoal: {role: plan, work: think, floor: hard}
551
+ model.planOp: {role: plan, work: think, floor: hard}
552
+ model.decide: {role: decide, work: think, floor: hard}
553
+ model.manageWorkflow: {role: decide, work: think, floor: hard}
554
+ model.validateOp: {role: verify, work: think, floor: hard}
555
+ model.critiqueGoal: {role: verify, work: think, floor: hard}
556
+ judge: {role: verify, work: think, floor: hard}
@@ -0,0 +1,66 @@
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
+ critic: frontier
32
+ # The kernel's own model calls (selection.yaml kernelFunctionKinds) take the tier of their seat.
33
+ kindSeats:
34
+ model.assessGoal: planner
35
+ model.planOp: planner
36
+ model.decide: kernelManager
37
+ model.manageWorkflow: kernelManager
38
+ model.validateOp: validator
39
+ model.critiqueGoal: validator
40
+ model.classifyScreen: kernelManager
41
+ model.presentOwnerQuestion: kernelManager
42
+ judge: validator
43
+ difficulty:
44
+ insane: frontier
45
+ hard: high
46
+ medium: medium
47
+ easy: low
48
+ # A kind named here takes that tier whatever its difficulty; the drawing ops take the tier of their difficulty like every core op.
49
+ kindTiers: {}
50
+ # 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.
51
+ tierUse:
52
+ imagegen: call
53
+ # Headless calls: the call tier whose chain the call is picked from (the same picker: hard filter, bias, balance, token use),
54
+ # the longest one call may run, and the most images and reference images it takes.
55
+ calls:
56
+ imagegen: {tier: imagegen, timeoutMs: 600000, maxImages: 8, maxReferences: 6}
57
+ # Balance over the recent picks of one tier, running seats included.
58
+ balance:
59
+ maxStreak: 3
60
+ maxSharePercent: 70
61
+ # Token use of the chosen member, in percent of its most used quota window. Automatic picks skip a member at
62
+ # `reservePercent`; an owner bias naming the member keeps it usable up to `biasPercent`; `exhaustedPercent` is never used.
63
+ usage:
64
+ reservePercent: 90
65
+ biasPercent: 95
66
+ exhaustedPercent: 100