Greenpen · design commission from Khoa via Will · 31 Jul 2026
The Writers’ Room
Pack v2 — the IA changed.
Khoa reviewed v1 against OwnerMind
/inapp and redirected
§9 twice on 31 July: the table form read as
many rooms. The room is now
one chronological lane per project, with a single focal seat at the top.
Open the v2 room → — the pages below are v1 and
several are superseded.
Every project can convene a staffed table: named agents with roles, temperaments
and faces, running a task through the paces while you sleep. In the morning you get minutes,
a row of notes to camera, a diff, and a tray of proposals that have not been applied to
anything.
It lives as a drawer on the Studio you already have. The Studio stays the
writing surface — the room comes to it.
Start here · v2
The lane is the room
One chronological lane per project. Focal seat at the top (default the Showrunner),
other seats a switchable row beneath; tapping one filters and addresses without ever
changing rooms. Sessions are collapsible segments, the paces shrink to a progress chip,
and three things never scroll away.
v1 · superseded IA, live mechanics
The table form
Superseded as an IA, but it is still where the gate tray and the
continuity lane are designed in full — both carry over into v2 unchanged and
the dock opens them.
The point of it
The morning after
What you walk into: what stopped it, what needs you, the notes, the diff, the minutes.
v3 · the Story Department
The Readers
Six style seats bound to measured grammars — Greenpen’s sibling to
Wideshot’s Fourth Unit. A Reader is a lens, never a writer: made focal its
grammar conditions the draft, sitting in it gives measured deltas. Coverage worn openly,
no author or title names anywhere.
Before it runs
The brief
Task, boundaries, who sits in, rounds, budget. Boundaries are the product.
The people
The roster
Seven seats designed in full, the persona-pack template, and the Tavus face direction.
Small screens
Mobile
A bottom sheet with three snap points. Peek never closes, because a closed panel that stops
telling you things is the failure mode.
What Khoa asked for, and where it is
1Drawer collapsed
A 46px rail of seat faces with presence (idle / reading / writing / has-notes)
and the waiting count. Tapping a face reopens onto that seat.
Open →
2Drawer open — the table
Seat cards with a stance chip and the latest note; tapping one opens full
notes, the clip, the temperament brief and the credential.
Open →
3The paces rail
Eight ticks, the round counter, a loop marker on READ–NOTES–REVISE,
and — for overnight — where it stopped and why.
Open →
4The notes round
Faces are play buttons on text cards, not video tiles. One sticky stage, so
at most one moving image is ever on screen. A failed render leaves a complete note.
Open →
5The gate
Two lanes. Proposals carry per-seat provenance and commit the moment you
tap them; reject captures its reason first. Continuity flags are a separate lane with
Mark read / Resolve / Dismiss, and a badge saying how many still keep the room looping.
Finish gate closes the pace once everything is decided.
Open →
6Morning-after view
One walk-in: what stopped it, the batch, the notes row, the diff, the minutes.
Open →
7Session setup
Task, canon-locked versus open, tone red lines, who sits in, rounds cap,
per-seat budget, and the two ruleset switches.
Open →
8Persona packs and the roster
Six-field template, seven seats written out, sprite sheet, face direction.
Open →
9Two-writer competing takes
Routed into the Draft compare that already ships. The room adds one thing:
each column is labelled with the seat that wrote it, and carries that seat stance.
Open →
10Mobile
Bottom sheet, three snap points, gate buttons under the thumb.
Open →
What this is built on
Every value, colour and layout dimension is read from the shipped product rather than chosen.
I did not get the staging demo gate, so this is grounded in source and in the running
architecture — not in screenshots of the live app.
| Taken from | What it gave |
| apps/web/components/fd-room.tsx |
The Studio shell: 248px drafts and scenes rail, the format tabs, the FD palette
(#e9e9e9 #efefef #c4c4c4 #2f7be6), the review-hunk row with per-hunk
accept and reject, compare mode and its four-colour legend, and the dock that already
collapses with dockOpen. |
| apps/web/components/fd-agent-dock.tsx |
The seam. A 300px right dock with a role picker, run history carrying role chips, and a
library-references block. The room becomes a sixth tab beside
agent, memory, markup, insights, history — not a new panel. |
| apps/web/app/globals.css |
Warm Playbook: paper #F4EFE7, ink #221A14,
accent #2D5BD0, Figtree, Newsreader, Courier Prime, 10px control
radius, warm-tinted shadows. |
| docs/ROLE_AGENTS_SPEC.md |
The role registry the seats are built on, and the restrictions kept verbatim: editor never
invents a scene, continuity never mutates. |
| docs/IDEA_PITCHER_SPEC.md |
The guest seat and its present-do-not-rank rule. |
docs/WRITERS_ROOM_SPEC.md
branch will/writers-room-spec ·
PR #26 |
The paces, the seatNote artifact, the hard rules, and the Tavus phasing. Re-read from the
branch at delivery rather than the scratchpad copy I was first given: that copy had moved
under me (181 lines → 186). I diffed them — §3 roster, §4 paces,
§5 hard rules, §6 Tavus and §9 UI brief are byte-identical, so nothing in
this pack changes. The five new lines are the status header and §11 blockers. |
The room is grey; the room drawer is warm paper.
The Studio is a Final-Draft-faithful desktop surface and the rest of Greenpen is warm paper.
Rather than invent a third palette I let the product identity into the one place in the Studio
where people and agents talk. It also does something useful: the room reads as a different kind
of place without a single new colour.
What the mechanics check changed
Will reviewed this against the spec and the shipped platform, and found one thing I had
designed wrong. Both of my judgement calls survived; the gate did not.
Corrected · the gate
I designed an atomicity the platform does not have
My first gate had one Apply button that stayed disabled until every proposal was
decided. That reads as a batch transaction. There isn’t one:
approve_pending and reject_pending are
individual calls that take effect when made, and there is no un-approve. Holding
decisions in the UI and firing them at the end would lose them on a crash and let the queue
move underneath.
So the gesture stays and its meaning changes: each decision commits on tap and the
row locks; the end button is Finish gate, which completes the GATE pace rather than
the writes. Partial triage is now first-class — decide 3 of 12, leave, come back.
One consequence I had to design for: because commit is irreversible, reject captures its
reason before the call, since reject_pending(reason) takes it
as an argument and there is no later.
Sharpened · continuity
It is not an approval at all
I had continuity annotations as a row in the approval tray, with approving meaning
read rather than fixed. Right instinct, wrong lane: annotations never touch the
approval queue. I checked the shipped registry —
resolve_annotation is kind:"action",
requiresApproval:false.
They are now a visibly different group with a different verb (Continuity flags), and
the word approve never appears on them. The source gave me one more than Will
specified: three terminal states, not two — acknowledged,
resolved, dismissed, with a resolution
required for the last two. Dismiss earns its place: without it, a human who thinks a
flag is wrong has no move but to leave it open, and an open blocking annotation keeps the
room looping for ever. A badge now says exactly that.
Four judgement calls I made
These are decisions, not defaults. The first two were put to Will and he endorsed both as
designed; the rest still stand open to you.
Call 1 · endorsed by Will
Stances are words, so I did not colour them like a traffic light
The obvious treatment is green ship it, amber another pass, red
structural concern. I did not do that. Six coloured chips in a column reads as a
leaderboard of seats — exactly the ranking the product law forbids, arriving through
the palette instead of the text.
So colour marks what needs you, not what is good: only structural concern is
coloured. The other two are neutral chips. You lose a little scanability and you lose the
ability to read a ranking off the row, which I think is the right trade.
Call 2 · the notes round
Faces are buttons on text cards, and there is only ever one stage
A row of six talking heads is a video wall, and it also quietly inverts the rule that text
is the source of record. So the note is a text card, the face on it is the play affordance,
and every clip plays into one sticky stage at the top of the panel.
At most one moving image is on screen at any time, the notes stay readable underneath it,
and a seat whose render failed simply has no play badge and a line saying the note is the
record. Nothing is missing when video is missing.
Call 3 · the morning after
What needs you comes before what explains it
The commission lists minutes, notes, diff, batch. I put the approval batch second,
straight after a single line saying what stopped the session — before the notes, the
diff and the minutes.
At 8am the question is not “what happened”, it is “what is waiting on
me”. The minutes are the best-written thing on the page and they are at the bottom,
on purpose. Say the word and I will lead with them instead.
Call 4 · endorsed by Will
The competing-takes view is the shipped compare, not a new surface
Greenpen already has draft-versus-draft compare with a four-colour legend and a side-by-side
mode. Building a second comparison surface for the room would give you two things that drift.
So the room routes into the existing one and contributes exactly one thing: each column is
labelled with the seat that wrote it and carries that seat stance. The drawer gets out of
the way so the stage has the width.
Things I want a decision on
| Question | Who | Why it matters now |
| Does the fold-on-first-response behaviour carry over? The room header folds into
one rail when the body gets heavy, and never re-folds once you have opened it by hand. |
Khoa | Same rule I shipped on the OwnerMind drawer. If you did not like it there,
it should not be here either. |
| Is the guest seat off by default? I have the Idea Pitcher unseated until you add
it in the brief. | Khoa | Seven faces in a 46px rail is a lot; six is comfortable. |
| Does dismissed clear the convergence blocker,
or only resolved? I have both clearing it and
acknowledged not. | Will |
Will’s ruling named resolve; the shipped enum also has dismiss. If a dismissed flag
still blocks, a human who disagrees with Continuity can never let the room converge. |
| The MCP handler hardcodes resolvedBy:"agent". In the room
it is the human resolving from the drawer. | Will · Adam |
Minor, but it would misattribute every human resolution in the record, and the room’s
whole point is that provenance is legible. |
| Paces rail — answered. Tap always views, any pace, live or done.
Moving a live session is human-only and there are exactly three moves, now in the rail:
Pause/Resume, Run another round, Abort to wrap. | Will · ruled |
No forward jumps ever — a pace consumes the previous pace’s artifacts, so NOTES
without READ is undefined. Seats never move the rail; the chair advances it; you interrupt it. |
| Staging demo-gate pass. Will did not transmit it, correctly. | Khoa |
Everything here is grounded in source. A pass would let me do a fidelity pass against the
running app and catch anything the source does not show. |
| Does the Better Auth clean reset change whose ownerId a seat
token carries? New blocker in §11 of the spec, added after I built this. |
Will · Adam |
Not a design change — the seat detail panel says a seat is project-scoped and cannot
approve, and that still holds. But it names an owner, and post-reset that identity follows the
reset contract rather than the legacy backfill. Worth settling before anyone builds Phase 2. |
Scope
Design only. No build, no backend, no Tavus provisioning — those are out of my
lane and out of this pack. Nothing here is committed to the Greenpen repo; the fleet hold
stands and this proceeds only because it is a Khoa direct instruction.
QA risk tier: T3 — non-executable mocks, no product behaviour change.
Names, pages and figures are illustrative. The seats, the paces, the lane
restrictions and the approval behaviour are the design. Portraits are placeholders and must be
replaced by stills taken from the Tavus replicas — see
assets/seat-manifest.json.