git:20261014.31a93fe to git:20261019.0a763e4

2 added, 2 removed. Audit A to A.

# DOX — packages/chat-gateway/src/client
Files in this directory. One row per source file. See change: add-chat-gateway.
| File | Purpose |
|------|---------|
- | `ChatGatewaySettings.tsx` | `ChatGatewaySettings` settings-section (lazy via manifest `module`; moved here from `index.tsx`). Claimed by `pi-dashboard-plugin.claims[]` as `ChatGatewaySettings`. Edits config (enabled, token, `allowedRoots`, `defaultCwd`, `fixedMap`, `allowlist`, `admins`, `groupChannels`, `steerPrefix`) and persists via `usePluginSend()`+`plugin_config_write`; a BLANK token is omitted so a save cannot erase the stored secret. Reads the read-only bindings/pairing/status view from `GET /api/chat-gateway/bindings`. The bot token NEVER arrives here: it is `writeOnly` in `configSchema.json` and stripped host-side by `redactWriteOnly`. Renders `<TeamControlsPanel />` below the gateway fields (`teamControls` is edited there, not here, because only that lane can await the platform). Tested in `__tests__/ChatGatewaySettings.test.tsx` (jsdom) — note its `configWrites()` helper: the nested panel sends its own surface request on mount, so a raw `sendMock` call count is no longer 1:1 with a config write. See change: align-ui-with-theme-tokens: settings copy "new session's cwd" (no spawn wording). Settings select `chat-gateway-session-visibility` (hidden|shown) saved as `sessionVisibility`. See change: hide-chat-gateway-sessions. Checkbox `chat-gateway-thread-per-conversation`. See change: chat-gateway-thread-per-conversation. `mirrorDashboardSessions` checkbox (`data-testid=chat-gateway-mirror-dashboard-sessions`), dirty-tracked. See change: chat-gateway-attach-dashboard-sessions. |
+ | `ChatGatewaySettings.tsx` | `ChatGatewaySettings` settings-section (lazy via manifest `module`; claimed as `ChatGatewaySettings`). Kit fields in 4 `SettingsGroup`s (Discord connection · Where sessions run · Who can use it · Behaviour) + pairing `SettingsNotice`, bound-channels `SettingsTable`, `<TeamControlsPanel />`. Header `GatewayStatus` pill (`chat-gateway-status`: Not set up / Paused / Running / Not running). Master switch labelled "Answer Discord messages" (testid `chat-gateway-enabled` kept). Save model: NO in-body Save; `useSettingsDraftSource({ id: "plugin:chat-gateway" })`; `draft` holds only EDITED keys (clean page adopts `plugin_config_update`, dirty keeps edited keys); `commit` sends ONLY edited keys via `plugin_config_write`, THROWS without writing when a channel-folders line is not `channelKey=folder` (field error names the line, `badMapLines`). Empty allowed folders → non-blocking warning `chat-gateway-allowed-roots-warning`. Bot token is write-only: draft holds only the typed replacement, never sent blank, cleared after commit/reset, never seeded from server; commit failure → `chat-gateway-notice` error `SettingsNotice` with generic sentence, raw server text only in "Copy details", run through `scrubSecret` (raw/trimmed/JSON/URL spellings of the token); host gets a generic `Error`. Bindings read from `GET /api/chat-gateway/bindings` (refetched after commit). Tested in `__tests__/ChatGatewaySettings.test.tsx` (jsdom; mock keeps real `useRuntimeT`; `hostSave()` drives the registered source; `configWrites()` filters the panel's own surface request). See change: align-ui-with-theme-tokens, hide-chat-gateway-sessions, chat-gateway-thread-per-conversation, chat-gateway-attach-dashboard-sessions, retro-ux-plugin-settings-wave-2. |
| `index.tsx` | Intentionally empty client entry (`export {}`); do not re-export lazy components. See change: lazy-plugin-claim-modules. |
- | `team-controls-panel.tsx` | `TeamControlsPanel` — the team-controls configuration surface (tasks 8.1-8.4), a PURE RENDERER of `TeamSurfaceView` that decides nothing. Renders per binding: principals, role map with the delegation disclosure, ceiling, mirror level, provisioned channel, provisioning problem, and folders with the inert ones marked. Shows the live disarm state plus a Re-arm control when disarmed. Reads/writes ride the plugin's browser-handler lane (`TEAM_SURFACE_MESSAGE` / `TEAM_CONFIG_MESSAGE`, no new HTTP route); re-arm WRITES config rather than issuing a chat verb, which is exactly the dashboard-only asymmetry the spec requires. The delegation disclosure renders `unavailable` as "missing permission: X" and an empty roster as "Nobody can assign this role" — the two states must never collapse, or a role looks safe precisely when the platform declined to say who holds it. Tested in `__tests__/team-controls-panel.test.tsx` (jsdom). |
+ | `team-controls-panel.tsx` | `TeamControlsPanel` — team-controls configuration surface (tasks 8.1-8.4), a PURE RENDERER of `TeamSurfaceView` that decides nothing. Renders in a `SettingsGroup`: Armed / Disarmed `SettingsStatusPill` (`team-disarm-state`, `data-status`) with Re-arm (`team-rearm`) beside it, `SettingsFacts` (highest level, allowed folders), one `SettingsItem` per binding (level + mirror pills, principals, roles with delegation disclosure, folders with "Not used" pill for inert), command-log `SettingsTable` with Done/Refused pills. Raw `problem` / `configError` / refusal `reason` never render: a sentence shows, raw text only via `SettingsNotice details` (Copy details). Reads/writes ride the plugin browser-handler lane (`TEAM_SURFACE_MESSAGE` / `TEAM_CONFIG_MESSAGE`, no HTTP route); re-arm WRITES config (dashboard-only asymmetry). Delegation `unavailable` = "missing permission: X", empty roster = "Nobody can assign this role" — must never collapse. Tested in `__tests__/team-controls-panel.test.tsx` (jsdom). See change: add-chat-gateway-team-controls, retro-ux-plugin-settings-wave-2. |