Capacity Profiles
The capacity-profile contract: ordered pools, all six ordering policies, burst posture, disconnect reconciliation, and the org-authors/project-consumes grant model.
A capacity profile is the org-authored, project-granted object that
declares which pools a project's sessions may land on, in what order, and
under what postures. It replaced the per-project execution route (the
preferredPoolIds / fallbackPoolIds pair you may remember from earlier
versions of this page) in a single clean-break migration: project_execution_routes
no longer exists as a table, and every route that referenced it was migrated
into a named profile.
This page documents the profile object itself - its fields, its ordering policies, and its grant model. For the six-stage decision that reads a profile at dispatch time, see Placement, Selection & Routing Vocabulary. For the CLI verbs that manage profiles, see Capacity CLI reference. For the pools a profile orders, see Capacity Pools.
The profile object
| Field | Meaning |
|---|---|
id, orgId, name, description | Identity. name is unique per org. |
| Ordered pool list | The declared candidate set and its order, stored as one row per (profile, pool, position) in a join table, not an array column - every pool id is foreign-key-validated against the org's pools on write. |
orderingPolicy | How stage 4 orders survivors - one of six values, below. Default: declared. |
preferenceVector | A cost / latency / quality posture, inherited org to project to node. Any narrower scope may tighten it; none may widen it. |
reservationPosture | { mode: "none" | "wait", timeoutMs: number | null } - whether bind may wait for a reserved slot instead of failing immediately. Consumed at bind time (stage 5), never as a sort key. |
burstPosture | off | manual | auto_when_entitled - whether the profile may reach capacity it does not name. See Burst posture. |
| Disconnect reconciliation | Three fields - disconnectLostAfterMs, disconnectReplace, disconnectReconcile - covering the "host came back and two copies of the work are alive" case. See Disconnect reconciliation. |
isOrgDefault | Whether this is the org's default profile, used when a project has no explicit grant. At most one per org. |
revision | Monotonic; stamped into every decision record that resolves against this profile. |
A profile has exactly one ordered pool list - there is no second, separately-authored fallback list. The ordered surviving subset of that list is the fallback chain: see the one fallback rule.
All six ordering policies
orderingPolicy selects how stage 4 (ranking) orders the candidates that
already survived permission and viability. declared is the default and the
only policy the open-source execution layer implements without the platform;
the other five are a platform-hosted extension of the same seam.
| Value | Behavior |
|---|---|
declared (default) | Take the survivors in the exact order the profile's author wrote them. Unscored - no cost/latency comparison, no cross-pool tie-break. |
ordered_fallback | Preferred pools first, then the remaining eligible pools, in the profile's configured order. |
least_loaded | The eligible pool with the most free session capacity. |
round_robin | Rotate across eligible pools on a persisted cursor (the profile's rotationCursor field). |
affinity | Prefer the pool that most recently ran work for this project. |
cost_aware | Prefer the cheapest eligible pool by recorded 24-hour pool cost. |
An ordering policy may only reorder the surviving candidate set - it can
never remove a candidate. A policy that could drop a candidate is a filter
mis-declared as an order, and is refused at the contract level. Richer
(load-aware) policies also carry a shipping precondition: a pool with no
enrolled machines scores as maximally idle under a naive load metric and
would rank first, always - so a load-aware policy must correct for that
before it ships. declared carries no such precondition, which is part of
why it is the default.
Set the ordering policy with rensei capacity profile set --policy <value>;
see Capacity CLI reference.
Burst posture
burstPosture decides whether a profile may reach capacity it does not name
in its ordered pool list - most concretely, a platform-managed system pool
made available by a plan entitlement.
| Value | Behavior |
|---|---|
off (default) | Never bursts, even if the org holds a capacity entitlement. Entitlement alone is not consent. |
manual | An operator or author may explicitly name the burst pool as a candidate; the entitlement gate still has to permit it. |
auto_when_entitled | The burst pool enters the candidate set automatically once the entitlement resolves. This still shows up on the decision record as a permission event at stage 1 - attributed and explainable, never a silent routing surprise. |
Burst is a permission question, not a ranking one: reaching capacity a profile does not name re-enters the composition law at stage 1 as an entitlement grant that widens the candidate set before viability, preference, and ranking run. See the one fallback rule.
What remains undesigned. Everything above is failure-triggered routing-around plus an explicit profile opt-in - there is still no predictive burst. Reaching for more capacity before a dispatch has actually failed would need a usage meter (token/credit budget telemetry) the platform does not currently collect, and no such meter, overflow policy, or exhaustion trigger exists today. Treat any description of pre-emptive or capacity-triggered bursting as a plan, not a shipped behavior.
Disconnect reconciliation
Three flat fields on the profile answer "the host that was running this work disconnected, then came back - now what":
| Field | Default | Meaning |
|---|---|---|
disconnectLostAfterMs | 300000 (5 minutes) | How long a disconnected host's work is held before it is considered lost. |
disconnectReplace | true | Whether a replacement is placed once the work is considered lost. |
disconnectReconcile | best_score | What happens if the original host reappears after a replacement was already placed - one of keep_original, keep_replacement, best_score, longest_running. |
Before this field existed, "the host came back and two copies of the work are alive" was undefined behavior. It is now a declared, per-profile policy rather than an accident of timing.
Org authors, project consumes
Profiles follow the org-provisions / project-consumes model:
- An org admin authors profiles and grants them to projects. A project can never author a profile, add a pool to one, or widen what it was granted - it can only narrow.
- A project selects among its granted profiles and may tighten: narrow the pool subset, tighten the preference vector, or pin a specific pool or peer. Tightening is a one-way ratchet, both for a single project and across the org-to-project-to-node inheritance chain generally - any level may restrict what a wider level granted; no level may restore what a wider level removed. This is the same intersection discipline the permission stage of the composition law uses: an allow must exist at every scope for a candidate to survive, so removing a grant at a wide scope has certain effect everywhere beneath it.
- Grant records are append-only, never overwritten. A grant carries who granted it and, once revoked, who revoked it and when - revocation writes a new state on the same row rather than deleting history. At most one grant per project is marked primary (the project's selected profile) at a time; granting a new primary revokes the previous one in the same transaction.
- Profile selection, when a project has none, follows a documented cascade: explicit selection, then the project's primary grant, then the org's default profile, then a loud error. A default never bypasses permission or viability, and an explicit invalid selector never silently falls back to a default.
Manage grants with rensei capacity profile grant; see
Capacity CLI reference.
Two API surfaces, one object
Two live API surfaces read and write capacity, and they are not the same age:
/api/capacity/profiles(and/api/capacity/profiles/{profileId},/api/capacity/profiles/{profileId}/grants) is the current, org-admin authoring surface for the object documented on this page./api/projects/{projectId}/capacity-routestill exists as a transitional, project-scoped adapter - it now reads and writes through the capacity-profile tables rather than aproject_execution_routesrow (which no longer exists), but it has not been renamed.rensei capacity profile setwrites through this same adapter for its project-scoped, patch-semantic edit path./api/projects/{projectId}/capacity-profile(and its/selectand/effective-candidateschildren) is the newer project-side read surface: it returns the project's effective profile - the resolved result of its grants - rather than a route record.
If you are integrating directly against the API rather than through the CLI,
prefer /api/capacity/profiles for authoring and
/api/projects/{projectId}/capacity-profile for reading a project's
effective profile; treat capacity-route as the legacy write path it is.
Related pages
- Placement, Selection & Routing Vocabulary - the six-stage law a profile is read into
- Capacity Pools - the substrate a profile's pool list orders
- Decision records & explain - reading what a profile actually produced for one dispatch
- Capacity CLI reference -
capacity profile {list,show,set,grant,test}andhost migrate