diff --git a/AGENTS.md b/AGENTS.md index 7cd25bcb..9a545ce0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,6 +60,7 @@ Performance invariants — all deliberate, keep them: - The full index is never shipped to or rendered by the UI. Always paginate. - Meta parsing reads at most 256KB per file. Parsers are failure-tolerant: session log formats are provider-internal and drift between releases, so skip anything unreadable rather than fail the scan. +- What a tool call hands the person — a plan, a to-do list, an edit — rides its transcript row as `SessionMessage.artifact`, read off the call's own input in main by `src/main/parsers/artifacts.ts` (shared by the three parsers and the stream parsers in `chat.ts` and `acp-core.ts`) and bounded there (files per call, lines per file, line width), because it crosses IPC on every transcript read. A call that failed is marked on its own row (`failed`), matched to its result by id where the log has one. The renderer only folds these for the Work panel (`work.ts`); it never parses tool JSON. - A parser reports only what its log states (`SessionMeta.logBranch`), never what the disk says. Anything derived from the checkout — `repo`, `isWorktree`, `gitBranch` — is the indexer's `annotate()`, is recomputed on every scan, and is stripped before the stat-cache is written so a renamed remote or a moved worktree can't freeze into it. Branches are the live example: Copilot stopped writing `context.branch` after CLI 1.0.80 and most Codex rollouts carry no `git` block, so for those the checkout's HEAD is the only source. - A session can span several files: Codex paginates a long thread into a new rollout whose `session_meta.history_base` names where the previous one's history ends. The parser reports that (`SessionMeta.historyBase`), `foldThread` in the indexer folds every file carrying one id into a session on the newest file with the earlier ones as `segments`, and anything that reads or removes a session's log — transcript, search, profile, cleanup — reads every file (`sessionLogFiles`; an earlier page only up to its `endByte` where the thread's history is what counts), never `sourcePath` alone. Only liveness reads just the newest file, the one being written. - The stat-cache (mtime+size, persisted to userData) means restarts only re-parse changed files; scans yield to the event loop so IPC never blocks. diff --git a/design-system/cockpit/MASTER.md b/design-system/cockpit/MASTER.md index c300e705..cb0aedcc 100644 --- a/design-system/cockpit/MASTER.md +++ b/design-system/cockpit/MASTER.md @@ -127,7 +127,8 @@ Reuse these; don't invent parallel variants: - **`.rail-resizer`** (`RailResizer.tsx`) — the sash on the rail's right edge: the sidebar's width is the person's, dragged between 200px and what the deck can spare (`rail.ts`, remembered per machine in localStorage, never in config), invisible at rest and an accent hairline on hover, focus and while dragging. A `role=separator` whose value is the rail's *measured* width; ← → move it 16px (64 with ⇧), Home/End reach the bounds, double-click resets. Because the rail is dragged, anything that sheds *inside* it asks the rail (`@container rail`), never the window. See `pages/sidebar.md`. - **Cards:** `.ns-card` — ONE width (`min(760px, 94%)`) shared by every card view (Settings, Agents, Profile, Cleanup, New session); navigating between them must never make the dialog jump sizes. `.composer-card` for the home prompt, in home's own `--home-col`. - **Card tabs** (`TabList` + `TabPanel`, `Tabs.tsx`) — the one way a card view pages its content: a `.pnl-tabs.ns-tabs` row of `.pnl-pill` tabs (hairline under it) over **one** mounted panel. Settings, Agents, Profile and Cleanup all use it. **A tab replaces the panel; nothing in a card scrolls to a heading further down** — a jump row that scrolled took the title, the row and Close off screen with it, and a card of stacked sections read as one long page. Switching mounts the panel fresh (two panels built alike must never share internal state) and puts the card's scroller back at `scrollTop 0`. The row is one tab stop (roving `tabIndex`; ←/→ wrap, Home/End), only the selected tab carries `aria-controls`, and the panel is named by its tab — so a panel holding one group carries no heading that repeats the pill, and a panel holding several names each group with an `h3` that doesn't either. A tab may carry its count (`.pnl-pill-n`, left off at zero), a warn tone or an amber dot. Controls that govern every tab (Cleanup's threshold, Agents' scope, Profile's headline numbers) sit above the row; the pill row must hold in two rows at the 560px floor. A new card view that grows past a screen gets tabs, not a jump row. -- **Chat:** user bubbles right (accent tint), assistant left with avatar; `.tool-row` = collapsed `
` one-liners; `.sys-row` = dotted-left-border annotations; streaming = accent left border. +- **Chat:** user bubbles right (accent tint), assistant left with avatar; `.tool-row` = collapsed `
` one-liners, or a `.tool-open` button when the call carries a plan, to-dos or an edit; `.sys-row` = dotted-left-border annotations; streaming = accent left border. +- **`.work-panel`** (`WorkPanel.tsx`) — the agent's plan, to-dos and edits beside the transcript (chat only, `.btn-work` / ⌘J, or one click on a `.tool-row.tool-open` row): card tabs over one scroller, the review's file blocks for edits, and under 720px of `@container chat-deck` it covers the conversation instead. See `pages/chat.md`. - **`.review`** — the worktree's changes in the transcript's place (chat only, `.btn-review` / ⌘D): the instructions review's `.idiff-*` line grammar with line numbers, a scope switch, and line notes that go back to the agent through the composer. With an open PR it leads with `.review-pr` (what the PR waits on + "Fix with ") and shows reviewers' unresolved threads (`.review-thread`) under their lines. See `pages/chat.md`. - **Semantic count pills:** bordered pill = "session count on a repo"; org counts are plain text. - **`Select`** — the one dropdown (see Native Controls); never a raw `