Subject profiles
Every constant in this pipeline was originally calibrated on one standing human figure,
and until the profile system existed nobody could say which constants were principles
and which were that warrior’s measurements wearing a principle’s clothes. A profile
(profiles/character.json · ship.json · beast.json · prop.json) is where the
second kind lives.
The Director’s charter for the system: “create profiles so that we don’t break the
humanoid character pipeline to make the ship.” It worked — the record is the record.
The classification that produced the system — every constant argued into one column or the other — is E04-profile-extraction.md; the boundary’s design is profiles-design.md. This page is the operator’s map. If it disagrees with the loader’s docstring or a ruling, those win.
Loader semantics (tools/subject_profile.py)
Section titled “Loader semantics (tools/subject_profile.py)”--profile <path>is accepted by every route-active tool; the profile’s values become that tool’s defaults, and an explicit command-line flag still wins — a recorded invocation keeps meaning what it meant.- No
--profilemeans byte-identity: nothing happens at all, every historical command reproduces exactly. This is why Gate 0 runs unprofiled on a new subject. - An unknown key raises, loudly. A profile naming a flag its tool does not have is the exact failure the system exists to prevent: a value that looks configured, reads as authoritative, and does nothing.
- Every value carries
whyandfrom— enforced by the loader, because a profile is otherwise a perfect hiding place for unexplained magic numbers, which is precisely how an earlier memory store became doctrine.
The decision forms
Section titled “The decision forms”Every flag on a route-active tool is decided in exactly one of these forms — the registry sweep (below) enforces it:
| form | meaning |
|---|---|
| MEASURED | derived from this subject’s own geometry or fixture, with the derivation cited |
| SPENT | a recorded run on this subject consumed the key; the value is what ran. A value cited as SPENT inherits its anchor only if every sibling of the anchored run is pinned to the anchored mode — a partial-family pin quietly voids the anchor it cites (E12 Ruling 4a) |
| FIRST-RUN OPERATING POINT (LIVE) | the accepted route’s value adopted explicitly, because silence resolves to inheritance; the next halt’s diagnostics rule what carries |
| SUSPENDED — EXPRESSED MECHANICALLY | a halt or bound this subject cannot calibrate yet, expressed as a value the tool actually receives (a vacuous 0.0 / 9.99) so the diagnostic still computes and prints while the halt cannot fire. Prose suspensions never reach the loader |
_not_on_route | decided: the profile supplies no value because the tool or key is not on this subject’s route — absence as a decision the sweep can see |
_per_invocation | decided: the profile supplies no value BY DESIGN because the correct value varies per invocation and arrives from the job context (stroke yaw/elevation, the cloud brush’s --lane). Minted E12 Ruling 6d after two executors correctly declined to misuse _not_on_route’s name for keys exercised on every run; site migration is scheduled and not yet done — until then those sites still wear the old name with the objection restated inside them |
_NOT_CLEARED | a lifecycle block: the tool is forbidden on this subject until a ruling clears it and pays for the clearance by deciding every key in the same commit. Removing the marker without deciding the keys makes the sweep fire — that firing is the procedure |
The instruments
Section titled “The instruments”- The registry sweep (
e04_registry_sweep.py) walks every route-active tool’s argparse surface against the profile and reports UNDECIDED flags. 0 UNDECIDED gates the texture arms (stage 1 onward) — not Gate 0, not the measurement pass, not the styled pair (E12 Ruling 7d). The registry is the classification table: 0 UNDECIDED means every flag the tools bothered to write down is decided; it does not mean every flag that matters is. - The coverage pass additionally checks
character.json’s relocation claim (profile values = code defaults, digit for digit).NOT A PURE RELOCATIONon any other subject’s profile is the expected verdict, not a finding.
Registry state (2026-08-07)
Section titled “Registry state (2026-08-07)”| profile | sweep | note |
|---|---|---|
ship.json | 81/81, exit 0 | the first profile with every flag on an accepted route explicitly decided |
beast.json | 83/83, exit 0 | certified at E13 handoff 15 step 0 — the gate the texture arms waited on since handoff 2, passed after Ruling 26’s transcriptions (the count read 80/81 mid-arc; flags grew as the arc added registry slots) |
character.json | 65/81 | that profile’s silences-as-defaults predate the sweep; its relocation claim holds with one standing mismatch (thin-extent 0.03 vs source default 0.0 — the only profile that ever turned the guard on) |
prop.json | ACCEPTED at Gate 1 (2026-08-08, Ruling 32) — every value spent, every artifact citable-only | canny 0.10/0.25 and the backdrop RULED (E14 Rulings 6/7); elevated NONE (Ruling 8); the recipe anchored on the accepted pair (Ruling 16b); the palette wired (Ruling 17, report-only); the stroke lane RULED at Ruling 24, EXECUTED through Ruling 29 (the stone garnet by re-projection, the collar repair on an asserted-count union, eight strokes at zero credits, both edge-ons 20b-clean), HALT 2 ACCEPTED (Ruling 30, “I love it”); finalize + pack SPENT at Ruling 31g (surface-aware, both ANDONs on defaults, the replay byte-identical) — mix 45.25/2.07/52.68 of valid, 88.71% of the visible surface reference; longsword_hero.glb at Gate 1 beside the clay |
The framing family
Section titled “The framing family”aspect, fit-axis and margin move together across four consumers
(turn_render, silhouette_masks, texpass_iter, and project_twins since E12
Rulings 24a/26a exposed and completed its slots) or subjects silently misregister — a
lesson paid for twice on the ship (Rulings 6/11/15/25: a clipped frame found at the
third consumer, and a generator-illegal width that broke every twin↔silhouette pairing
— the Qwen VAE decodes ÷8, so derive the frame from the mesh, then round to the
nearest legal width, ÷16 preferred). And the family pins PER SUBJECT: the beast’s
fit axis is width (landscape, 1.74), the prop’s is height (the route’s first
portrait subject, 0.226) — importing one subject’s family value onto another
misregisters every mask, which is why prop.json’s entries say so explicitly.