Skip to content

Commit f82480e

Browse files
authored
Spec suite overhaul: lifecycle convention, verification sweep, and pairing presence (#209)
2 parents 7262215 + bda2824 commit f82480e

46 files changed

Lines changed: 1734 additions & 1147 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 48 additions & 36 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

DESIGN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ This system has no "primary" accent in the brand sense. The closest analogue is
134134
- **Terminal Background / Foreground** (`var(--vscode-terminal-background)` / `var(--vscode-terminal-foreground)`): the terminal content surface and xterm default text. Orthogonal to the chrome.
135135
- **Error** (`var(--vscode-terminal-ansiRed)`): destructive actions and kill-confirm letter flash.
136136
- **Success** (`var(--vscode-terminal-ansiGreen)`): TODO check, theme-store install confirm.
137-
- **Alarm** (`var(--vscode-terminal-ansiYellow)` initial; runtime-rotated): bell-ringing alert tint. Per-surface OKLCH hue-rotation by `use-dynamic-palette.ts` from the bg the bell sits on, so the alert pops off any header.
137+
- **Alarm** (`var(--vscode-terminal-ansiYellow)` baseline; runtime-overridden): bell-ringing alert tint. `computeDynamicPalette()` replaces each `--color-alarm-vs-*` token with plain white or black by the OKLab lightness of the bg the bell sits on (active header, inactive header, or door), so the ringing bell stays maximally legible on any surface.
138138

139139
### Fixed Exception
140140
- **Window Close Hover** (`#b92a1b`): the only literal color in the whole system. Native OS close-button hover on Windows/Linux chrome buttons; matches the platform convention across themes.

docs/specs/alert.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ These do not count as attention: mere visibility, command-mode selection, hover,
4848

4949
Attention is lost when the attention timer expires, the app loses focus, the attended Session is minimized or destroyed, or another Session becomes attended. `T_USER_ATTENTION` also acts as the minimum runtime for command-exit alerts.
5050

51+
Source of truth: `cfg.alert` in `lib/src/cfg.ts` defines `T_USER_ATTENTION` and the other timer defaults and their purpose.
52+
5153
## WATCHING Track
5254

5355
WATCHING is the user-controlled output/silence monitor. It starts fresh when enabled and is disposed when disabled. Meaningful output excludes resize redraw noise during `T_RESIZE_DEBOUNCE`; theme changes, remounts, DOM reparenting, selection, and focus changes are not output.
@@ -61,8 +63,6 @@ WATCHING is the user-controlled output/silence monitor. It starts fresh when ena
6163
| `MIGHT_NEED_ATTENTION` | A busy Session went quiet. Debounce state. |
6264
| `ALERT_RINGING` | WATCHING observed likely completion while the Session lacked attention. |
6365

64-
Source of truth: `cfg.alert` in `lib/src/cfg.ts` defines timer defaults and their purpose.
65-
6666
Source of truth: `ActivityMonitor` in `lib/src/lib/activity-monitor.ts` implements the transitions. The invariants the implementation must honor:
6767

6868
- Output drives the monitor up the chain `NOTHING_TO_SHOW` -> `MIGHT_BE_BUSY` -> `BUSY`; silence drives it down `BUSY` -> `MIGHT_NEED_ATTENTION` -> `ALERT_RINGING`. The `MIGHT_*` states are debounce windows in both directions.
@@ -135,10 +135,10 @@ Supported keys:
135135

136136
`title` and `body` chunks append to the pending notification. Completion rings once if the sanitized title or body is nonempty. If `i` is omitted, only a complete single-sequence notification is meaningful.
137137

138-
Management payloads do not ring:
138+
Management payloads contribute no notification content:
139139

140140
- `p=?` sends a support response advertising the support payload defined in `lib/src/lib/terminal-protocol.ts` (`OSC99_SUPPORT_PAYLOAD`).
141-
- `p=close`, `p=alive`, `p=icon`, and `p=buttons` are consumed or ignored without creating notification UI.
141+
- `p=close`, `p=alive`, `p=icon`, and `p=buttons` are consumed or ignored without creating notification UI of their own. Like any chunk, one carrying the (default) `d=1` done flag still completes a pending same-`i` notification, which may then ring on its previously accumulated title/body — kitty's done-flag semantics apply regardless of the final chunk's payload type.
142142

143143
Source of truth: `lib/src/lib/terminal-protocol.ts` defines the pending OSC 99 chunk TTL and max-pending-id cap.
144144

@@ -177,7 +177,7 @@ Clearing behavior:
177177

178178
> See `docs/specs/glossary.md` for the Workspace / Window containers.
179179
>
180-
> **Partially implemented.** The union *projection* is a pure function — `computeWorkspaceUnion(surfaceIds, activitySnapshot)` in `lib/src/lib/workspace-union.ts` (stage 2b). **VS Code surfacing is implemented**: the editor-tab title and the bottom-panel view badge reflect the union (terminal ring/TODO only; `docs/specs/vscode.md`). Still not built: the **standalone strip indicators** (stage 3). A browser Surface's user-set `todo` round-trips to the activity store live and persists across restart (stage 2a), so it is counted by the projection and shows on the surface's door today.
180+
> The union *projection* is a pure function — `computeWorkspaceUnion(surfaceIds, activitySnapshot)` in `lib/src/lib/workspace-union.ts`. **VS Code surfacing is implemented**: the editor-tab title and the bottom-panel view badge reflect the union (terminal ring/TODO only; `docs/specs/vscode.md`). A browser Surface's user-set `todo` round-trips to the activity store live and persists across restart, so it is counted by the projection and shows on the surface's door today. The **standalone strip indicators** are staged — see `docs/specs/layout.md` `## Future` (workspaces-rollout).
181181
182182
A Workspace projects a **union status** over the attention state of the Surfaces it contains (terminal Sessions and browser Surfaces alike — see `docs/specs/glossary.md`):
183183

@@ -193,10 +193,8 @@ Rules:
193193

194194
Where the union surfaces is host-specific:
195195

196-
- **Standalone:** each inactive Workspace's tab in the strip shows the union `ringing` bell and `todo` pill, reusing the Door indicator vocabulary (`bellIconClass`, the TODO pill). The **active** Workspace's tab shows no union indicator — its rings and TODOs are already visible on its own panes and doors. See `docs/specs/layout.md`.
197-
- **VS Code:** the host reflects the **terminal** portion of each Workspace's union onto the webview's native chrome — an editor tab's icon (and optionally title) and the sidebar view's numeric badge. Browser-surface TODO stays webview-local and is not shown on native chrome until a future webview→host Surface-state channel exists. See `docs/specs/vscode.md`.
198-
199-
This spec fixes the projection and the surfacing rules; the exact visual treatment of the standalone strip is settled in the Storybook UI pass.
196+
- **Standalone:** the workspace strip that will show per-tab union indicators is staged with the rest of the strip UI — the behavioral rules live in `docs/specs/layout.md` `## Future` (workspaces-rollout). Today a browser Surface's `todo` shows on its own door, and terminal rings/TODOs show on their panes and doors as specified above.
197+
- **VS Code:** the host reflects the **terminal** portion of each Workspace's union onto the webview's native chrome — an editor tab's icon (and optionally title) and the sidebar view's numeric badge. Browser-surface TODO stays webview-local; surfacing it on native chrome needs the webview→host Surface-state channel staged in `docs/specs/vscode.md` `## Future`. See `docs/specs/vscode.md`.
200198

201199
## UI Contract
202200

@@ -265,6 +263,19 @@ Security requirements:
265263
- Bell, TODO, preview, and dialog controls must remain keyboard reachable; dialogs trap focus and support `Escape`.
266264
- Tooltips, dialog copy, and future localized TODO labels must wrap in narrow layouts.
267265

266+
## Files
267+
268+
| File | Role |
269+
|------|------|
270+
| `lib/src/lib/activity-monitor.ts` | Per-Session WATCHING state machine (output/silence timers) |
271+
| `lib/src/lib/alert-manager.ts` | `AlertManager`: protocol + command-exit tracks, attention, TODO, notification storage, status projection |
272+
| `lib/src/lib/terminal-protocol.ts` | Notification/progress OSC parsing (`OSC 9` / `9;4` / `99` / `777`, BEL), sanitization limits, OSC 99 chunk state |
273+
| `lib/src/lib/session-activity-store.ts` | React activity snapshot store, primed alert state, platform delegates |
274+
| `lib/src/lib/workspace-union.ts` | `computeWorkspaceUnion` projection |
275+
| `lib/src/components/bell-icon-class.ts` | Bell tilt/animation mapping from public status |
276+
| `lib/src/components/wall/TerminalPaneHeader.tsx` | Bell button, TODO pill, notification preview and dialog |
277+
| `lib/src/components/Door.tsx` | Door bell + TODO display |
278+
268279
## References
269280

270281
- iTerm2 proprietary escape codes (OSC 9, OSC 9;4): https://iterm2.com/documentation-escape-codes.html

docs/specs/auto-update.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ app launch
99
1010
├─ check for post-install markers in localStorage
1111
│ ├─ success marker → show "Updated to vX.Y.Z" banner (auto-dismisses after 10s)
12-
│ ├─ failure marker → show "Update failed." banner with debug action
12+
│ ├─ failure marker → show "Update failed" banner with debug action, then STOP —
13+
│ │ the update check is skipped this launch (re-prompting for the version that
14+
│ │ just failed would unmount an open debug dialog)
1315
│ └─ no marker → continue
1416
1517
├─ wait 5 seconds
@@ -61,7 +63,7 @@ When a notice has follow-up actions, it uses ` · ` as the separator between the
6163

6264
All states are dismissible via [×]. Dismissing an unapproved `available` notice means no update is downloaded or installed in that session. Dismissing a `downloading` or `downloaded` notice hides it for the session only — it does not cancel an already-approved download/install.
6365

64-
The notice matches the Baseboard's existing text style (9px mono, `text-muted`). It's pushed right via `ml-auto` so it doesn't compete with doors or the shortcut hint on the left.
66+
The notice matches the Baseboard's existing text style (`text-sm font-mono text-muted` — 12px via the theme.css `text-sm` override). It's pushed right via `ml-auto` so it doesn't compete with doors or the shortcut hint on the left.
6567

6668
### Threading
6769

docs/specs/deploy.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ If you edit `CHANGELOG.md` manually outside `/release-notes` and want to preview
195195
| `OVSX_PAT` | `vscode-extension-publish` GitHub environment secret | OpenVSX publish |
196196
| `GITHUB_TOKEN` | GitHub Actions (automatic) | Artifact upload |
197197
| `APPLE_SIGNING_IDENTITY` | Local keychain | macOS codesign |
198-
| `APPLE_ID` | Local env / prompted | Notarization |
198+
| `APPLE_ID` | Hardcoded in `sign-and-deploy.sh` | Notarization |
199199
| `APPLE_SIGN_PASS` | Local env / prompted | Notarization password |
200200
| `APPLE_TEAM_ID` | Local env / hardcoded | Notarization |
201201
| `EV_SIGN_PIN` | Local env / prompted | Windows PIV signing |
202-
| `TAURI_SIGNING_PRIVATE_KEY` | Local env | Tauri update signatures |
202+
| `TAURI_SIGNING_PRIVATE_KEY` | Local env / prompted | Tauri update signatures |
203203
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Local env / prompted | Tauri update key password |

docs/specs/dor-browser.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ Surface lifetime owns backing resources:
8686
- Killing or swapping an agent-browser pane also disposes its surface controller
8787
(`disposeAgentBrowserSurfaceController`), releasing the connection, screenshot
8888
loop, CDP observer, timers, and screen registration. This is the immediate
89-
per-surface teardown hook Future Work still lists generally; it exists today
90-
for agent-browser surfaces.
89+
per-surface teardown hook that [Future](#future) still lists generally; it
90+
exists today for agent-browser surfaces.
9191
- Iframe proxy grants are currently reclaimed by the proxy idle sweep, not by an
9292
immediate per-surface teardown hook.
9393

@@ -112,6 +112,10 @@ Header contract:
112112
HTML title is tooltip/secondary state.
113113
- Clicking the URL opens an inline editor. `normalizeNavUrl` keeps explicit
114114
schemes, uses `http://` for bare loopback hosts, and `https://` otherwise.
115+
For an iframe-rendered pane a bare remote hostname therefore resolves to
116+
`https://`, which the proxy cannot instrument — the pane shows the scheme
117+
error with its `dor ab` hint. This is intended: remote sites are steered to
118+
the agent-browser renderer rather than silently proxied over plain HTTP.
115119
- Back, forward, and reload are always enabled. Agent-browser sends native
116120
`back` / `forward` / `reload`; iframe uses parent-side history and re-resolves
117121
the proxy on reload/back/forward.
@@ -271,7 +275,9 @@ screenshot through the host's `agentBrowserScreenshot`, and draws that to canvas
271275
with latest-only backpressure. A capture whose bytes are identical to the last
272276
displayed frame (a static page the daemon keeps re-pulsing) costs no decode or
273277
draw; a re-attach bumps a draw generation so a fresh blank canvas still repaints.
274-
If the host cannot screenshot, it falls back to the stream frame path.
278+
A host without `agentBrowserScreenshot` renders only the placeholder — stream
279+
frame bytes are discarded by design (the connection reduces every frame message
280+
to a `frame-pulse`), so there is no frame-drawing fallback path.
275281

276282
The high-rate `[ab-panel]`/`[agent-browser]` stream and screenshot console
277283
diagnostics sit behind the `dormouse.flags.abDebugLogs` localStorage flag, read
@@ -493,7 +499,17 @@ Source of truth: `lib/src/lib/platform/types.ts`,
493499
`standalone/src/tauri-adapter.ts`, `standalone/src-tauri/src/lib.rs`,
494500
`standalone/sidecar/main.js`.
495501

496-
## Future Work
502+
## Maintainer checklist
503+
504+
When changing browser-surface behavior:
505+
506+
- `renderMode` is canonical. Never reintroduce `surfaceType: 'iframe' | 'agent-browser'` or `poppedOut` as stored stateextend the `resolveRenderMode` migration instead, and update the kind-mapping table in `docs/specs/glossary.md` if adding a render mode.
507+
- Every browser panel keeps dockview `renderer: 'always'`moving iframe DOM reloads it, and moving the screencast canvas mid-click breaks click synthesis.
508+
- A new agent-browser subcommand must be added to `AGENT_BROWSER_ALLOWED_SUBCOMMANDS` (`lib/src/lib/platform/types.ts`); the host-side allowlist is the security boundary, not the CLI.
509+
- External-binary spawns go through `spawnAndCapture` (`dor-lib-common`), never raw `child_process`see `docs/specs/dor-cli.md`Spawning External Binaries.
510+
- New kill/swap/teardown paths must run `closeAgentBrowserSession` **and** dispose the surface controller (`disposeAgentBrowserSurfaceController`), and respect the closed-session mark so pop-out auto-revert cannot resurrect a killed session.
511+
512+
## Future
497513

498514
- Stable agent-browser profile/state persistence so pop-out preserves logins,
499515
cookies, tabs, DOM state, and scroll.

docs/specs/dor-cli.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,18 @@ Invariants:
190190
`surface:1`, `pane:2`.
191191
- List output defaults to refs; commands that list handles accept
192192
`--id-format refs|uuids|both`.
193-
- Workspace/window refs are defined now that Dormouse supports multiple
194-
Workspaces: `workspace:<n>` (and `workspace:<name>` when exactly one Workspace
195-
matches) and `window:<n>` select a container. A `--workspace` target flag and
196-
`dor workspace` management commands (list / new / rename / close / switch) are
197-
the next handles to expose; like every other command they ship with their
198-
snapshot-tested help and the control methods that back them, not ahead of them.
193+
- Reserved: `workspace:<n>` (and `workspace:<name>` when exactly one Workspace
194+
matches) and `window:<n>` select a container. The ref grammar is reserved now
195+
so surface/pane refs never collide with it; the flag and commands that consume
196+
it are staged — see [Future](#future).
199197

200198
## Current Implemented Commands
201199

202200
Implemented commands call private `surface.*` control methods. `surface.list`
203201
derives its response from current Dockview panels plus terminal state/activity
204-
snapshots where available, then returns `workspace:1` and `window:1`. Once
205-
`surface.list` is made Workspace-aware it tags each surface with the real
206-
`workspace:<n>` / `window:<n>` membership defined above; until then it reports the
207-
single active Workspace.
202+
snapshots where available, then returns `workspace:1` and `window:1` — it
203+
reports the single active Workspace (Workspace-aware tagging is staged; see
204+
[Future](#future)).
208205

209206
Command tails captured after `--` are sent as raw argv arrays (`command:
210207
string[]`); the host — not `dor` — quotes them for the target shell. `dor`
@@ -240,3 +237,16 @@ from `command-detail`.
240237
unified `browser` surface, see [dor-browser.md](dor-browser.md)
241238
- `dor list-panes` [impl](../../dor/src/commands/list-panes.ts) [docs](../../dor/test/snapshots/help/list-panes.md)
242239
- `dor list-pane-surfaces` [impl](../../dor/src/commands/list-pane-surfaces.ts) [docs](../../dor/test/snapshots/help/list-pane-surfaces.md)
240+
241+
## Future
242+
243+
- **Workspace handles and commands** — a `--workspace` target flag and `dor
244+
workspace` management commands (list / new / rename / close / switch)
245+
consuming the reserved `workspace:<n|name>` / `window:<n>` ref grammar in the
246+
handle model above. Like every other command they ship with their
247+
snapshot-tested help and the control methods that back them, not ahead of
248+
them. Staged with the workspaces rollout (`docs/specs/layout.md` `## Future`,
249+
workspaces-rollout).
250+
- **Workspace-aware `surface.list`** — tags each surface with its real
251+
`workspace:<n>` / `window:<n>` membership instead of reporting the single
252+
active Workspace.

0 commit comments

Comments
 (0)