SUPERSEDED 19 Aug 2026 — the master/child model was deleted the day after it shipped.
It was replaced by peer windows (348b5122): every window keeps its project
list and takes its study from the serve. Read this sheet as history, not as spec.
Dead — do not build from these: §2 (two window types),
§4 (the ⌥⌘N gate — NewWindowGate is deleted), §5 (menu states by window type —
there are no window types), and the study-axis dimming throughout. WindowRoster.Role,
role(for:) and the role: parameter on every menu site went with them.
Still live, and the reason this file is kept: §1 "The surfaces a user judges by" — the framing that caught a window disagreeing with itself in three places, and the method the peer work was reviewed by. The context menus (Open in New Window on the lens row; Open Project in New Window on the project row) are still specced and still unbuilt. §7's video pop-out defect (Finding 22) is still open.
What replaces it: peer today, and Stage 3b next — every window shows any project, any lens, or Welcome, independently. Under 3b the sheet's central question (which window may change the study?) dissolves: all of them may, and each changes only itself.
Stage 3a as decided 18 Aug 2026. One serve, one project at a time, N windows, N lenses.
Masters get projects, children get lenses.
This sheet exists to be judged the way
the app is judged — by the surfaces a researcher can actually see.
The one rule everything else falls out of: a child has no project of its own. Its title is read from what is being served, never from a per-window selection. A child therefore cannot name a study it isn't showing — the failure is unrepresentable rather than guarded.
Every claim below is a thing on screen. If two of them can disagree, that's the bug.
| Surface | Master reads it from | Child reads it from | Can it lie? |
|---|---|---|---|
| Window title | its own selection | the serve | Master: only during a switch (guarded). Child: never. |
| Window subtitle | lens counts over the bridge | same | No — the bridge is per-window and reports what that window shows. |
| Sidebar — project list | present, one row selected | absent | Today: yes. Master's highlight and title share one source, so they agree and are wrong together. |
| Sidebar — lens rail | present, own lens | present, own lens | No |
| Window menu row | title + subtitle, as rendered | same | Inherits whatever the title does. |
| Content pane | the serve | the serve | No — it is the source of truth |
| View ▸ Show Projects | live | live | No — a child has a sidebar (the lens rail) and hiding it is a real thing to want. The name is right for 3b; only 3a makes it read oddly. |
The correction this sheet is built on. The design doc argued a master is
safer than a child because a wrong title is contradicted by the sidebar. It isn't:
in a master the title and the sidebar highlight both derive from the same per-window
selection, while the content comes from the shared serve. They agree with each
other and are wrong together. Two cues from one source are one cue drawn twice — which
makes the master the more deceptive shape, because it looks corroborated.
A new child opens at its parent's lens, then gets re-pointed. Two steps, and the first one is never a surprise — the gesture reads as "duplicate this view, now change it". Opening at a fixed default would cost the same two steps but start somewhere nobody asked for.
The child is one flag, not a second window type: ProjectSidebarOutline
already folds the lens rows into the same NSOutlineView as the project list, so
this is a section omitted.
stopServeIfLastProjectWindow).| State | ⌥⌘N makes | Why |
|---|---|---|
| Zero windows | master | the menu bar outlives windows, so this is the documented way back from empty |
| Welcome master, nothing served | disabled | you would simply get two welcome screens |
| Master with a project | child | the normal case |
| Master + children | child | |
| Children only — master closed | child | ⌥⌘N means "another lens window", always — see the note |
| Welcome master + children (serve still up) | child | something is served — the children are showing it |
| Only Settings / Import open | master | the roster deliberately doesn't count them |
The condition is what is served, not what windows exist (decided 18 Aug 2026). ⌥⌘N means one thing everywhere — another lens window on the study I am looking at — and it needs a study to be looking at. So it is disabled exactly when nothing is served and a window is already open, which is the two-welcomes case and nothing else. With no windows at all it still makes a master, because the menu bar outlives windows and that is the way back from empty.
Note the case this quietly handles. A master that returns to the welcome screen while
children are open does not stop the serve — stopServeIfLastProjectWindow
keeps it up while any other window still shows a project, and the children do. So the study stays
served, the children keep working, and ⌥⌘N stays live because there is still something to be a
child of. "Is anything served" is the right question; "is the front window a welcome screen" is
not.
Dimmed, never hidden — except where the item is gone for good.
decided View ▸ Show Projects keeps its name
and stays live in a child. An earlier draft dimmed it — written when a child was going to have
no sidebar at all. The child we settled on keeps the lens rail, so it has a sidebar and
hiding it to widen the content is a real thing to want; dimming would remove a capability to make
a label read better. The name stays because it is right for the destination: at 3b it
will still be called Projects in a window that has them. 3a is the temporary state, not the name.
Why the whole New/Add/Import family dims in a child. They all act on which study — the axis a child deliberately doesn't have. Dimming them is the same statement the missing project list makes, in the place a Mac user checks when a click does nothing. The lens rows stay live, because that is the axis a child owns.
Measured, not assumed: AppKit's automatic Window-menu path renders
Title (Subtitle) — verified 18 Aug 2026 with two throwaway binaries. So two
windows on one study at different lenses are already distinguishable without an ordinal.
decided Both ship, and the project one is renamed. Right-click a lens → Open in New Window (a child at that lens — the most direct form of the gesture, and it beats ⌥⌘N-then-re-point for the deliberate case). Right-click a project → Open Project in New Window. Two commands, two labels, no collision — and the longer label is on the item that does the bigger thing, which is the right way round. The project one stays dimmed until 3b; the lens one works today.
| Trigger | What moves | What the user sees | |
|---|---|---|---|
| T1 | ⌥⌘N from a master | new child, parent's lens | a second window, same title, same view — then they re-point it |
| T2 | Master switches study | every child follows | all titles change together; all panes change together; nothing disagrees |
| T3 | Child switches lens | that child only | its subtitle changes; nothing else moves |
| T4 | Master closes, children remain | nothing — no promotion | children keep working; ⌥⌘N brings a master back |
| T5 | Last window closes | serve stops | — |
| T6 | Dock icon, no windows | master | the roster answers, not AppKit's hasVisibleWindows |
T2 is the accepted oddity. Switching the master to another study takes the children with it, so you cannot peek at study B while keeping study A's transcripts open. Accepted 18 Aug 2026 — odd, manageable through beta, and better than any alternative before 3b. It is the Stage 3a constraint stated honestly rather than hidden behind a window that lies. Removing it is exactly what 3b buys, which gives that stage a one-line headline: "you can now have two studies open at once."
T4 is why promotion was rejected. Promotion would exist only to paper over there being one serve, would be deleted at 3b, and would make a window change shape while you're looking at it because you closed a different one. An orphaned child is a working window, and the recovery is a keystroke already in the researcher's fingers.
The title says IKEA, the sidebar highlights IKEA, the pane shows Nokia — and Send to Miro exports Nokia's quotes to a board named IKEA. The child shape makes it unrepresentable: no selection, no second source, title read from the serve.
Between "user clicks study B" and "sidecar for B is up", the master's title already says B while
the pane still shows A. Short, real, and the one place a master can lie. Guard the mount on
serveManager.currentProjectPath == project.path; show the boot state, never a sibling's
report. Children are immune — they follow the serve, so they simply arrive when it does.
Different studies are told apart by name; different lenses by subtitle. The ordinal is a
last resort for genuinely byte-identical rows — the case Finder and Mail both leave stuck.
That is a better justification than the one window-menu-naming.html rejected it on
("two views of one project, not two projects"), which was only ever true of Stage 3a.
That mockup's E4 card needs correcting, not just reconciling with its own summary.
A child of nothing is an empty lens rail, and making a second master just gives you two welcome screens. So the item dims. It is the one state where ⌥⌘N has nothing to mean. With no windows at all it stays live and makes a master, because the menu bar outlives windows and that is the documented way back from empty.
⌥⌘N makes another child. The rule stays one sentence — another lens window on the study I am looking at — rather than gaining a special case, and the researcher gets what they almost certainly wanted, which is one more lens.
What it costs, stated plainly: with the master closed there is no project list anywhere,
New Project / New Folder / Add Files / Import are all dimmed (they act on the study axis a child
does not have), and Open in New Window is dimmed until 3b. So the only route back to a
project list is close every window, then ⌥⌘N.
accepted 18 Aug 2026 Not nice, and lived with until 3b — at which point every window can serve its own study and the state stops existing. Every alternative is worse, and the reasons are worth keeping so none of them gets re-proposed:
| Alternative | Why not |
|---|---|
| Refuse to close the master | You can always close a Mac window. A window that refuses is one of the few genuinely un-Mac behaviours — even sheets and panels close. |
| Close the children with it | The nearest real idiom (closing a document closes its inspectors) is the opposite move — close more, not refuse. And it destroys a deliberate arrangement: five transcript windows the researcher placed by hand. |
| Promote a child to master | Already rejected — exists only to paper over the single serve, deleted at 3b, and makes a window change shape while you are looking at it because a different one closed. |
Keep ⌘N New Project live in a child |
Workable, and the closest call: NewItemFallback already almost does it. But
it breaks the one clean rule a child has — every study-axis command dims — for a state that
disappears at 3b. |
mainThe subtitle narrates the run, and the rule was .key-only — so opening Settings
meant no window narrated, every subtitle dropped back to its resting count, and it read as
the run having stopped. The intent was always "one window narrates, not five", and
main expresses that where key does not: AppKit keeps
mainWindow on the frontmost main-capable window when a panel takes key.
Measured, and it went against the guess. The Settings window cannot become main —
the package overrides it (SettingsWindowController.swift:168,
override var canBecomeMain: Bool { false }). So gating on isMainWindow
fixes the Settings case in one line, with no roster change and no most-recently-key list —
the structure the design declined for command routing stays declined.
| Frontmost | A project window narrates? | |
|---|---|---|
| Settings | yes | free — it cannot take main |
| Import · Shoal · System Health | no | plain Window scenes, so ordinary NSWindows that do take main
— accepted |
Import is a busy foreground task of its own; the other two are decorative or debug. Revisit only
if it bites — then UtilityWindow (macOS 26, makes an NSPanel) for Shoal and
System Health but not Import, or the roster MRU, earned rather than anticipated.
The cheap test for the next window that raises this: open it over a project window and watch
that window's title bar — main draws at full contrast, not-main goes grey.
It is a plain NSWindow, not a scene, so @FocusedValue(\.bridge) goes nil
and every Video menu item dims exactly when the video window is what you're looking at.
Master/child doesn't fix this — the pop-out is a third shape, and "front window" routing has no
answer for a key window that isn't a scene.
parked 18 Aug 2026 — solve later Deliberately not fixed with this
work, and deliberately written down in three places so it cannot be lost: here, Finding 22 of the
workspace review log, and docs/design-workspace.md. It is a regression from
b72c23eb (the per-window bridge), not a pre-existing wart — the Video menu worked
before, when one app-level handler served every window. The general case is the same one E-series
finding 26 names: a key window that is not a scene, which also covers open/save panels. Fix
the pop-out first, then re-check whether the general case still bites.
Children have no selection to restore — they take the served study. So only the master's
selection needs persisting, which makes the restore story simpler under this shape than
under all-masters. Today one global @AppStorage("selectedProjectID") means every
restored window lands on the same study; harmless once only one window reads it.
| Still open | Cheapest coherent answer | |
|---|---|---|
| E4b | The orphan dead end. Master closed, children only: no project list, every study-axis command dimmed, so the way back is close-everything then ⌥⌘N. Reachable by ordinary tidying. | Accept it and say so — or keep ⌘N New Project live in a child, which already has
a fallback that opens a window (NewItemFallback) and would be the one study-axis
command that stays lit. Costs the clean "the whole family dims" rule. |
| E5 | Who narrates a run when Settings or Import is frontmost — today nobody does, and every subtitle drops to its resting count mid-run. | Either keep one narrator (the last key project window) or accept silence and state it. Nobody decided the no-window case; it currently reads as the run having stopped. |
| E3 | window-menu-naming.html's E4 card still
argues against the ordinal, on a premise ("two views of one project, not two
projects") that was only ever true of Stage 3a. |
Correct the card, don't just reconcile it with its own summary — the reasoning is what the next reader will cite. |
| — | Pin vs hide-by-default-switchable, deferred on purpose by the design doc. | Its own test still stands and needs the child to exist first: spin off a Quotes window and go ten minutes without reaching for the project list. |
Settled 18 Aug 2026 — not reopened here. A child holds a lens, not a project
and reads its title from the serve. Its sidebar is the master's minus the Studies section
— same width, same metrics, Views simply moves up. ⌥⌘N is gated on what is served: child
when something is, master when no window is open, dimmed otherwise; children-only still gives a
child. Promotion rejected. A new child opens at its parent's lens. Switching the master takes the
children. View ▸ Show Projects keeps its name and dims. Lens row gets
Open in New Window, project row gets Open Project in New Window. Video pop-out
parked. All in docs/design-workspace.md §"What a child window is" and its changelog.