Adam ratification · amendment 9, plus the scale ruling · binding

The signed design contract

This pack gates Phases 2–5, so the parts a build has to honour are written down here rather than inferred from a mock. Six things are contract: the route map, the responsive states, the clearance boundary, the persona IDs, the member lifecycle, and the portrait recipe — the last two added by Khoa's scale ruling, which replaced the five-card room with an information architecture for 100+.

The one that costs most if it is got wrong: display names are a skin over stable internal IDs. The late Bindi gate can rename every member in a config change — but only if nothing downstream ever persisted a display name. If a run record, a lineage sibling or a proposal stores "Jimmy" instead of fan.verite-hand.v1, the rename becomes a migration.
Route ruling — the same principle, extended into URL space (Tom, 31 Jul, on my flag; his amendment note is the paper trail). The crew room’s route is /create/unit, not /create/crew-room and no longer /create/warroom. A route is an identifier, so it carries the most durable locked token. Unit is the load-bearing word of the design lock; crew room is display flavour that can still move at the Bindi gate, and amending the map to follow it would churn the contract twice. So the address bar says /unit while the heading says “crew room” — exactly the rule §4 already applies to persona IDs, which is why it needed no new argument. Renamed once. Scope-checked as product-local: no external deep-link contract references it (Keystack covers /film/* only) and we are pre-launch, so no redirects are owed.

1 · Route map

Nested routes per funnel stage. State lives in the URL, not in component state — the stale-persisted-stage defect in the assessment (§7.2) becomes structurally impossible when the stage is the route.

RouteSurfaceDeep-linkable state
/create/unitThe crew room — search-primary, virtualized roster ?fan=fan.verite-hand.v1 opens that member’s dossier sheet · ?school=…&pace=…&era=…&temper=…&stage=… restore the roster view
/create/:runId/conditioningWho is directing — pilot selection The only stage where the pilot may change; a swap here re-plans downstream
/create/:runId/planScenes and rhythm ?scene=3 · ?scene=3&shot=5 opens the note on that shot
/create/:runId/keyframesPlanned from the pilot’s measured rhythm ?scene=3
/create/:runId/renderShots to cut?shot=…
/create/:runId/cutsCompeting takes — lineage siblings /cuts/:cutId · /cuts/compare?a=…&b=…
/my-workTheir films, cuts and runs
/my-work/:filmIdA member’s homework view, reached through a member ?via=fan.slow-bloom.v1 — never a library browse
/admin/benchThe same crew room at admin clearance /admin/bench/:fanId opens the full fingerprint

Two rules that go with the map. The unit drawer is not a route — it is a panel over whatever stage you are on, so its open/closed state is a preference, not a URL. And the private search key never enters a URL: typing a director resolves server-side and the address bar only ever carries the resulting fan.* id.

Amended by the scale ruling. Two changes, both forced by the roster becoming virtualized. One: ?fan= can no longer mean “scroll to and highlight” — in a windowed list the target row does not exist in the DOM until you scroll to it, so a deep link that depends on finding it is a link that works only sometimes. It now opens the dossier, which is what the link was for. Two: the five facets are URL state, because a filtered roster is a view worth sending someone. That sharpens the privacy rule rather than bending it: facets are public and go in the URL; the search key is private and never does. The distinction is not that one is a query and the other is not — it is that school=anamorphic wide discloses a category we publish, and a director name discloses the corpus.

2 · Responsive states

Three, per my own OwnerMind pattern, and the same breakpoints the Instrument already uses.

WidthFunnelStageUnit drawer
Desktop ≥1200px246px, persistentfluid 400px, collapsible to 0 — the toggle says Show unit when hidden
Tablet <1200pxhiddenfull width hidden; reachable from the stage header
Phone <900pxhiddenfull width overlay, never a squeezed third column — fixed inset, own scroll, sticky header so the way out is always visible

The rule underneath: a pane is never narrowed below the width its content needs. When there is not room it becomes an overlay or it goes away and says how to get it back. A 400px drawer squeezed to 180px is worse than no drawer.

3 · Clearance boundary — one room, two faces

The same crew room, two audiences. The user face is the cinematic one; admin is the workshop that runs the unit. Nothing in the user face is a subset of a corpus view — the corpus is not rendered to users at all, which is what makes the provenance private.

User clearance

  • Dossier cards: avatar, first name + specialty tag, look-reel, coverage numbers, temperament
  • Measured / verified chips
  • The bio line, where and only where it is measured-true
  • Typeahead that resolves a private key to a persona
  • Hire as pilot · sit in as guest

Admin clearance — adds

  • The full fingerprint: distributions, shot-size mix, motion, the wave
  • Source titles and directors behind each grammar — the corpus
  • Graduation pipeline: what auto-review has passed, what is pending
  • Per-member call metering and session telemetry
  • Degenerate-profile warnings — which members are too thin to seat
The boundary is server-side, not a hidden div. The user payload must not contain the corpus fields at all. I built the typeahead the wrong way first and could read the whole director-to-fan map out of the bundle — a clearance enforced by what is rendered is not a clearance.

4 · Stable persona IDs

Naming is locked for design (Khoa, 31 Jul) — the fourth-unit vocabulary is final for every surface and no longer carries a provisional hedge. Scope, so nothing overreaches: this is a design lock, not a brand or marketing lock. Trademark and persona wording still clear at the Bindi pre-production gate, and the internal IDs below stay name-agnostic per the skin rule — which is exactly what lets a design lock be safe to take now and a naming ruling to arrive later without rework.

Display names are a skin. These IDs are what everything downstream stores.

Internal IDDisplay nameSpecialty tagCoverage Lifecycle stage — derived
fan.verite-hand.v1Jimmyhandheld verité 55 sc / 299 sh4 — verified · bio: Greengrass
fan.slow-bloom.v1Meisaturated interior 41 sc / 186 sh4 — verified · bio: Wong Kar-wai
fan.held-breath.v1Daralong-lens compression 38 sc / 241 sh3 — bio published · Bigelow
fan.long-room.v1Verastatic deep-focus master 29 sc / 97 sh 1 — in measurement · under the recipe floor
fan.cut-reflex.v1Otiskinetic montage 47 sc / 512 sh2 — measured · not attributed
The bio slot is a measured field, not a copy field. Director names are permitted as influences — but the listed directors are exactly those whose films we hold fingerprints for. Three hero-grade fingerprints are substantiated, so three of five members can carry a bio and two cannot. Those two say so on the card. The bio is generated from the fingerprint index, never authored: unchanged by the scale ruling and now mandatory, because at a hundred members nobody proofreads a bio for truth, so it must not be possible to write one that is not.

5 · The member lifecycle — contract, because it is derived

Every member passes through four stages: in measurement measured bio published verified. What makes this a contract item rather than a styling one is that the stage is computed, never stored.

No column anywhere holds a stage. stageOf(member) reads coverage against the recipe floor, whether a bio has been generated, and whether a human has verified it — in that order. A stored stage is a field that can drift out of agreement with the numbers printed next to it, and at a hundred members nobody notices when it does. A derived one cannot lie. If a build adds a stage column for query convenience it must be a materialised view of the same function, never a writable field.
Writing the rule down reclassified one of my own five. Vera is measured from 97 shots — under the floor the portrait recipe needs for stable colour and size statistics. Her hand-directed portrait exists and reads well, because by eye you can carry a thin member on craft; the pipeline cannot and should not. So the hand-directed set quietly contained a member the generator would refuse, and only stating the floor made that visible. She now renders in measurement, which is true, and it is the same fact the admin-clearance list above already promised as a degenerate-profile warning — the user side and the admin side of one rule.

6 · Portraits are generated, and the recipe is the deliverable

At 100+ members portraits stop being artworks and become the output of a template driven by each grammar’s own measured visual parameters. The full spec, the five reference outputs and a live renderer are on the portrait recipe page; three things from it are contract:

Three of the five parameters — colour statistics, contrast and key angle, source aspect — are not in the fingerprint index today. All three are cheap frame statistics on a pass that already walks every frame. Named here because a recipe that silently depends on fields nobody produces is a drawing, not a spec.

The .v1 is load-bearing. A member re-measured on more titles is a new id, not an edited one — otherwise a lineage sibling cut months ago silently claims a grammar it was never cut against. Renaming is a skin change; re-measuring is a new version.

7 · The drawer is a view of a server-resident session

Khoa-directed, and it changes framing rather than layout. The same fan conversation continues from a terminal over MCP, so the panel must not read as a chat that begins when it opens:

Design consequence worth stating: the drawer must never own state the session does not have. If a note is composed here and the tab closes before it lands, it is gone — because the session is the record and this is a window. That argues against local drafts in the composer, or for making them visibly local.

Design only. No build authorised by this page; the route map is a design position on URL shape, not a routing decision. QA risk tier: T3. Measurements illustrative; the contract items are not.