record the close of §5.9

This commit is contained in:
2026-08-07 09:43:30 +00:00
parent ca046a3876
commit fed2badd28
+34 -8
View File
@@ -28,9 +28,9 @@ before deciding what to pick up next. Its conclusions in one paragraph:
(§5.1), but inverted: the requirement is that closing a chat panel must _not_ destroy the agent; (§5.1), but inverted: the requirement is that closing a chat panel must _not_ destroy the agent;
`normalizeLayout` as framework (§5.4); and the mobile-collapse decision (§6), which swings on one `normalizeLayout` as framework (§5.4); and the mobile-collapse decision (§6), which swings on one
unanswered question. unanswered question.
**§5.9's first three are done** — the bag is a parsed identity (`dbe585f`) and the two file-browser **§5.9 is closed** (`dbe585f`, `717580f`, `585f234`, `d3922bd`, `ca046a3`) — the bag is a parsed
fields are deleted (`717580f`, `585f234`). What is left there is `promptPrefix` and the identity, the app-config fields are deleted or moved onto the app, and the inert context is built
constructed-in-two-places preview context. once. Tier B is now panel lifecycle (§5.1), `normalizeLayout` (§5.4) and the mobile decision (§6).
- **Tier C, orthogonal:** everything else — including **§5.2 (remounts) and §5.3 (drag-to-move), both - **Tier C, orthogonal:** everything else — including **§5.2 (remounts) and §5.3 (drag-to-move), both
large downgrades.** A panel is now a pointer to a server-side session, so a remount costs a replay, and large downgrades.** A panel is now a pointer to a server-side session, so a remount costs a replay, and
the drag hazard is disarmed by resolving identity by name (`e588524` + `bc82086`). the drag hazard is disarmed by resolving identity by name (`e588524` + `bc82086`).
@@ -490,9 +490,11 @@ All the same bug: an app guessing "am I being closed?" from an unmount, or payin
`WorkspaceContext` is 18 fields, of which the framework itself reads none of the first six. Apps never `WorkspaceContext` is 18 fields, of which the framework itself reads none of the first six. Apps never
touch the framework half, so the abstraction holds in one direction; the leak is entirely outbound. touch the framework half, so the abstraction holds in one direction; the leak is entirely outbound.
**Down to 16 as of 2026-08-07**, and the outbound half is now `workspace`, `cwd`, `root`, `promptPrefix`. **§5.9 is closed as of 2026-08-07.** The context is 15 fields, and the outbound half is `workspace`,
Of those, `workspace` and `cwd` are genuinely the framework's to state — where the panel is and what `cwd`, `root` — all three facts about *where the panel is*, which is the one thing a framework of this
directory it is over. `promptPrefix` is the one remaining courier job, and it is the next item. shape genuinely owes an app. Nothing left on it is an app's vocabulary: the file-browser pair is
deleted, the chat's system prompt is a prop on the chat, and the key three apps used to parse is a
parsed identity. The two hand-written copies of the inert half are one named constant.
- [x] **Delete `initialFilePath` and `defaultFileSort`.** Declared `WorkspaceContext.ts:14-15`, plumbed - [x] **Delete `initialFilePath` and `defaultFileSort`.** Declared `WorkspaceContext.ts:14-15`, plumbed
through `WorkspaceView.tsx:19-20,30`, read only by through `WorkspaceView.tsx:19-20,30`, read only by
@@ -505,11 +507,17 @@ directory it is over. `promptPrefix` is the one remaining courier job, and it is
the duplicate `DefaultSort` type in two files, and `useFileBrowserApp`'s `isolated` — which was the duplicate `DefaultSort` type in two files, and `useFileBrowserApp`'s `isolated` — which was
`!!initialPath` and so had always been false. No behaviour changed at either level; both were `!!initialPath` and so had always been false. No behaviour changed at either level; both were
already running on the defaults. already running on the defaults.
- [ ] **Move `promptPrefix` onto the component, not the context.** `WorkspaceContext.ts:16` → - [x] **Move `promptPrefix` onto the component, not the context.** `WorkspaceContext.ts:16` →
`Chat/ChatPanelWrapper.tsx:78` → `useEmbeddableChat.ts:107`. Set by `EmailScreen.tsx:53` and `Chat/ChatPanelWrapper.tsx:78` → `useEmbeddableChat.ts:107`. Set by `EmailScreen.tsx:53` and
`BrowserScreen.tsx:35`, each a screen-local ~40-word system prompt. The framework is a courier for `BrowserScreen.tsx:35`, each a screen-local ~40-word system prompt. The framework is a courier for
a string only one app understands, and the `components` prop already exists for exactly this — a string only one app understands, and the `components` prop already exists for exactly this —
Email can supply a pre-configured chat by panel id. Email can supply a pre-configured chat by panel id.
**Done in `d3922bd`**, exactly that way: both screens put their own `ChatPanelWrapper` in
`components` under the chat panel's id and pass the prefix as a prop. `PanelSlot` prefers a
`components` entry over the registry for the *body* only, so the panel keeps its registry header —
the screens did not have to reproduce any chrome. `ChatPanelWrapper` is exported from the barrel
for it. The same prop came off `WorkspaceLayout`, where it had no callers at all: every settings
pane and job detail rendering through it had always been passing its chat panels `undefined`.
- [x] **`dashboardId` is a bag whose _format_ three apps parse.** It is literally `workspace.key` - [x] **`dashboardId` is a bag whose _format_ three apps parse.** It is literally `workspace.key`
(`WorkspaceView.tsx:163`). Consumers reverse-engineer meaning from its shape: (`WorkspaceView.tsx:163`). Consumers reverse-engineer meaning from its shape:
`Chat/ChatPanelWrapper.tsx:51-56` does `dashboardId === 'email' || dashboardId === 'screens/email'` `Chat/ChatPanelWrapper.tsx:51-56` does `dashboardId === 'email' || dashboardId === 'screens/email'`
@@ -531,11 +539,16 @@ directory it is over. `promptPrefix` is the one remaining courier job, and it is
against a workspace with no row to hang it on), and `terminalStateKey` now takes the identity, so against a workspace with no row to hang it on), and `terminalStateKey` now takes the identity, so
it can no longer nest a key inside itself. Seven tests in `workspace-identity.test.ts`, and the it can no longer nest a key inside itself. Seven tests in `workspace-identity.test.ts`, and the
two live agents on `ws-layout-agent-mvp` were confirmed still addressable after the restart. two live agents on `ws-layout-agent-mvp` were confirmed still addressable after the restart.
- [ ] **`WorkspaceLayout.tsx:36` silently omits `root`** — apps inside a `DashboardPreview` fall through - [x] **`WorkspaceLayout.tsx:36` silently omits `root`** — apps inside a `DashboardPreview` fall through
to the `createContext` defaults. (`initialFilePath` and `defaultFileSort` were on this list too and to the `createContext` defaults. (`initialFilePath` and `defaultFileSort` were on this list too and
are now deleted; `workspace` is omitted there on purpose, since a preview is not a place an agent are now deleted; `workspace` is omitted there on purpose, since a preview is not a place an agent
can be addressed.) Whatever survives the three items above should be constructed in one place, not can be addressed.) Whatever survives the three items above should be constructed in one place, not
twice by hand. twice by hand.
**Done in `ca046a3`.** The eleven interaction fields — everything a panel uses to rearrange the
workspace, plus the state those interactions run on — are one exported `inertInteraction`, spread
by `WorkspaceLayout` and by the `createContext` default. `root` stays omitted, and that is now a
stated decision rather than an oversight: it is only ever read when `cwd` is scoped, and no caller
of `WorkspaceLayout` passes a `cwd` at all, so there is nothing for it to be the root *of*.
### 5.10 Channel hygiene — _(found 2026-08-07)_ ### 5.10 Channel hygiene — _(found 2026-08-07)_
@@ -696,3 +709,16 @@ Low priority, but each line here is a line someone will read and believe.
In the same pass, the two file-browser fields the context had grown were deleted, and with them In the same pass, the two file-browser fields the context had grown were deleted, and with them
the prop chain underneath that had no other caller. Net: the context is 16 fields from 18, and the prop chain underneath that had no other caller. Net: the context is 16 fields from 18, and
four dead parameters and a permanently-false flag are gone from the file browser. four dead parameters and a permanently-false flag are gone from the file browser.
- [x] **The framework stopped carrying things only one app understands.** _(`d3922bd` + `ca046a3`,
branch `agent-coordination-mvp`)_ — the last two items of §5.9, and with them the section. A
screen's ~40-word system prompt used to travel screen → `WorkspaceView` → context →
`ChatPanelWrapper`, three of those four steps by a framework that has no idea what the string is.
It is now a prop on the chat panel the screen mounts itself through `components` — the prop that
already existed for supplying a panel's component, and which `PanelSlot` honours for the body
while still taking header and provider from the registry entry, so nothing about the panel's
chrome had to be reproduced.
The second half is the inert context: `WorkspaceLayout` and the `createContext` default had each
written out the same eleven switched-off fields by hand. `inertInteraction` names the idea once.
Net across all of §5.9: the context is 15 fields from 18, and everything still on it answers
"where is this panel" rather than "what should this panel do".