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+.
"Jimmy" instead of
fan.verite-hand.v1, the rename becomes a migration./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.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.
| Route | Surface | Deep-linkable state |
|---|---|---|
/create/unit | The 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/conditioning | Who is directing — pilot selection | The only stage where the pilot may change; a swap here re-plans downstream |
/create/:runId/plan | Scenes and rhythm | ?scene=3 · ?scene=3&shot=5 opens the note on that shot |
/create/:runId/keyframes | Planned from the pilot’s measured rhythm | ?scene=3 |
/create/:runId/render | Shots to cut | ?shot=… |
/create/:runId/cuts | Competing takes — lineage siblings | /cuts/:cutId · /cuts/compare?a=…&b=… |
/my-work | Their films, cuts and runs | — |
/my-work/:filmId | A member’s homework view, reached through a member | ?via=fan.slow-bloom.v1 — never a library browse |
/admin/bench | The 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.
?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.Three, per my own OwnerMind pattern, and the same breakpoints the Instrument already uses.
| Width | Funnel | Stage | Unit drawer |
|---|---|---|---|
| Desktop ≥1200px | 246px, persistent | fluid | 400px, collapsible to 0 — the toggle says Show unit when hidden |
| Tablet <1200px | hidden | full width | hidden; reachable from the stage header |
| Phone <900px | hidden | full 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.
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.
Display names are a skin. These IDs are what everything downstream stores.
| Internal ID | Display name | Specialty tag | Coverage | Lifecycle stage — derived |
|---|---|---|---|---|
fan.verite-hand.v1 | Jimmy | handheld verité | 55 sc / 299 sh | 4 — verified · bio: Greengrass |
fan.slow-bloom.v1 | Mei | saturated interior | 41 sc / 186 sh | 4 — verified · bio: Wong Kar-wai |
fan.held-breath.v1 | Dara | long-lens compression | 38 sc / 241 sh | 3 — bio published · Bigelow |
fan.long-room.v1 | Vera | static deep-focus master | 29 sc / 97 sh | 1 — in measurement · under the recipe floor |
fan.cut-reflex.v1 | Otis | kinetic montage | 47 sc / 512 sh | 2 — measured · not attributed |
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.
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.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:
(fingerprint, id) and is never re-rendered under a finished film. A re-measured
member is .v2 and gets a new portrait by construction — the same rule as
§4, now load-bearing twice.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.
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:
proposals →
accept 2 is as natural in a terminal as in the panel.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.