# The consumer-profile contract + the three active consumers

_Source: `system/core/lib/chassis-core/CONSUMER_PROFILE_CONTRACT.md` (the definitive profile
contract) and the three live consumer profile files. No redaction required — framework contract
documentation and declarative profile data, no secrets or customer data._

## A profile is three kinds of thing, not one flat bundle

`CONSUMER_PROFILE_CONTRACT.md § 0` defines the seam line: conflating the three kinds "collapses the
imperative driver into an undefined escape hatch." A profile spans —

| Kind | What it contains | Who validates it |
| --- | --- | --- |
| **(a) Declarative data** | `{ stages, exitEnums, forks, routingRows, stageConsequenceMap, budgetThresholds, modelContractTable }` — a JS-literal bundle | `loadProfile` / `validateProfile` at load — **fails closed** (`ProfileContractError` on any missing/malformed surface; no `\|\| AUTO_DEFAULTS`) |
| **(b) Seam wiring** | the per-consumer seam implementation factory (`buildLiveSeams`) — a callable, not inert data — plugged into the four engine seam slots | caller verifies slot signatures against the `SeamBundle` typedef at `wireSeams` time |
| **(c) Procedure** | the consumer's stage handlers + close/summary surface passed to `runProfile()` | caller verifies `handlers.runStage` presence at `runProfile` entry |

`bindingRules` is deliberately **NOT** a profile surface — it is shared engine doctrine
(`binding-rules.mjs::BINDING_RULES`), and `createJudgmentResolver`'s `bindingRules` param is a
verified `void` no-op (`CONSUMER_PROFILE_CONTRACT.md § 0`; `judgment-resolver.mjs:29-30`). A consumer
that needs different binding rules is an INV-5 **surface-up gap** — the engine is extended at the
engine level, never patched per-consumer (`§ 7`).

## The four seam slots (kind b)

`CONSUMER_PROFILE_CONTRACT.md § 2` (`seams/index.mjs:25-39`):

| Slot | Engine stub (degrade-to-manual default) |
| --- | --- |
| `kernel` | `STUB_KERNEL` — elevates all judgment forks |
| `router` | `STUB_ROUTER` — elevates all tokens |
| `ledger` | `STUB_LEDGER` — in-memory, inline-only |
| `selector` | `STUB_SELECTOR` — bus-only, no failover |

## The fail-closed loader (kind a)

`loadProfile(profile) → validateProfile(profile)` (`§ 4`; `profile-loader.mjs:222-224`) rejects every
malformed surface — an empty `stages` array, a duplicate `registry_id`, a `modelContractTable` value
outside `{opus, sonnet, haiku}`, a composite routing-key whose stage prefix names no real stage.
"No surface has a fallback default. A missing surface is an extraction-contract error, not a degraded
run" (`§ 4`).

## The three active consumers

The engine has exactly **three** live consumers today. Each is a real profile that imports
`chassis-core/index.mjs` and calls `loadProfile`:

| # | Consumer | Profile file (verified present + imports `chassis-core`) |
| --- | --- | --- |
| 1 | **Auto Plan** | `system/converge/lib/chassis/auto-profile.mjs` — `import { loadProfile } from '…/core/lib/chassis-core/index.mjs'` (`:28`) |
| 2 | **Cure v2** | `system/cure/lib/run-driver/cure-v2-profile.mjs` — Cure's declarative-data bundle against the shared engine, "ZERO edits to `chassis-core/**`" |
| 3 | **Cure v3 fix-pass** | `system/cure/lib/run-driver/cure-v3-fixpass-profile.mjs` — "the genuinely-different SECOND Cure shape", `import { loadProfile, det, elev } from '…/core/lib/chassis-core/index.mjs'` (`:51`) |

This three-consumer count is the figure published in the already-public
`publications/chassis-reference/METRIC_PROVENANCE.md` ("**3** consumers of the engine (auto, Cure v2,
Cure v3 fix-pass)"), derived there from the live sources.

**Provenance nuance worth recording.** `CONSUMER_PROFILE_CONTRACT.md` dates to 2026-06-15 (the Core G2
extraction) and describes Auto as "consumer #1" with Cure as "consumer #2 (G3 guidance)" — at that
time Cure's profiles were guidance, not yet built. The two Cure profile files above now exist and
import the engine, so the contract's own "declaring a new consumer profile" recipe (`§ 6`) has since
been executed twice. The count is grounded in the live files, not the older prose.
