git:20260824.680846f to git:20260829.b3c4b6f

2 added, 2 removed. Audit A to A.

# DOX — packages/kb-plugin/src/client
Files in this directory. One row per source file. See change: add-kb-folder-slot.
| File | Purpose |
|------|---------|
| `FolderKbSection.tsx` | `sidebar-folder-section` claim. `deriveKbRowState` ordered five-state: error→indexing→not-indexed→stale→populated (error wins over chunks:0). `clientError = reindexError ?? error` forces `error` state (trigger reject OR poll outage) over the derived state; `indexing` spinner survives transient poll blips. Count tooltip `F files · N chunks`. `→` opens kb settings. Pill is STATE-ONLY: `folder-kb-reindex` / `-index-now` / `-retry` and the indexing spinner GONE. ONE `useFolderMenuItem` (`id:"kb-reindex"`, `group:"maintenance"`, `mdiDatabaseRefreshOutline` so it never collides with the menu's plain `mdiRefresh`) varies by state: Retry (error), indexing… (indexing), Index now (not-indexed), Reindex now (stale, populated or loading); `badge` = `N stale` when stale; `disabled = busy`. Scope is `null` when `placement === "card"` (worktree card has no folder menu) — registers nothing. Optimistic `pending` from `useKbStats` renders the `indexing` branch on activation (error still outranks it); `busy` (pending OR polled indexing) covers the whole optimistic window (no double-submit). Inline `folder-kb-stale` marker STAYS in the pill (state, not a control) — badge on the menu item is the action's context, not duplication. See change: add-kb-folder-slot; fix-kb-index-feedback; add-kb-index-optimistic-pending; move-slot-actions-to-menu. |
| `index.tsx` | Client barrel. Exports `FolderKbSection`, `KbSettingsClaim` for plugin-registry. See change: add-kb-folder-slot. |
| `kb-api.ts` | REST client. `fetchKbStats`/`reindexKb`/`fetchKbConfig`/`saveKbConfig`. base64url folder-path codec `encodeFolderPath`/`decodeFolderPath`, `kbSettingsUrl`. Content-type guard: non-JSON body → typed error not parse crash. See change: add-kb-folder-slot. |
| `KbSettingsClaim.tsx` | `shell-overlay-route` claim `/folder/:encodedCwd/kb`. Decodes cwd param, renders `KbSettingsPanel`. See change: add-kb-folder-slot. |
- | `KbSettingsPanel.tsx` | Per-folder KB path editor. Edits sources[] (add/remove/reorder priority)/include/exclude/dbPath only; other config round-tripped. Shows origin + count. `Save + Reindex`. Worktree bootstrap: `Create project config` + `Copy from parent repo` (`parentRepoOf` derives parent from `.worktrees/` path). See change: add-kb-folder-slot. |
+ | `KbSettingsPanel.tsx` | Per-folder KB path editor. Edits sources[] (add/remove/reorder priority)/include/exclude/dbPath only; other config round-tripped. Shows origin + count. `Save + Reindex`. Worktree bootstrap: `Create project config` + `Copy from parent repo` (`parentRepoOf` derives parent from `.worktrees/` path). Standalone `Reindex now` (`kb-reindex-now`, `mdiDatabaseRefreshOutline`) in BOTH footer branches: gated on `resolvedSources.length > 0` (the disk list `reindexAll` walks) + `saving/busy` — NOT on `dirty`; reason rendered as visible inline text when disabled for zero sources. Error region `kb-settings-error` precedence `bootstrapErr ?? reindexError ?? error(useKbConfig) ?? statsError` (user-initiated before ambient). Bootstrap banner gated on `!isProject && !canIndex`; sources notice two variants keyed on `canIndex` ("(no sources defined)" vs "(no sources — nothing will be indexed)"). Second consumer of `useKbStats` `reindex`/`pending`/`reindexError`. See change: add-kb-folder-slot; fix-kb-settings-reindex-gate. |
| `useKbConfig.ts` | `useKbConfig(cwd)`. GET config, `save(patch)` PUT. Round-trips full config. See change: add-kb-folder-slot. |
- | `useKbStats.ts` | `useKbStats(cwd)`. Fetch `/api/kb/stats`, `reindex()` POST (202→`refetch()` engages poll). Polls every 1000ms while `indexing`, stops on settle. Two error channels: `reindexError` (trigger POST rejected, no job) vs `error` (poll outage, surfaced only after `MAX_POLL_MISSES`=3 consecutive misses — a lone blip keeps the spinner, never `clearPoll`s). Optimistic `pending` boolean: set `true` synchronously in `reindex()` (spinner on click), cleared ONLY on POST reject / a `/stats` poll seeing `indexing:true` / a `REINDEX_GUARD_MS` timeout guard (`refetch()`s) — never on the bare `202`, so no spinner flicker/wedge. See change: add-kb-folder-slot; fix-kb-index-feedback; add-kb-index-optimistic-pending. |
+ | `useKbStats.ts` | `useKbStats(cwd)`. Fetch `/api/kb/stats`, `reindex()` POST (202→`refetch()` engages poll). Polls every 1000ms while `indexing`, stops on settle. Two error channels: `reindexError` (trigger POST rejected, no job) vs `error` (poll outage, surfaced only after `MAX_POLL_MISSES`=3 consecutive misses — a lone blip keeps the spinner, never `clearPoll`s). Optimistic `pending` boolean: set `true` synchronously in `reindex()` (spinner on click), cleared ONLY on POST reject / a `/stats` poll seeing `indexing:true` / a `REINDEX_GUARD_MS` timeout guard (`refetch()`s) — never on the bare `202`, so no spinner flicker/wedge. TWO consumers of `reindex`/`pending`: the folder slot AND `KbSettingsPanel` — a refactor cannot assume the slot is the only caller; the double-submit `disabled` guard is CONSUMER-side (derived `busy`), not in the hook. See change: add-kb-folder-slot; fix-kb-index-feedback; add-kb-index-optimistic-pending; fix-kb-settings-reindex-gate. |