git:20261004.0ae4da8 to git:20261008.6f1cd01

1 added, 1 removed. Audit A to A.

# DOX — packages/client/src/components/openspec
Files in this directory. One row per source file. See change: fold-oversized-agents-directories.
| File | Purpose |
|------|---------|
| `ArchiveBrowserView.tsx` | Browser view for archived OpenSpec changes. Exports `ArchiveBrowserView`. → see `ArchiveBrowserView.tsx.AGENTS.md` |
| `AttachChangePicker.tsx` | Exports `AttachChangePicker({changes,groups?,assignments?,onSelect,onCancel})`. `GroupedAttachDialog` when groups exist, else flat `SearchableSelectDialog` (open first, complete last). Shared by session card + composer. See change: redesign-composer-session-strip. |
| `AttachmentTrace.tsx` | Read-only attachment trace: `Archived <date>` + archive letters, `In main checkout` + preview letters, muted `Not found`. Exports `AttachmentTrace`, `isLiveActive`. See change: resolve-archived-attached-proposal. |
| `ExploreDialog.tsx` | Modal dialog for OpenSpec Explore prompts. Textarea + `useImagePaste` for pasted images; `Cmd/Ctrl+Enter` sends `onSend(text, images?)`. Renders shared `ImagePreviewStrip`. Mounts `<ComposerPanelSlot draft={text} onApplyText={setText}/>` under the textarea (grammar plugin's `composer-panel` slot; no `sessionId` — inert until claimed). Exports `ExploreDialog`. See change: grammar-llm-only-with-explore. |
| `FolderOpenSpecSection.tsx` | Slim single-line navigation entry `OpenSpec (N) →` to board route; STATE-ONLY — Refresh/Archive/Specs are folder-actions-menu items contributed host-side by `SessionList`. → see `FolderOpenSpecSection.tsx.AGENTS.md` Readiness-driven pill variants: ABSENT (Initialize+dismiss), BROKEN·missing-changes-dir (Repair+confirm), BROKEN·cli-failed (inert, error text), STALE (Update); confirms name the directory; failures surface stderr via `onToast`. See change: add-openspec-init-affordances. |
| `lazy-openspec-dialogs.ts` | Code-splits click-only OpenSpec dialogs off the cold-landing entry chunk (`mdi-chunk-size` 900 KB gz cap). Exports `openspecDialogLoaders` + `LazyAttachChangePicker`, `LazyExploreDialog`, `LazyNewChangeDialog`, `LazyProposeDialog`, `LazyTasksPopover`. Callers render inside `<Suspense fallback={null}>`. See change: redesign-composer-session-strip. |
| `NewChangeDialog.tsx` | Dialog launching `/skill:openspec-new-change`. Exports `NewChangeDialog`, `formatNewChangePrompt(name,… → see `NewChangeDialog.tsx.AGENTS.md` |
| `openspec-actions.ts` | Pure `deriveOpenSpecActions(ctx) → {primary?, overflow, unattached}`. Exports `ActionSpec`, `OpenSpecActionKey`, `OpenSpecActionsCtx`, `OpenSpecActions`. No closures (key → handler map per surface). `blocked` + reason while working, except `detach`. `includeDetach` card true / composer false; `idPrefix` "" / "composer-". See change: redesign-composer-session-strip. |
| `openspec-helpers.tsx` | Shared OpenSpec UI helpers. Exports `LETTER_MAP`, `artifactLetter(id)`, `statusColor(status)`,… → see `openspec-helpers.tsx.AGENTS.md` |
| `OpenSpecActivityBadge.tsx` | Session-card sub-badge showing active OpenSpec phase. Exports `OpenSpecActivityBadge`. → see `OpenSpecActivityBadge.tsx.AGENTS.md` |
| `OpenSpecArtifactDialog.tsx` | Non-mobile artifact reader in a flush full `Dialog` (URL unchanged). Owns its dismissal: passes `onBack={onClose}` + `backLabel` on ALL THREE branches (loading/not-found/loaded) because a flush Dialog renders no ✕; `closeInset` and the `h-[85vh]` wrapper are GONE (the panel is itself a capped flex column). See change: fix-flush-dialog-scroll-and-close-collision. → see `OpenSpecArtifactDialog.tsx.AGENTS.md` |
| `OpenSpecBoardView.tsx` | Full-page OpenSpec kanban board. Route `/folder/:encodedCwd/openspec`. → see `OpenSpecBoardView.tsx.AGENTS.md` Prop `isGitRepo`+`gitWorktreeEnabled` replaced by one `worktreeAvailability` (`resolveWorktreeAvailability`); the per-card `New worktree` button always renders, `disabled`+`title` reason when unavailable. See change: fix-openspec-board-worktree-button-gating. Swaps session hide/unhide for `onArchiveSession` archive button. See change: archive-sessions-lazy-load. |
| `OpenSpecGroupManager.tsx` | CRUD manager: create/rename/recolor/reorder(dnd-kit)/delete groups. See change: add-openspec-change-grouping. |
| `OpenSpecGroupPicker.tsx` | Per-row chip+dropdown assigning change to group; inline create. See change: add-openspec-change-grouping. |
| `OpenSpecGroupPills.tsx` | Pill row filtering OpenSpec changes by group; single-select; "Manage groups…" link. See change: add-openspec-change-grouping. |
| `OpenSpecGroupSection.tsx` | Collapsible group section header with color swatch, name, count, body slot. See change: add-openspec-change-grouping. |
| `OpenSpecGroupsSettingsSection.tsx` | Settings section listing cwds with per-cwd group manager. See change: add-openspec-change-grouping. |
| `OpenSpecProfileSection.tsx` | Settings section. Sets global OpenSpec profile (core/expanded/custom) + workflow multiselect. → see `OpenSpecProfileSection.tsx.AGENTS.md` Surfaces `offerInitialization` toggle + opted-out directory list with per-row re-enable (PUT /api/config partial). See change: add-openspec-init-affordances. |
| `OpenSpecStepper.tsx` | 5-segment lifecycle bar (Proposal·Design·Specs·Tasks·Archive). Exports `OpenSpecStepper`, `deriveStepperState`, `SegmentId`, `SegmentState` (`done\|current\|todo\|skipped`). Tasks absorbs Apply: `current` in READY/IMPLEMENTING, fill = completed/total. Archive `current` iff COMPLETE. Segment = `<button>` only when its handler passed (`onReadArtifact`/`onOpenTasks`/`onArchive`), else inert `<div>`. `role="group"`; testids `stepper-segment-<id>` + `data-state`. Colors via `statusPresentation` tokenVar; `@container (width < 250px)` → letters. See change: redesign-session-card-and-composer, compact-openspec-lifecycle-bar. `testIdPrefix?` (default "") prefixes every id → `composer-openspec-stepper`, `composer-stepper-segment-*`, `composer-stepper-tasks-fill`. See change: redesign-composer-session-strip. |
| `ProposeDialog.tsx` | Name-only dialog launching `/skill:openspec-propose`. Exports `ProposeDialog`, `formatProposePrompt(name)`. → see `ProposeDialog.tsx.AGENTS.md` |
| `SessionOpenSpecActions.tsx` | OpenSpec action panel for a session (attach/detach, New/Propose/Explore, Continue/FF/Apply/Verify/Archive,… → see `SessionOpenSpecActions.tsx.AGENTS.md` |
- | `useOpenSpecRunConfigRow.tsx` | Shared "Runs with" run-config row + confirm-before-send gate for the three OpenSpec launch dialogs (Explore / Propose / New Change). Exports `useOpenSpecRunConfigRow` → `{ rowElement, submit, sending }` and `RUN_CONFIG_CONFIRM_TIMEOUT_MS` (8s). The gate emits `set_model`/`set_thinking_level` then waits until the session REPORTS the new `(model, thinkingLevel)` pair before sending, because the bridge pump dispatches handlers concurrently and an unguarded prompt runs on the OLD model; times out with a notify-and-send fallback. Sticky: changes apply to the session, disclosed in-row. `fieldset[disabled]` gates both selectors for pointer AND keyboard while pending. **9th popover-consumer surface (fix-popover-pane-bounded-height):** it re-mounts `ModelSelector` + `ThinkingLevelSelector` — NOT a new `usePopoverFlip` call site (still 8) — and mounts `PopoverBoundaryProvider` with `closest('[role="dialog"]')`, resolving to the shared `Dialog` panel (`packages/client-utils/src/Dialog.tsx`, `max-h-[80vh] overflow-y-auto`). Both height bounds are INHERITED because each consumer sets its own floor internally, so a new host cannot forget it; the dialog is the shortest host in the app, making it where the 260px list floor most often exceeds available space (capped by `minHeight = min(floor, maxHeight)`). Pinned by `components/__tests__/OpenSpecRunConfig.test.tsx` incl. a contrast case (no dialog ancestor → viewport `440px` vs pane `192px`). See change: openspec-dialog-model-effort-selector. See change: fix-popover-pane-bounded-height. |
+ | `useOpenSpecRunConfigRow.tsx` | Shared "Runs with" run-config row + confirm-before-send gate for the three OpenSpec launch dialogs (Explore / Propose / New Change). Exports `useOpenSpecRunConfigRow` → `{ rowElement, submit, sending }` and `RUN_CONFIG_CONFIRM_TIMEOUT_MS` (8s). The gate emits `set_model`/`set_thinking_level` then waits until the session REPORTS the new `(model, thinkingLevel)` pair before sending, because the bridge pump dispatches handlers concurrently and an unguarded prompt runs on the OLD model; times out with a notify-and-send fallback. Sticky: changes apply to the session, disclosed in-row. `fieldset[disabled]` gates both selectors for pointer AND keyboard while pending. **9th popover-consumer surface (fix-popover-pane-bounded-height):** it re-mounts `ModelSelector` + `ThinkingLevelSelector` — NOT a new `usePopoverFlip` call site (still 8) — and mounts `PopoverBoundaryProvider` with `closest('[role="dialog"]')`, resolving to the shared `Dialog` panel (`packages/client-utils/src/Dialog.tsx`, `max-h-[80vh] overflow-y-auto`). Both height bounds are INHERITED because each consumer sets its own floor internally, so a new host cannot forget it; the dialog is the shortest host in the app, making it where the 260px list floor most often exceeds available space (capped by `minHeight = min(floor, maxHeight)`). Pinned by `components/__tests__/OpenSpecRunConfig.test.tsx` incl. a contrast case (no dialog ancestor → viewport `440px` vs pane `192px`). See change: openspec-dialog-model-effort-selector. See change: fix-popover-pane-bounded-height. Change: Run-config model picker uses `useRolePick`; role pick resolves into the draft, existing confirm-before-send gate applies it. See change: add-role-aware-model-refs. |
| `SpecsBrowserView.tsx` | Full-page main specs reader for a cwd. Combobox jump-to-spec + `MarkdownPreviewView` (searchable). Backed by `useMainSpecsReader`. Props: `cwd`, `onBack`. Exports `SpecsBrowserView`. |