git:20260905.f582517 to git:20260905.848dd6d

54 added, 241 removed. Audit A to A.

# AGENTS.md - apps/canvas-workspace
- > Local entry for `apps/canvas-workspace`.
- > Repository harness entry: `../../harness/README.md`.
- > `CLAUDE.md` is a thin import shell of this file — edit here, never both.
-
- ## Module Positioning
+ Local router for Pulse Canvas. Read root AGENTS and harness/README.md first. CLAUDE.md is a thin import shell; edit this file.
- `canvas-workspace` owns the Pulse Canvas Electron workbench: the desktop shell,
- React renderer, preload bridge, canvas persistence and migration, Canvas Agent
- chat, agent-team flows, plugin loading, embedded webviews, terminal/agent PTYs,
- artifacts, settings, local runtime-control server, and app-specific harness.
+ ## Role and ownership
- This is an active pnpm app workspace. It consumes `pulse-coder-engine` and
- `pulse-coder-agent-teams`, interoperates with `@pulse-coder/canvas-cli` through
- the canvas store and runtime-control server, and hosts external node plugins
- through manifests and plugin registries (runtime-loadable plugin dirs; the
- former in-repo example `packages/canvas-nodes` was removed in the 2026-08
- slim-down — see tag `pre-slim-archive` for a reference implementation).
+ This active pnpm workspace owns the Electron shell, React renderer, preload bridge, canvas persistence/migration, Canvas Agent chat, agent teams, plugins, webviews, terminal/agent PTYs, artifacts, settings, and runtime-control server. It consumes engine/agent-teams and interoperates with canvas-cli through storage and runtime capabilities.
- Keep this file as the local router. Put durable implementation detail in
- existing workspace docs or tests. Add new workspace docs only when a behavior
- or operating runbook needs a durable source of truth.
+ The app owns migrations, PTYs, plugin activation, runtime endpoints, and UI-visible recovery; the CLI adapts. External plugin-node history and examples belong to plugin-node-mf2.md.
- **Local harness layout** — `harness/` is this workspace's repo-harness
- container, aligned with `packages/engine/harness/`:
- - `harness/knowledge/` — per-topic knowledge files, routed in the Knowledge
- Navigation table below (`docs/` keeps only project records like perf
- analyses and roadmaps).
- - `harness/tools/driver/` — the headless-Electron driver (launch profiles,
- CDP, screenshots, logs). It is BOTH the repo-harness Tool face and a
- product-operation CLI; `pnpm --filter canvas-workspace harness <cmd>`
- still points at it.
- - `harness/tools/describe-canvas.mjs` — static structure snapshot: agent-tool
- registry, IPC handle↔invoke contract diff, node-type union↔factory
- sync, and app↔canvas-cli parity (NodeType union subset + storage
- schema-version constants). Run before touching any of those registries;
- exits non-zero on a broken invoke, union/factory drift, a CLI-only node
- type, an unallowlisted app-only type, or a schema-version mismatch.
- - `harness/tools/ui-showcase/` — Playwright screenshot baseline for
- `components/ui/` (the blessed design-system set): a plain-React vite page
- (zero Electron/preload imports) mounting every `ui/` piece in its
- meaningful variants/states, plus the one Playwright spec that captures
- and compares against committed baselines. `pnpm run visual` /
- `visual:update` from this package; Linux-rendered baselines only (fonts
- differ per OS) — see `harness/tools/ui-showcase/README.md`. Built as the
- prerequisite for `docs/ui-reuse-burndown.md`'s Batch C3.
- - `harness/skills/` — SKILL.md procedures for coding agents operating this
- app: `canvas-harness`, `canvas-onboard-harness` (drive the real app),
- `diagnose-agent-latency` (trace and optimize Agent turn latency),
- `validate-canvas-change` (choose quick/standard/release evidence),
- `check-renderer-structure` (audit renderer directory/module health),
- `add-canvas-node`, `add-agent-tool`, `add-builtin-main-plugin`,
- `extend-blessed-ui`, `add-ipc-surface` (safe-change procedures for the
- five recurring extension shapes).
- - `harness/spec/` — decision-pending intent; empty is the success state. Surface definition: ../../packages/engine/harness/spec/README.md; resolved entries end as tests/skills, then delete.
- - `harness/validate/validation.yaml` — path→check bindings for the repo runner.
+ harness/ is the repo container for Knowledge, Tools, Validate, Skills, and optional Spec. The driver is also a product-operation CLI. Repo action skills are distinct from src/main/agent/skills runtime features and the externally installed pulse-canvas skills. Existing docs/ holds project records.
- **"skills" disambiguation** — `harness/skills/*/SKILL.md` are procedures for
- CODING agents working on this app; `src/main/agent/skills/` is the PRODUCT
- runtime-skills feature of the in-app Canvas Agent; `files/skill-installer.ts`
- deploys the external-agent `pulse-canvas` CLI + bundled skills. Do not mix them.
+ ## Required task routes
- ## Knowledge Navigation
+ A matching trigger requires reading its owner before changing code. Keep detailed mechanisms and test inventories there.
- | Task | Read |
+ | Change / task | Required resource |
|---|---|
- | Repository harness and root validation | `../../harness/README.md`, `../../harness/validate/validation.yaml` |
- | App overview | `README.md` |
- | Drive the real app (launch/CDP/screenshot) | `harness/tools/driver/README.md`, `harness/skills/canvas-harness/SKILL.md`, `harness/skills/canvas-onboard-harness/SKILL.md`; what "correct" looks like: `harness/knowledge/renderer-surfaces.md` |
- | Security posture, agent execution reach, disk/config surfaces | `harness/knowledge/security-posture.md` |
- | Confirmed-but-unfixed defects | `harness/knowledge/known-defects.md` |
- | Main/renderer/preload boundaries | `harness/knowledge/conventions/README.md`, `harness/knowledge/conventions/architecture-boundaries.md` |
- | Renderer conventions | `harness/knowledge/conventions/frontend.md` |
- | Renderer current→module-first architecture and directory health | `harness/spec/renderer-modules/README.md`; run `harness/skills/check-renderer-structure/SKILL.md` |
- | Main-process conventions | `harness/knowledge/conventions/backend.md` |
- | PATH for anything the app spawns | `src/main/shell-path.ts` — a GUI launch inherits a stripped PATH, and every child (agent `bash`, which the engine spawns with NO `env`, MCP stdio servers, the bundled CLI) takes `process.env` verbatim, so a missing binary surfaces as a bare "command not found". Repaired once in `bootstrap.ts` before any spawn: `augmentProcessPath()` (sync, well-known per-user bin dirs incl. `~/.pulse-coder/bin`) then a best-effort `applyLoginShellPath()` (async, timeout-bounded `$SHELL -ilc`, only ever widens). PTY env shares the same bin-dir list — do not fork a second copy. Tests: `src/main/__tests__/shell-path.test.ts` |
- | Main domain map | `harness/knowledge/main-domain-modules.md`, `src/main/index.ts`, `src/main/app/bootstrap.ts` |
- | Renderer routes and full-app surfaces | `harness/knowledge/renderer-surfaces.md`, `src/renderer/src/app/App/`, `src/renderer/src/app/shell/Workbench/`, `src/renderer/src/modules/dock/internal/RightDock/` |
- | Keyboard shortcuts | Read `harness/knowledge/keyboard-shortcuts.md` before adding/changing/removing a shortcut, editing `menu.ts` accelerators, or touching webview/terminal key handling. Key contracts: `shortcuts/definitions.ts`, `shortcuts/registry.ts`, `modules/canvas/runtime/useCanvasKeyboard.ts`, `app/shortcuts/useAppShortcuts.ts`, `src/main/app/menu.ts`, `src/shared/webview-shortcuts.ts`. Bound tests (`keyboard-shortcuts` rule, `harness/validate/validation.yaml`): `shortcuts/registry.test.ts`, `modules/canvas/runtime/useCanvasKeyboard.test.ts`, `app/shortcuts/useAppShortcuts.test.ts`, `src/main/webview/__tests__/shortcut-forwarding.test.ts`, `AgentNodeBody/utils/terminalKeys.test.ts`, `AgentNodeBody/utils/terminalFocus.test.ts`, `shortcuts/terminalShortcuts.test.ts`. |
- | Cross-process API bridge | `src/preload/index.ts`, `src/preload/bridge/`, `src/renderer/src/types.ts`, `src/shared/` |
- | Add a capability spanning main + preload + renderer | `harness/skills/add-ipc-surface/SKILL.md` (ordered procedure — contract placement, streaming pattern, bootstrap wire, lockstep rule) |
- | Canvas node/edge schema | `src/shared/canvas.ts` |
- | Add a new canvas node capability | `harness/skills/add-canvas-node/SKILL.md` (ordered procedure — plugin is the default path, host type is the exception); background: `harness/knowledge/plugin-node-mf2.md` (plugin path), `src/shared/canvas.ts`, `src/renderer/src/utils/nodeFactory.ts`, `src/renderer/src/modules/canvas/components/canvas/CanvasNodeView/` (host-type touch points) |
- | Current registries (agent tools / IPC pairs / node types) | run `node harness/tools/describe-canvas.mjs` (from this dir; `--json` for machines) |
- | Visual-regression baseline for ui/ pieces | `harness/tools/ui-showcase/README.md`; run `pnpm run visual` / `pnpm run visual:update` |
- | Canvas persistence and migration | `src/main/canvas/store.ts`, `src/main/canvas/storage.ts`, `src/main/canvas/nodes/` (NB: `nodes/` here = knowledge-node records + tags, NOT node types) |
- | Node Detail (knowledge-node detail surface) | Read `harness/knowledge/node-detail.md` before changing the panel/host split, `enterNodePage`, the record→CanvasNode adapter, `nodeDetailDescriptor.ts`, save-failure handling, or missing-node/cross-surface/Escape behavior. Key contracts: `WorkspaceNodes/NodeDetailPanel.tsx`, `WorkspaceNodes/nodeDetailDescriptor.ts`, `WorkspaceNodes/NodeCanvasPreview.tsx`, `RightDock/dock-store.ts`, `WorkspaceNodes/useWorkspaceNodes.ts`, `Workbench/KnowledgeChatPortal.tsx`. Tests: `WorkspaceNodes/__tests__/NodeDetailPanel.test.tsx`, `RightDock/__tests__/dock-store.test.ts`, `Workbench/__tests__/ChatDockLifecycle.test.tsx` |
- | Canvas Agent and tools | `src/main/agent/`, `src/main/agent/tools/`, `src/renderer/src/modules/chat/components/` |
- | Diagnose Canvas Agent latency or verify an optimization | `harness/skills/diagnose-agent-latency/SKILL.md`; trace model and exporter details: `harness/knowledge/langfuse-observability.md` |
- | Canvas Agent full-chain Langfuse observability | `harness/knowledge/langfuse-observability.md` |
- | xterm sizing for agent/terminal surfaces | Read `harness/knowledge/terminal-surfaces.md` before changing xterm sizing/fit behavior for agent nodes, terminal nodes, or the workspace terminal dock. Key contracts: `AgentNodeBody/utils/terminal.ts`, `TerminalNodeBody/index.tsx`, `WorkspaceTerminalDock/index.tsx`. Tests: `AgentNodeBody/utils/terminalFit.test.ts` |
- | AI Chat loading states | Read `harness/knowledge/chat-sessions.md` before changing session loading flags (`sessionsLoading` vs `sessionLoading` vs stream `loading`) or the submit veto. Key contracts: `src/renderer/src/modules/chat/sessions/useChatSessions.ts`, `modules/chat/components/ChatComposer/useChatComposerInput.ts`, `modules/chat/components/ChatSessionsRail/index.tsx`, `modules/chat/components/ChatPanel/ChatHeader.tsx`, `modules/chat/components/ChatMessages/ChatThreadSkeleton.tsx`. Tests: `modules/chat/sessions/useChatSessions.test.tsx`, `modules/chat/components/ChatComposer/__tests__/useChatComposerInput.submit-veto.test.tsx`, `modules/chat/components/__tests__/ChatSessionLoading.test.tsx` |
- | Full-page chat ↔ dock Tabs | Read `harness/knowledge/chat-sessions.md` before changing the full-page chat topbar, its dock-content-tabs toggle, or the dock inset rule. Key contracts: `chat/ChatPageBody/index.tsx`, `RightDock/dock-content-tabs.ts`. Tests: `RightDock/__tests__/dock-content-tabs.test.ts`, `RightDock/index.test.tsx` |
- | Dock width policy | Read `harness/knowledge/chat-sessions.md` before changing right-dock width behavior. Key contract: `RightDock/dock-width.ts`. Tests: `RightDock/__tests__/dock-width.test.ts`, `RightDock/index.test.tsx` |
- | Coding agents in the Coding Agent node (roster, brand marks, launch flags, per-node session binding) | Read `harness/knowledge/coding-agent-registry.md` before adding/removing an agent, changing per-agent launch flags, or changing how a node binds to one CLI conversation. Key contracts: `src/renderer/src/config/agentRegistry.ts`, `modules/coding-agent/session/piSession.ts`, `src/main/agent/codex-sessions.ts`. Bound tests (`coding-agent-roster` rule, `harness/validate/validation.yaml`): `AgentNodeBody/__tests__/AgentPicker.test.tsx`, `AgentNodeBody/__tests__/AgentIcon.test.tsx`, `AgentNodeBody/__tests__/piSessionBinding.test.tsx`, `modules/coding-agent/session/piSession.test.ts`, `utils/__tests__/codingAgentCommand.test.ts`. |
- | Multi-role chat (@角色 group chat + relay) | Read `harness/knowledge/agent-roles.md` before changing role marker/routing, the relay stream protocol, handoff policy, renderer role rendering, external CLI drivers, or the stopped-vs-failed turn rule. Key contracts: `src/shared/agent-roles.ts`, `src/main/agent/role-turn.ts`, `src/main/agent/roles-store.ts`, `src/main/agent/external/`, `src/main/agent/chat-stop.ts`. Tests: `src/main/agent/__tests__/role-turn.test.ts`, `src/main/agent/__tests__/external-driver.test.ts`, `src/main/agent/segment-execution.test.ts`, `src/main/agent/chat-stop.test.ts`. |
- | Agent long-term memory (global + per-workspace) | `src/main/agent/memory-store.ts` (store + prompt injection; explicit-save-only by design), `src/main/agent/tools/memory.ts` (`memory_save` eager; `memory_list`/`memory_forget`/`memory_adopt` deferred — `memory_adopt` is the sole cross-workspace write path, reserved for user-confirmed candidates from the `memory-review` default skill), tests in `src/main/agent/__tests__/memory-store.test.ts` + `tools-graph.test.ts` |
- | Artifact runtime capabilities (page → host actions) | `src/shared/artifact-capabilities.ts` (trust model + contract), `src/main/artifacts/capability-ipc.ts` (`artifact-capability:invoke`, main-side authoritative validation), `src/renderer/src/modules/artifacts/internal/capabilityBridge.ts` + `ArtifactTabView` (host-authored bridge script, postMessage relay, audit toast). Capabilities are declared on the artifact RECORD by creating code (never by the page), gated on a real user gesture in the bridge, and every write surfaces a toast. Current capabilities: `memory.adopt`, `skill.save` (both = the user's click IS the confirmation). Tests: `src/main/artifacts/__tests__/capability-invoke.test.ts` |
- | Artifact pin lifecycle + Library drawer | `src/main/artifacts/ipc.ts` — pin refuses sentinel (`__*`) scopes, dedupes against a live mirror, list/get lazily clear a stale `pinnedNodeId`, delete removes the canvas mirror node; `artifact:list-all` (metadata-only summaries) skips `__*` dirs EXCEPT `__global_chat__` (session-store sentinel rule — a blanket skip silently hides global artifacts). Library drawer = renamed ReferenceDrawer: Pinned entries persist per workspace via the `references` IPC domain (`src/main/references/`, `src/shared/references.ts`, hydrate/save in `Workbench/useReferenceEntries.ts`); Artifacts source tab is `ReferenceDrawer/ArtifactsPicker.tsx` (cross-scope pin disabled by design). Tests: `src/main/artifacts/__tests__/pin-lifecycle.test.ts` |
- | Headless (background) agent runs | `src/main/agent/headless-run.ts` (one-shot bounded Engine run: no session store, `builtInTools:{}` = structurally read-only, wall-clock timeout, never throws), `src/main/agent/memory-report.ts` (first consumer — cross-workspace memory report as self-contained HTML; adoption stays interactive-only; scheduled entry archives to `<memory>/reports/` with rolling retention AND publishes a `__global_chat__`-scoped artifact, surfaced by an OS notification whose click pushes `dock:open-artifact`). Tests: `src/main/agent/__tests__/headless-run.test.ts` |
- | Scheduled tasks | Read `harness/knowledge/scheduled-tasks.md` before changing cadence/DST math, catch-up or `intervalMinutes` migration, the run-finished toast/dock-chat chain, or the scheduled session-store vocabulary. Key contracts: `src/shared/scheduled.ts`, `src/main/scheduled/`, `src/shared/agent-chat.ts`, `src/renderer/src/shared/dock/dock-chat-availability.ts`, `src/main/agent/tools/scheduled.ts`. Tests: `src/shared/scheduled.test.ts`, `src/main/scheduled/scheduled-task-service.test.ts`, `src/main/__tests__/scheduled-run-notify.test.ts`, `src/renderer/src/shared/dock/dock-chat-availability.test.ts`. |
- | Dock web tabs (the embedded browser) | Read `harness/knowledge/dock-browser.md` before changing guest navigation, identity/routing, retention, focus, shortcuts, or tab overflow. Key contracts: `src/shared/webview-registration.ts`, `src/shared/link-open.ts`, `src/shared/dock-shortcuts.ts`; main policy/registry under `src/main/app/` + `src/main/webview/`; renderer ownership under `IframeNodeBody/webview-identities.ts`, `RightDock/`, and `LinkDrawer/`. |
- | Add a capability shared by Tool + CLI | `../../harness/skills/add-canvas-capability/SKILL.md`; use `harness/skills/add-agent-tool/SKILL.md` for the optional task-specific Canvas Agent adapter |
- | Agent teams | `src/main/agent-teams/`, `src/renderer/src/modules/agent-team/` |
- | Runtime-control server | `src/main/runtime/control-server.ts` |
- | Agent Plugins market, package precedence, install/trust state, and MCP adaptation | `harness/knowledge/plugin-market.md`, `src/shared/plugin-market.ts`, `src/main/plugin-market/` |
- | Plugin node contract | `harness/knowledge/plugin-node-mf2.md`, `src/plugins/types.ts`, `src/plugins/main/`, `src/plugins/renderer/`, `src/plugins/mock-node/` |
- | Project records (perf analyses, roadmaps) | `docs/` |
- | Channel plugin | `src/plugins/main/channel/README.md`, `src/plugins/main/channel/` |
- | Boundary, file-size, and UI-reuse gates | `src/main/__tests__/import-boundaries.test.ts`, `src/main/__tests__/file-size-governance.test.ts`, `src/main/__tests__/ui-reuse-governance.test.ts` |
- | Storage/plugin/runtime tests | `src/main/canvas/__tests__/storage.test.ts`, `src/plugins/main/__tests__/registry.test.ts`, `src/main/runtime/__tests__/control-server.test.ts` |
- | Local validation | `harness/validate/validation.yaml` |
- | Choose proportionate local validation | `harness/skills/validate-canvas-change/SKILL.md` |
+ | Main/renderer/preload boundary, shared contracts | `harness/knowledge/conventions/architecture-boundaries.md`, `harness/skills/add-ipc-surface/SKILL.md` |
+ | Renderer conventions / module architecture | `harness/knowledge/conventions/frontend.md`, `harness/spec/renderer-modules/README.md`, `harness/skills/check-renderer-structure/SKILL.md` |
+ | Main conventions / domain and entry map | `harness/knowledge/conventions/backend.md`, `harness/knowledge/main-domain-modules.md` |
+ | Full-app routes, shell, Library, dock placement | `harness/knowledge/renderer-surfaces.md` |
+ | Child-process PATH, bundled CLI/skills, install/repair queue | `harness/knowledge/packaged-tooling.md` |
+ | Chat runtime/queue/stop, sessions/loading, approvals, attachments, rail, dock chat and widths | `harness/knowledge/chat-sessions.md` |
+ | Multi-role chat, external drivers, relay, stopped-versus-failed behavior | `harness/knowledge/agent-roles.md` |
+ | Keyboard shortcuts, menu accelerators, terminal/webview key ownership | `harness/knowledge/keyboard-shortcuts.md` |
+ | Agent/terminal xterm fit and sizing | `harness/knowledge/terminal-surfaces.md` |
+ | Coding Agent roster, branding, launch flags, session binding | `harness/knowledge/coding-agent-registry.md` |
+ | Dock browser, focus/navigation, retention/capture, overflow | `harness/knowledge/dock-browser.md` |
+ | Knowledge-node detail panels and cross-surface behavior | `harness/knowledge/node-detail.md` |
+ | Storage, edges, mindmap transactions, artifact pins | `harness/knowledge/main-domain-modules.md`, `src/shared/canvas.ts` |
+ | Runtime security, memory/adoption, artifact capabilities, headless runs | `harness/knowledge/security-posture.md` |
+ | Schedules, catch-up/DST, completion notification flow | `harness/knowledge/scheduled-tasks.md` |
+ | Agent plugins market, package/trust state, MCP adaptation | `harness/knowledge/plugin-market.md` |
+ | Add node capability / plugin node contract | `harness/skills/add-canvas-node/SKILL.md`, `harness/knowledge/plugin-node-mf2.md` |
+ | Add agent tool / built-in main plugin | `harness/skills/add-agent-tool/SKILL.md`, `harness/skills/add-builtin-main-plugin/SKILL.md` |
+ | Capability shared by Tool + CLI | `../../harness/skills/add-canvas-capability/SKILL.md` |
+ | Latency and tracing | `harness/skills/diagnose-agent-latency/SKILL.md`, `harness/knowledge/langfuse-observability.md` |
+ | Add/reuse a UI primitive or verify visual baseline | `harness/skills/extend-blessed-ui/SKILL.md`, `harness/tools/ui-showcase/README.md` |
+ | Agent teams / channel plugin | `src/main/agent-teams/`, `src/renderer/src/modules/agent-team/`; `src/plugins/main/channel/README.md` |
+ | Drive the app / choose validation | `harness/skills/canvas-harness/SKILL.md`, `harness/skills/canvas-onboard-harness/SKILL.md`, `harness/skills/validate-canvas-change/SKILL.md` |
+ | Known defects / current check bindings | `harness/knowledge/known-defects.md`, `harness/validate/validation.yaml` |
- ## Local Constraints
+ ## Hard boundaries
- - Renderer code reaches privileged behavior only through the typed
- `window.canvasWorkspace` preload API. Do not import Electron, Node, `src/main`,
- or `src/preload` from renderer code.
- - Cross-process contracts should move toward `src/shared/`. Existing preload
- imports from `src/renderer/src/types.ts` are allowlisted migration debt; do
- not add new preload-to-renderer imports.
- - Keep main-process code in domain folders under `src/main/`; preserve
- IPC channel names and preload API shape when refactoring.
- - Agent tool names, schemas, and descriptions under `src/main/agent/tools/`
- ship in the main bundle. Keep descriptions concise and run the bundle gate
- for tool-surface growth; repeated usage prose belongs in the system prompt.
- - Follow file-size governance: new production `.ts`/`.tsx` files must stay at
- or below 500 lines, and existing over-500 baseline files must not grow.
- - Runtime data belongs under user locations such as `~/.pulse-coder/canvas/`,
- `~/.pulse-coder/canvas-runtime/`, and model/settings files. Do not write user
- runtime state into the repository.
- - Packaged builds install `@pulse-coder/canvas-cli` + all bundled skills as one versioned, fingerprinted compatibility bundle under `~/.pulse-coder/`; route every install/repair/update trigger (startup, Settings repair, experimental-flag triggers) through the shared `AgentToolingManager` / `agent-tooling-queue`, never a source checkout, `pnpm`, or a global link.
- Guard: `packaged-agent-tooling` rule in `harness/validate/validation.yaml` (`agent-tooling-manager.test.ts`, `agent-tooling-queue.test.ts`, `shell-path.test.ts`, `agent-tooling-package.test.ts`).
- Detail: `harness/knowledge/packaged-tooling.md`.
- - `harness/tools/driver/` launches the real Electron app. Use `temp`, `demo`,
- or `clone` profiles by default; use `real --allow-real-writes` only after
- explicit user intent because it can mutate real Pulse Canvas data.
- Reopening `demo` without `--reset` preserves its existing manifest and
- imported workspaces; fixture reseeding is a reset operation.
- - Canvas Agent scope activation must stay single-flight in `src/main/agent/service.ts`, or an unguarded check-then-initialize creates duplicate engines for one scope and session switching visibly stalls.
- Guard: `src/main/agent/__tests__/service-history.test.ts`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - Chat run state is conversation-owned: each conversation key has an independent runtime (`conversation-runtime/` in main, `src/renderer/src/modules/chat/runtime/conversationStore.ts` + `useChatComposerController` in renderer) over one shared CanvasAgent engine; switching = changing the store selector, two conversations run in parallel, same-conversation second turn queues. Legacy compensation hooks were deleted; `ActiveChatRegistry` stays only for scheduled/back-compat.
- Guard: `conversation-runtime/*.test.ts`, `shared/conversation-runtime.test.ts`, `src/renderer/src/modules/chat/runtime/conversationStore.test.ts`, `modules/chat/runtime/useConversationRuntimeStream.test.tsx`, `modules/chat/runtime/useConversationRuntimeStream.shared-snapshot.test.tsx`, `ChatComposer/__tests__/useChatComposerController.test.tsx`, `active-chat-registry.test.ts`, `useChatPagePendingSession.test.tsx`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - The renderer has one visible approval card, so main must serialize concurrent clarification requests, starting each timeout only once visible; answering one must reveal, not clear, the next queued request.
- Guard: `clarification-registry.test.ts`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - Conversation pointer changes are fail-closed: archive publication and the replacement current-session write complete before the in-memory pointer advances, with collision-safe archive filenames; deleting the current session reverses that order, and post-commit cleanup stays best-effort in both directions.
- Guard: `src/main/agent/__tests__/session-store.test.ts`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - Chat image uploads are bounded again in main before a prepared run is accepted; removing a ready draft attachment deletes its file, clearing a sent draft retains it, and a failed turn persists its tool-call snapshot with unfinished tools settled.
- Guard: `src/renderer/src/modules/chat/attachments/useChatAttachments.test.tsx`, `chat-protocol.test.ts`, `chat-failure-persistence.test.ts`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - An external-role driver rejection after its AbortSignal fires is a stopped turn, never a failed turn.
- Guard: `segment-execution.test.ts`, `chat-stop.test.ts`.
- Detail: `harness/knowledge/agent-roles.md` (stopped-vs-failed turn rule).
- - The full-screen chat rail is one stable cross-scope projection: never swap per-scope list caches into it or fetch a list while `loadSession` is promoting an archive; commit current/other lists together after promotion.
- Guard: `src/renderer/src/modules/chat/sessions/useChatSessions.test.tsx`, `src/renderer/src/modules/chat/components/ChatSessionsRail/__tests__/ChatSessionsRail.test.tsx`.
- Detail: `harness/knowledge/chat-sessions.md`.
- - Chat-target registration is synchronously observed at the app root: props feeding a mounted `ChatPanel` target or its handlers must use stable empty-collection fallbacks, never an inline `[]`, or the target unregisters/re-registers every root render and hits React's max update depth.
- Guard: `Workbench/__tests__/ChatDockLifecycle.test.tsx` (knowledge chat follows the same rule but isn't covered by this guard).
- Detail: `harness/knowledge/chat-sessions.md`.
- - The app owns v2 canvas storage migration, PTY sessions, runtime-control
- endpoints, plugin activation, and UI-visible data recovery. The CLI adapts to
- those contracts but does not own them.
- - External canvas-store synchronization must treat edges as first-class state:
- watcher events carry edge ids, renderer reloads must accept edge-only events,
- and stale saves merge edges by `updatedAt` without dropping unsaved local
- edges. Guards: `src/main/canvas/__tests__/store-merge.test.ts` and
- `src/renderer/src/modules/canvas/document/__tests__/externalMerge.test.ts`.
- - Live-app capabilities live under `src/main/runtime/capabilities/` behind tiered access (`read`/`operate`/`unsafe`) and the runtime server's bearer-auth boundary; Pulse CLI gets `read`/`operate` by default, with `browser.page.eval` (`page_eval` tool) and `host.renderer.eval` (`host_renderer_eval` tool / `pulse-canvas runtime host-eval`) as the only `unsafe` exceptions, each independently flag-gated; external Canvas node updates stay limited to title/content.
- Guard: `src/main/runtime/capabilities/*.test.ts`, `src/main/runtime/__tests__/control-server.test.ts`.
- Detail: `harness/knowledge/security-posture.md` ("Runtime-control capability tiers and registry").
- - Canvas node and edge shapes are sourced from `src/shared/canvas.ts`, not the
- shorter README node table. Current host node types include `file`,
- `terminal`, `frame`, `group`, `agent`, `text`, `iframe`, `image`, `shape`,
- `mindmap`, `reference`, `dynamic-app`, and `plugin`.
- - Cross-mindmap topic transfers are canvas-level atomic transactions: rekey
- every moved topic subtree, update both maps in one history snapshot, and
- degrade bound edges in that same snapshot when a whole source map is removed.
- Topic components own only drag intent; `useCanvasDocument` owns mutation and undo.
- - Plugin nodes use stable host type `plugin` with plugin-owned
- `data.payload`. **New node capabilities default to plugin nodes** (decided
- 2026-07-08); extending the host `CanvasNode['type']` union is the
- exception, reserved for nodes needing main-process integration the plugin
- capability registry can't cover (a persistent session/IPC channel like
- PTY, or a dedicated storage-migration path) — see
- `harness/skills/add-canvas-node/SKILL.md`.
- - The channel plugin is inert unless the experimental flag and channel config
- are enabled; keep channel credentials in local settings/env, not source.
+ - Renderer privileged operations use the typed window.canvasWorkspace preload API; never import Electron, Node, main, or preload into renderer code. Move cross-process contracts toward src/shared; do not add preload-to-renderer imports beyond existing allowlisted debt.
+ - Keep main code in domain folders and preserve IPC names/API shape during refactors.
+ - New production TS/TSX files stay at or below 500 lines; existing over-limit baseline files must not grow.
+ - Tool names/schemas/descriptions ship in the main bundle. Keep descriptions concise and run the bundle gate for tool-surface growth; repeated usage prose belongs in the system prompt.
+ - Runtime data stays under user runtime/settings locations, not the source tree. Packaged CLI/skills install and repair must use AgentToolingManager and agent-tooling-queue, never a source checkout, pnpm, or global link.
+ - Driver profiles default to temp/demo/clone. Use real --allow-real-writes only with explicit user intent. Reopening demo without --reset preserves fixtures; reseeding is a reset.
+ - Read chat-sessions.md before any run/session change: preserve single-flight activation, conversation-owned runtimes and queues, serialized visible approvals, fail-closed pointer changes, attachment/failure persistence, stable rail projections, and stable mounted-target props. Guard details and test references belong there.
+ - External-role driver rejection after its abort signal is a stopped turn, not a failed turn; follow agent-roles.md.
+ - Preserve edges as first-class synchronized state and make cross-mindmap transfers atomic with history/edge updates. Full rules and guards: main-domain-modules.md.
+ - src/shared/canvas.ts owns node/edge shapes. Node capabilities default to plugin nodes with plugin-owned data.payload. Host-union additions are exceptions for main-process facilities the plugin capability registry cannot provide, such as persistent PTY/IPC or dedicated migration paths.
+ - Runtime capabilities retain read/operate/unsafe tiers and bearer authentication. Only the independently flag-gated page/host eval exceptions expose unsafe operations to external CLI callers; node updates stay limited to title/content. Follow security-posture.md.
+ - Channel integration stays inert until both its experimental flag and configuration are enabled; keep credentials in local settings/env, never source.
- ## Common Commands
+ ## Tools and acceptance
+ Before changing agent-tool, IPC, node-type/factory, or app/CLI storage registries, run `node apps/canvas-workspace/harness/tools/describe-canvas.mjs` from the repository root. It checks registry parity; passing it is not a substitute for runtime behavior tests.
+
+ Use local validation YAML through the root runner. Iteration uses quick, completed work standard, and relevant performance/release work release. Canvas is not included in root core-only commands.
+
```bash
pnpm --filter canvas-workspace typecheck
pnpm --filter canvas-workspace test
pnpm --filter canvas-workspace build
- pnpm --filter canvas-workspace dev
- pnpm --filter canvas-workspace dev:temp-home
```
- Harness commands for interaction-heavy or visual changes:
-
- ```bash
- pnpm --filter canvas-workspace harness start --profile demo --build
- pnpm --filter canvas-workspace harness status
- pnpm --filter canvas-workspace harness snapshot-ui
- pnpm --filter canvas-workspace harness screenshot
- pnpm --filter canvas-workspace harness logs --lines 120
- pnpm --filter canvas-workspace harness close --cleanup
- ```
-
- Packaging commands exist, but are slower and platform-dependent:
+ For interaction or visual changes, use the real-app driver protocol and report the observed scenario. UI showcase baselines are Linux-rendered; follow its README for visual/visual:update rather than comparing fonts across OSes. Dev, isolated-home launch, packaging, and detailed driver command sequences are in package.json and harness/tools/driver/README.md.
- ```bash
- pnpm --filter canvas-workspace package
- pnpm --filter canvas-workspace package:mac
- pnpm --filter canvas-workspace package:mac:arm64
- pnpm --filter canvas-workspace package:win
- pnpm --filter canvas-workspace package:linux
- ```
+ Boundary/UI/file-size guards live under src/main/__tests__; storage/plugin/runtime suites and focused domain tests are selected by validation.yaml. Report executed evidence and remaining manual risk.
- ## Key Files
+ ## Knowledge maintenance
- - `src/main/index.ts`: thin Electron main entrypoint.
- - `src/main/app/bootstrap.ts`: startup wiring for IPC, canvas storage, agent,
- teams, plugins, runtime-control, window creation, and teardown.
- - `src/preload/index.ts`: exposes `window.canvasWorkspace` and assembles bridge
- APIs.
- - `src/renderer/src/app/App/index.tsx`: top-level renderer routes, shell, settings, and
- plugin route/nav integration.
- - `src/renderer/src/modules/canvas/components/canvas/Canvas/`: canvas surface and interaction wiring.
- - `src/renderer/src/app/shell/Workbench/`: mounted workspace state and chat
- portal ownership.
- - `src/renderer/src/modules/dock/internal/RightDock/`: tabbed right dock for chat and
- previews (link, artifact, node-detail, canvas-preview, terminal tabs).
- Behavior: `harness/knowledge/dock-browser.md`.
- - `src/shared/canvas.ts`: canonical canvas node, edge, reference, and workspace
- node contracts.
- - `src/main/dock/`: right-dock tab support in main — `tab-store.ts` (tab
- mirror), `tab-actions.ts` (tab-activation pushes), `history-store.ts`
- (browsing history). Detail: `harness/knowledge/dock-browser.md`.
- - `src/main/webview/lifecycle.ts`: shared Canvas-node and right-dock webview
- lifecycle policy. Real-time Feishu/Lark hosts remain eligible for the 1fps
- paint throttle but are exempt from L2 freeze and therefore L3 discard; match
- the guest's current URL, not the node/tab's originally saved URL.
- - `src/main/canvas/store.ts`: workspace manifest/store IPC, watchers, export,
- import, and migration hooks.
- - `src/main/canvas/storage.ts`: atomic JSON I/O, v2 split storage, migration,
- recovery, and pollution detection.
- - `src/main/agent/`: Canvas Agent service, session store, prompt config, tools,
- and chat IPC; shared provider/model config lives in `src/main/models/`.
- - `src/main/agent-teams/`: agent-team service, store, IPC, PTY bridge, and
- canvas node integration.
- - `src/main/runtime/control-server.ts`: loopback runtime server used by live
- `pulse-canvas` commands.
- - `src/plugins/main/`, `src/plugins/renderer/`, `src/plugins/types.ts`: Canvas
- plugin registries and shared plugin contracts.
- - `harness/`: workspace harness container — `knowledge/` (conventions + maps),
- `tools/driver/` (Electron launch, CDP, screenshot, input, logs, cleanup),
- `skills/` (agent procedures), `validate/` (check bindings).
+ Reuse existing topic owners, preserve facts when extracting prose, and leave trigger-based routes here. Spec records unresolved intended behavior; resolve it into tests/skills/Knowledge and remove obsolete intent. Its surface definition is shared with the engine's harness/spec/README.md. Do not grow this entry into another source map or test inventory.