diff --git a/Design.md b/Design.md
index 6aa8681a..9c038b8b 100644
--- a/Design.md
+++ b/Design.md
@@ -76,7 +76,7 @@ over the workspace below the window strip; dock width calculations reserve the i
Workspace pane headers share a 40px height. Right-panel tabs and the close control live in the global 32px window strip, aligned to the panel width, leaving no inner dock header. Both use a 28px control height with 2px vertical clearance. Toolbar labels collapse to named icons below 48rem so task titles remain readable; the selected tool tab retains its text at narrow widths. Native window controls retain their existing placement. Settings and Scene Studio retain their existing dedicated page layouts.
New installations start in compact mode; saved document-mode preferences remain unchanged. The
-checkout context sits above the compact input. An empty compact workspace centers its greeting in the space above the bottom composer. The same
+checkout context sits below the compact input as an inset footer. Model, reasoning, and permission choices remain directly visible in the input toolbar; attachment and send actions align to the right. An empty compact workspace centers its greeting in the space above the bottom composer. The same
composer/editor tree serves compact and document modes so layout changes preserve the draft. Short
windows scroll without clipping the input; conversations retain their transcript scroll behavior.
@@ -86,7 +86,7 @@ list with descriptions available on hover or focus. Keep normal text contrast, p
states and visible keyboard focus; muted styling does not mean dimming usable controls. Preserve
the established shell regions and reserve decorative color for meaningful state. New-task panes omit a duplicate local title. Plugin actions above the composer or transcript are flat named Ghost controls with descriptions on hover or keyboard focus, rather than nested cards.
-The compact composer and document-mode control bar use a constant raised surface. The right tool panel floats within an 8px gutter using the shared raised Card, retaining resize and collapse behavior. Gutters are included in its reserved width and disappear when closed. The main workspace uses the same 16px rounded, clipped boundary and 8px outer inset. Adjacent workspace and dock surfaces share one 8px gap instead of summing their margins. Checkout context has an 8px inset within the input width; related plugin/context rows use 8px vertical spacing. The main workspace, right tool panel and composer share the same constant raised shadow token: 0 2px 8px, 4% opacity in light mode and 12% in dark mode. Sidebars retain flat elevation.
+The compact composer and document-mode control bar keep a white canvas surface in the default light theme and the raised surface in dark mode. A constant 0.5px neutral edge and a soft 8px/24px shadow distinguish the input from the page. This composer-specific edge is an explicit exception to the borderless surface rule, requested on 2026-09-28; it is painted without changing the editor or control geometry. Neutral model, reasoning, permission, composer action and checkout controls use the same muted foreground as the workspace toolbar; permission warnings, voice recording and filled submit controls retain their semantic colors. Composer chevrons do not add opacity, and prompt placeholders use upright text. The right tool panel floats within an 8px gutter using the shared raised Card, retaining resize and collapse behavior. Gutters are included in its reserved width and disappear when closed. The main workspace uses the same 16px rounded, clipped boundary and 8px outer inset. Adjacent workspace and dock surfaces share one 8px gap instead of summing their margins. Checkout context has a 16px inset within the input width; related plugin/context rows use 8px vertical spacing. The main workspace and right tool panel share the same constant raised shadow token: 0 2px 8px, 4% opacity in light mode and 12% in dark mode. Sidebars retain flat elevation.
## Application rules
diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx
index c0ccd0c5..4ca886e2 100644
--- a/apps/desktop/src/App.tsx
+++ b/apps/desktop/src/App.tsx
@@ -8354,7 +8354,7 @@ export default function App() {
-
+
{/* Full-page mode hides the transcript, so the header carries the only sign that a turn
is in flight — and the way back to the answer without leaving the mode for good. */}
{docMode && hasConversationContent && (
diff --git a/apps/desktop/src/components/ui/icons.tsx b/apps/desktop/src/components/ui/icons.tsx
index 6804099e..f76feac0 100644
--- a/apps/desktop/src/components/ui/icons.tsx
+++ b/apps/desktop/src/components/ui/icons.tsx
@@ -97,6 +97,7 @@ import { MoonIcon as PhosphorMoonIcon } from "@phosphor-icons/react/dist/csr/Moo
import { NotePencilIcon } from "@phosphor-icons/react/dist/csr/NotePencil";
import { PackageIcon as PhosphorPackageIcon } from "@phosphor-icons/react/dist/csr/Package";
import { PaletteIcon as PhosphorPaletteIcon } from "@phosphor-icons/react/dist/csr/Palette";
+import { PaperclipIcon } from "@phosphor-icons/react/dist/csr/Paperclip";
import { PaperPlaneTiltIcon } from "@phosphor-icons/react/dist/csr/PaperPlaneTilt";
import { PathIcon } from "@phosphor-icons/react/dist/csr/Path";
import { PauseIcon as PhosphorPauseIcon } from "@phosphor-icons/react/dist/csr/Pause";
@@ -319,6 +320,7 @@ export const Pencil = createIcon(PencilSimpleIcon, "Pencil");
export const Pin = createIcon(PushPinIcon, "Pin");
export const PinOff = createIcon(PushPinSlashIcon, "PinOff");
export const Play = createIcon(PhosphorPlayIcon, "Play");
+export const Paperclip = createIcon(PaperclipIcon, "Paperclip");
export const Plus = createIcon(PhosphorPlusIcon, "Plus");
export const Power = createIcon(PhosphorPowerIcon, "Power");
export const Puzzle = createIcon(PuzzlePieceIcon, "Puzzle");
diff --git a/apps/desktop/src/design/tokens.css b/apps/desktop/src/design/tokens.css
index 1d652da4..c9ee3088 100644
--- a/apps/desktop/src/design/tokens.css
+++ b/apps/desktop/src/design/tokens.css
@@ -305,6 +305,10 @@
--ds-focus-offset: var(--ds-foundation-space-2);
--ds-elevation-surface: none;
--ds-elevation-raised: 0 2px 8px rgb(16 24 40 / 4%);
+ --ds-composer-elevation:
+ 0 0 0 0.5px
+ color-mix(in oklch, var(--ds-color-text) 12%, var(--ds-color-canvas)),
+ 0 8px 24px rgb(16 24 40 / 10%), 0 2px 6px rgb(16 24 40 / 4%);
--ds-elevation-modal: 0 12px 36px rgb(16 24 40 / 12%);
/* Shared material for menu-like transient layers. It remains mostly opaque so text contrast is
@@ -399,6 +403,10 @@
--ds-color-chart-6: var(--ds-foundation-light-chart-6);
--ds-elevation-surface: none;
--ds-elevation-raised: 0 2px 8px rgb(16 24 40 / 4%);
+ --ds-composer-elevation:
+ 0 0 0 0.5px
+ color-mix(in oklch, var(--ds-color-text) 12%, var(--ds-color-canvas)),
+ 0 8px 24px rgb(16 24 40 / 10%), 0 2px 6px rgb(16 24 40 / 4%);
--ds-elevation-modal: 0 12px 36px rgb(16 24 40 / 12%);
--ds-material-raised-background: color-mix(
in oklch,
@@ -468,6 +476,10 @@
--ds-color-chart-6: var(--ds-foundation-dark-chart-6);
--ds-elevation-surface: none;
--ds-elevation-raised: 0 2px 8px rgb(0 0 0 / 12%);
+ --ds-composer-elevation:
+ 0 0 0 0.5px
+ color-mix(in oklch, var(--ds-color-text) 16%, var(--ds-color-canvas)),
+ 0 8px 24px rgb(0 0 0 / 28%), 0 2px 6px rgb(0 0 0 / 12%);
--ds-elevation-modal: 0 16px 40px rgb(0 0 0 / 28%);
--ds-material-raised-background: color-mix(
in oklch,
diff --git a/apps/desktop/src/session/Composer.tsx b/apps/desktop/src/session/Composer.tsx
index 7872122f..cfb85f8c 100644
--- a/apps/desktop/src/session/Composer.tsx
+++ b/apps/desktop/src/session/Composer.tsx
@@ -28,7 +28,7 @@ import {
LockOpen,
Maximize2,
Minimize2,
- Plus,
+ Paperclip,
PenLine,
Sparkles,
SlidersHorizontal,
@@ -44,7 +44,6 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
-import { Separator } from "@/components/ui/separator";
import {
Tooltip,
TooltipContent,
@@ -219,7 +218,7 @@ export function CheckoutBar({
return (
@@ -1425,90 +1420,6 @@ export function Composer({
if (files.length > 0) void onAttachImages(files);
}}
/>
-
-
-
-
- }
- />
-
-
-
-
- {t("composer.mentionFile")}
- {filesHint && (
- {filesHint}
- )}
-
- imageInputRef.current?.click()}>
-
- {t("composer.attachImage")}
-
-
-
- {t("composer.insertSkill")}
- {skillHint && (
- {skillHint}
- )}
-
- {canvasEnabled && (
-
-
- {t("composer.insertCanvas")}
-
- )}
-
-
- {t("composer.pullIssue")}
-
-
-
- {t("composer.market")}
-
-
-
- {t("composer.newSkill")}
-
- {/* With content already in the document the floating offer stays away; the brief is
- still one menu entry away while a scene with one is active. */}
- {activeBrief && !composerEmpty && (
-
-
- {t("brief.menuInsert")}
-
- )}
-
-
- {t("composer.addHint")}
-
-
-
-
-
-
- {pluginActions}
-
- {/* Session configuration shares the control row with attach, voice and run: one strip, one
- baseline. The chips row wraps inside its own measure when the composer is narrow. */}
-
-
- {/* Document mode is the app's own feature — it deserves a control you can see, not just a
- chord and a grip gesture. */}
-
- onDocMode(!docMode)}
- >
- {docMode ? (
-
- ) : (
-
+
+
+
+
+
+ }
+ />
+
+
+
+
+ {t("composer.mentionFile")}
+ {filesHint && (
+ {filesHint}
+ )}
+
+ imageInputRef.current?.click()}>
+
+ {t("composer.attachImage")}
+
+
+
+ {t("composer.insertSkill")}
+ {skillHint && (
+ {skillHint}
+ )}
+
+ {canvasEnabled && (
+
+
+ {t("composer.insertCanvas")}
+
)}
-
+
+
+ {t("composer.pullIssue")}
+
+
+
+ {t("composer.market")}
+
+
+
+ {t("composer.newSkill")}
+
+ {/* With content already in the document the floating offer stays away; the brief is
+ still one menu entry away while a scene with one is active. */}
+ {activeBrief && !composerEmpty && (
+
+
+ {t("brief.menuInsert")}
+
+ )}
+
+
+ {t("composer.addHint")}
+
+
+
+
+
-
- {docMode ? t("composer.collapse") : t("composer.expand")}
-
-
- {voiceEnabled ? (
-
- ) : null}
+ {pluginActions}
- {/* Required slot fields still empty — a hint beside Run, never a gate on it. */}
- {unfilledRequired.length > 0 && (
+ {/* Document mode is the app's own feature — it deserves a control you can see, not just a
+ chord and a grip gesture. */}
onDocMode(!docMode)}
>
-
-
- {t("slotCard.requiredShort", {
- count: unfilledRequired.length,
- })}
-
-
+ {docMode ? (
+
+ ) : (
+
+ )}
+
}
/>
- {t("slotCard.requiredWarning", {
- slots: unfilledRequired.join(", "),
- })}
+ {docMode ? t("composer.collapse") : t("composer.expand")}
- )}
- {/* Enter makes a paragraph in a document, so the send chord has to be taught rather than
- assumed. It shows only while the document is empty, and so retires itself. */}
- {composerEmpty && !running && !loading && runHint && (
-
- {t("composer.toSend", { key: runHint })}
-
- )}
+ {voiceEnabled ? (
+
+ ) : null}
- {running ? (
- <>
-
+ {/* Required slot fields still empty — a hint beside Run, never a gate on it. */}
+ {unfilledRequired.length > 0 && (
+
+
+
+
+ {t("slotCard.requiredShort", {
+ count: unfilledRequired.length,
+ })}
+
+
+ }
+ />
+
+ {t("slotCard.requiredWarning", {
+ slots: unfilledRequired.join(", "),
+ })}
+
+
+ )}
+
+ {running ? (
+ <>
+
>
);
@@ -1718,11 +1706,6 @@ export function Composer({
docMode ? "min-h-0 flex-1" : "mx-auto w-full max-w-3xl"
)}
>
- {/* Execution location and source control are adjacent but distinct: changing where a fresh
- session runs must never be confused with inspecting the current branch. */}
- {!docMode && checkout && (
-
- )}
{/* No `overflow-hidden`: BlockNote's drag/insert handles render just outside the text
column, and clipping them takes the block gutter away. The compact card paints its own
surface so its background can never drift away from the editor in WebKit. */}
@@ -1733,13 +1716,13 @@ export function Composer({
? // Expanded, the composer *is* the page: no card, no border, the app's own surface.
// `relative` anchors the floating control bar below.
"min-h-0 flex-1"
- : "rounded-composer bg-raised shadow-raised"
+ : "rounded-composer bg-canvas shadow-composer dark:bg-raised"
)}
>
, { running: true, onStop: () => stopped++ })
+ );
+ const stop = actions?.querySelector('button[aria-label="Stop this turn"]');
+ expect(stop).toBeTruthy();
+ click(stop);
+ expect(stopped).toBe(1);
+ } finally {
+ rendered.unmount();
+ }
+});
diff --git a/apps/desktop/tests/sceneChip.test.tsx b/apps/desktop/tests/sceneChip.test.tsx
index 8396c44d..6147bbf6 100644
--- a/apps/desktop/tests/sceneChip.test.tsx
+++ b/apps/desktop/tests/sceneChip.test.tsx
@@ -281,7 +281,7 @@ describe("SceneChip", () => {
row?.querySelector('button[title="Reasoning"]')?.textContent
).toContain("Extra High Effort");
expect(row?.querySelector('input[type="range"]')).toBeNull();
- expect(row?.textContent).not.toContain("Ask first");
+ expect(row?.textContent).toContain("Ask first");
expect(row?.textContent).not.toContain("Memory on");
expect(row?.textContent).not.toContain("No worktree");
@@ -374,9 +374,14 @@ describe("SceneChip", () => {
expect(picker?.querySelector("[data-provider-rail]")).toBeNull();
const providerSwitcher = picker?.querySelector("[data-provider-switcher]");
expect(providerSwitcher).toBeTruthy();
- expect(providerSwitcher?.textContent).toContain("Codex");
+ expect(
+ providerSwitcher?.querySelector('button[aria-label="Codex"]')
+ ).toBeTruthy();
+ expect(providerSwitcher?.getAttribute("aria-orientation")).toBe("vertical");
expect(providerSwitcher?.textContent).not.toContain("OpenAI Codex");
- expect(providerSwitcher?.textContent).toContain("Grok");
+ expect(
+ providerSwitcher?.querySelector('button[aria-label="Grok"]')
+ ).toBeTruthy();
expect(
picker?.querySelector('input[aria-label="Search models"]')
).toBeTruthy();
@@ -641,7 +646,7 @@ describe("SceneChip", () => {
);
expect(rendered.container.textContent).not.toContain("Develop");
- expect(rendered.container.textContent).not.toContain("Ask first");
+ expect(rendered.container.textContent).toContain("Ask first");
click(button(rendered.container, "Show session settings"));
await flush();
diff --git a/apps/desktop/tests/shellChromeContract.test.ts b/apps/desktop/tests/shellChromeContract.test.ts
index 20388b1e..ac15d534 100644
--- a/apps/desktop/tests/shellChromeContract.test.ts
+++ b/apps/desktop/tests/shellChromeContract.test.ts
@@ -99,12 +99,12 @@ describe("shell chrome contract", () => {
});
test("keeps one composer control row with the session chips in it", () => {
- const spacer = composer.indexOf('');
+ const spacer = composer.indexOf("data-composer-actions");
const chips = composer.indexOf(" {
'"session-header window-titlebar electrobun-webkit-app-region-drag flex min-w-0 shrink-0 items-center gap-2 pr-4"'
);
expect(appSource).toContain(
- 'className="session-header-toolbar [&_svg]:text-muted-foreground gap-inline flex min-w-0 shrink-0 items-center"'
+ 'className="session-header-toolbar gap-inline flex min-w-0 shrink-0 items-center"'
);
expect(sessionHeaderActionsSource).toContain(
'className="session-header-actions gap-inline flex shrink-0 items-center"'
diff --git a/docs/design/system.md b/docs/design/system.md
index bafd50a0..906d3930 100644
--- a/docs/design/system.md
+++ b/docs/design/system.md
@@ -319,6 +319,10 @@ size.
Workspace window chrome uses a 32px drag strip and scoped 40px workspace pane headers. Right-panel tabs and its close control share the window strip through the shell titlebar host, track the dock width, and leave no inner dock header. The focused task title reuses the existing title projection in the window strip; feature pages leave that title empty. Search and the sidebar toggle live in the window strip, aligned with the expanded sidebar and reserving at least 96px before controls for native window buttons. The task list has no duplicate header or product wordmark. Below 48rem, toolbar actions retain accessible names while dropping visible labels; the selected dock tab keeps its label even when inactive tabs become icons. Settings and Scene Studio retain their own header geometry.
+Left workspace controls, icon navigation and task sidebar share the session toolbar’s muted foreground for labels and icons. Hover and selected rows use neutral fills, selection markers and visible keyboard focus; status badges retain semantic colors.
+
+Neutral composer configuration, action and checkout controls use the same muted foreground, including hover/open states. Warning chips, voice recording and the filled submit action retain their semantic colors.
+
Session titlebar tools form one quiet monochrome toolbar. Icons and labels keep the muted foreground
across rest, hover, open, and pressed states; hover may add a neutral surface and keyboard focus
adds the standard ring. A real open or pressed state may use only a neutral fill. Titlebar actions
@@ -336,17 +340,19 @@ Elevation communicates a real layer and never changes on hover:
| role | use |
| --- | --- |
| `elevation-surface` | no shadow; compatibility role for cards, inputs, and persistent panels |
-| `elevation-raised` | 0 2px 8px, 4% light / 12% dark; one restrained short shadow for menu, popover, tooltip, floating main workspace, composer and right tool panel |
+| `elevation-raised` | 0 2px 8px, 4% light / 12% dark; one restrained short shadow for menu, popover, tooltip, floating main workspace and right tool panel |
+| `composer-elevation` | constant 0.5px neutral edge plus 0 8px 24px and 0 2px 6px shadows; compact composer and document toolbar only |
| `elevation-modal` | one restrained wider shadow for dialog, blocking overlay |
-Persistent titlebars, sidebars, cards, inputs, and panels are flat by default. The workspace composer (including its document-mode control bar) and right tool panel are explicit floating surfaces: use the existing raised background and elevation, constant across focus and hover. The right panel uses the shared raised Card with an 8px outer gutter included in its reserved width; closing it leaves no gutter. The main workspace shares its 16px rounded, clipped boundary and 8px inset, using the same raised shadow as the right panel and composer. Keep its reading background unchanged. Other surfaces use tonal planes, spacing,
+Persistent titlebars, sidebars, cards, inputs, and panels are flat by default. The workspace composer (including its document-mode control bar) and right tool panel are explicit floating surfaces. The composer uses the canvas surface in light mode (white in the default palette), the raised surface in dark mode, and its own composer elevation token for a 0.5px neutral edge plus fixed soft shadow. The right tool panel uses the existing raised background and elevation. Both remain constant across focus and hover. The right panel uses the shared raised Card with an 8px outer gutter included in its reserved width; closing it leaves no gutter. The main workspace shares its 16px rounded, clipped boundary and 8px inset, using the same raised shadow as the right panel. Keep its reading background unchanged. Other surfaces use tonal planes, spacing,
and contextual structural separators. Settings pages separate major groups with page-section spacing. Related rows share one tonal module
with internal hairlines; they do not become a stack of individually elevated cards. Choice tiles
such as appearance schemes and themes use tonal hover and a semantic selected ring without surface
elevation. Popovers and dialogs retain only the restrained elevation assigned to their real layer.
-Static borders and decorative rings are forbidden. The complete whitelist is:
+Static borders and decorative rings are forbidden outside the following whitelist:
+- the composer’s constant 0.5px neutral edge, explicitly requested with its white surface (the composer elevation token also owns its fixed soft shadow);
- neutral keyboard focus;
- error or warning status;
- table, diff, code, or document content structure;
diff --git a/docs/sdlc/changes/2026-09-28-composer-reference-layout/intent.md b/docs/sdlc/changes/2026-09-28-composer-reference-layout/intent.md
new file mode 100644
index 00000000..63165131
--- /dev/null
+++ b/docs/sdlc/changes/2026-09-28-composer-reference-layout/intent.md
@@ -0,0 +1,33 @@
+---
+id: 2026-09-28-composer-reference-layout
+schema: 5
+stage: intent
+status: accepted
+owner: Codex
+created: 2026-09-28
+source: user
+risk: medium
+approved_by: chenli
+approved_at: 2026-09-28
+approval_source: "User in this chat: 参考这个改进输入框, with seven visual references."
+---
+
+# Intent: Composer Reference Layout
+
+## Intent
+
+Improve the desktop prompt composer using the seven supplied reference images: generous writing space, direct model/reasoning/permission controls, right-aligned attachment and submit actions, and a quieter checkout footer. Preserve editor drafts, provider-owned choices, permissions and session/worktree lifecycle. Local implementation and checks are authorized. Do not invent server updates, remote hosts or service tiers from screenshot content. No release requested.
+
+Follow-up authorization: chenli reported “对比度不行” in the same chat on 2026-09-28. Correct surface separation and control legibility within the existing composer scope.
+
+Latest user direction (2026-09-28): “保持主输入框白色底,考虑从阴影、边框入手”. This supersedes the gray input fill and authorizes a composer-specific boundary/shadow design exception.
+
+Final edge refinement (2026-09-28): user requests “改成0.5”; reduce the composer edge from 1px to 0.5px, retaining its fill and shadows.
+
+Toolbar follow-up: user supplied a screenshot of the session header and explicitly chose “统一为柔和的中灰” on 2026-09-28. Apply the existing neutral toolbar design contract to text and icons, preserving disabled states.
+
+Sidebar follow-up: chenli requested “左侧也统一柔和的中灰” in this chat on 2026-09-28. Extend the same tone to left navigation text and icons, preserving meaningful state indicators.
+
+Composer-toolbar follow-up: user supplied the composer controls/checkout screenshot and requested “还有这些” on 2026-09-28. Extend the same soft medium gray to these labels and neutral action icons. This supersedes the earlier full-foreground control contrast choice.
+
+PR authorization: chenli requested “pr” in this chat on 2026-09-28, authorizing a review branch, commit, push and pull request for this completed scope. Merge and release remain unrequested.
diff --git a/docs/sdlc/changes/2026-09-28-composer-reference-layout/plan.md b/docs/sdlc/changes/2026-09-28-composer-reference-layout/plan.md
new file mode 100644
index 00000000..d068635c
--- /dev/null
+++ b/docs/sdlc/changes/2026-09-28-composer-reference-layout/plan.md
@@ -0,0 +1,30 @@
+---
+id: 2026-09-28-composer-reference-layout
+schema: 5
+stage: plan
+status: accepted
+owner: Codex
+created: 2026-09-28
+based_on: spec.md
+scope: apps/desktop/src/App.tsx, apps/desktop/tests/windowChromeContract.test.ts, Design.md, docs/design/system.md, apps/desktop/src/design/tokens.css, apps/desktop/src/styles.css, apps/desktop/tests/composerGeometryContract.test.ts, apps/desktop/tests/checkoutPickerRendered.test.tsx, apps/desktop/src/components/ui/icons.tsx, apps/desktop/src/session/Composer.tsx, apps/desktop/tests/sceneChip.test.tsx, apps/desktop/tests/shellChromeContract.test.ts, apps/desktop/tests/composerLayoutRendered.test.tsx, docs/sdlc/changes/2026-09-28-composer-reference-layout/
+---
+
+# Plan: Composer Reference Layout
+
+## Plan
+
+Codex owns layout and regression tests. Reuse menus and tokens without new dependencies or parallel state. Update the design contract for footer placement. Run affected rendered component, provider/model, permission, checkout and draft tests, desktop lint, TypeScript and renderer build, documentation and worktree SDLC gates. Inspect the actual app renderer in light/dark/narrow states and exercise draft retention and menus. Backend, packaging and release checks are outside this presentation-only scope.
+
+Temporary resources: task Vite renderer on 1438 and /private/tmp/codetwo-composer-reference/. Stop the server and remove scratch outputs at handoff; retain a small screenshot set for review, owned by Codex until next continuation. No Core launch or user data ownership. Retain frozen-lockfile desktop dependencies until worktree retirement.
+
+Rollback: revert scoped layout, tests and design wording; no persisted state migration.
+
+Contrast follow-up: user reported insufficient contrast in the rendered result. Reuse the existing muted/rest surface tokens, foreground text and opaque chevrons; preserve permission warning tones. Recheck actual light/dark rendering and measured text contrast, affected geometry tests, lint/types/build and repository gates.
+
+White-surface follow-up: user explicitly requests retaining the white main input and improving separation through shadow/border. Use one composer elevation token with a geometry-preserving 0.5px edge and fixed layered shadow; restore the canvas/raised background for light/dark. Scope the exception to Composer and its floating document toolbar. Check actual light/dark/narrow and focus rendering, affected tests, style lint and renderer build.
+
+Toolbar color: the existing parent colors icons only while text inherits foreground from Ghost buttons. Resolve the inconsistency at the toolbar CSS owner, including control states; remove the icons-only parent utility. This localized composition fix is sufficient, without changing global buttons or palette values. Check header behavior/contracts, style lint, renderer lint/types/build and actual light/dark rendered text/icon colors. Reuse unaffected composer evidence.
+
+Sidebar tone: the existing rail mixes inherited foreground, Ghost/selectable button foreground and muted utilities. Scope the shared muted color to existing left navigation roots and buttons, retaining status descendants and selection geometry. A CSS-only owner fix avoids replacing stateful rail modules. Verify actual light/dark rendering, selection/collapse, relevant rail tests and style lint; reuse unchanged renderer type/build evidence.
+
+Composer toolbar tone: use existing controls/actions/checkout boundaries and neutral button variants as the CSS owner. Remove the checkout label’s foreground override so it inherits this tone. Exclude warning chips and non-neutral recording/submit variants. Check real light/dark compact/document rendering, warning and send states, affected rendered tests, style lint and renderer build.
diff --git a/docs/sdlc/changes/2026-09-28-composer-reference-layout/spec.md b/docs/sdlc/changes/2026-09-28-composer-reference-layout/spec.md
new file mode 100644
index 00000000..c55b8454
--- /dev/null
+++ b/docs/sdlc/changes/2026-09-28-composer-reference-layout/spec.md
@@ -0,0 +1,29 @@
+---
+id: 2026-09-28-composer-reference-layout
+schema: 5
+stage: spec
+status: accepted
+owner: Codex
+created: 2026-09-28
+based_on: intent.md
+---
+
+# Spec: Composer Reference Layout
+
+## Design
+
+Reuse Composer, ModelPicker, SessionModePicker and CheckoutBar as the unique state owners. Recent shell cleanup put checkout above the card and permission inside secondary settings. This is a local presentation issue: changing composition costs less to implement, validate and maintain than replacing these stateful modules or rewriting the editor. No data migration or transaction changes.
+
+Keep the editor subtree mounted across compact/document mode. Move checkout after the card, using existing surface tokens and 24px radius. Keep permission directly visible; secondary settings contain memory and worktree where applicable. Use a named provider icon rail, preserving search, favorites and callbacks. Wrap controls at narrow widths.
+
+## Acceptance criteria
+
+- [x] AC-1: Rendered light, dark and narrow layouts have more writing room, direct model/reasoning/permission controls, right-aligned actions and an inset checkout footer. The default light input stays white; a fixed neutral 0.5px edge and soft shadow distinguish it from the reading canvas without changing geometry on focus/hover; neutral controls use theme-muted gray and chevrons do not attenuate opacity.
+- [x] AC-2: Provider/model search and switching, reasoning, permission disabling, checkout and secondary settings preserve existing behavior without duplicate permission controls.
+- [x] AC-3: Expand/collapse preserves the editor and draft; attachment, send/loading and running controls remain available. Applicable desktop and repository checks pass.
+
+- [x] AC-4: Session-header text and icons share the theme-muted gray in rest, hover and open states; disabled actions retain their reduced-opacity and inoperable state. Composer styling stays unchanged.
+
+- [x] AC-5: Left window controls, icon navigation and task sidebar use the same theme-muted text/icon tone as the session toolbar. Hover and selection remain identifiable through surfaces, markers and focus; status badges preserve semantic colors.
+
+- [x] AC-6: Composer configuration, neutral action icons and checkout labels share the toolbar medium gray in compact/document modes and interaction states. White fill and 0.5px edge remain; warnings, recording and filled submit states preserve their semantic colors.
diff --git a/docs/sdlc/changes/2026-09-28-composer-reference-layout/verification.md b/docs/sdlc/changes/2026-09-28-composer-reference-layout/verification.md
new file mode 100644
index 00000000..2ae7534d
--- /dev/null
+++ b/docs/sdlc/changes/2026-09-28-composer-reference-layout/verification.md
@@ -0,0 +1,105 @@
+---
+id: 2026-09-28-composer-reference-layout
+schema: 5
+stage: verification
+status: passed
+owner: Codex
+created: 2026-09-28
+based_on: plan.md
+revision: "ba9102a8 plus the scoped composer-reference-layout worktree diff"
+verification_mode: owner
+verified_by: Codex
+verified_at: 2026-09-28
+release_target: none
+cleanup_status: complete
+---
+
+# Verification: Composer Reference Layout
+
+## Verification
+
+- AC-1: PASS — `cua_repl` rendered the actual App through the task-owned Vite renderer at 127.0.0.1:1438. Checked 1280×720 light, 800×600 light/dark, and Ocean palette. Real Composer with deterministic provider props additionally checked at 400×600: configuration wraps above right-aligned actions, checkout/branch wrap without clipping. Screenshots retained in `/private/tmp/codetwo-composer-reference/` (composer-light.png, narrow-light.png, narrow-dark.png, ocean.png, component-narrow.png).
+- AC-2: PASS — `bun test tests/composerLayoutRendered.test.tsx tests/composerGeometryContract.test.ts tests/shellChromeContract.test.ts tests/sceneChip.test.tsx tests/checkoutPickerRendered.test.tsx tests/composerDrafts.test.ts tests/modelFavorites.test.ts tests/modelPreferences.test.ts --timeout 15000` passed 49 tests / 232 assertions. `cua_repl` also filtered the rendered real ModelPicker to Review fast and selected it, observing the trigger update; model-search.png records the filtered list. That menu check used fixture metadata, not a live provider.
+- AC-3: PASS — The same test run verified editor identity and draft preservation, direct disabled permission control without duplication, attachment availability and send/loading/stop callbacks. `cua_repl` entered Chinese text into the actual BlockNote editor and observed it after expansion and collapse. `bun run build:renderer -- --outDir /private/tmp/codetwo-composer-reference/build` passed lint, TypeScript and Vite production compilation. `bun script/verify/docs.ts`, `bun script/verify/sdlc.ts --worktree`, and `git diff --check` passed.
+
+- AC-4: PASS — `bun test tests/windowChromeContract.test.ts tests/sessionHeaderActionsRendered.test.tsx tests/shellChromeContract.test.ts --timeout 15000` passed 31 tests. Actual light/dark toolbar rendering shows matching text and icon colors. Open remained theme-muted while hovered and expanded; disabled source control retained 0.5 opacity. See toolbar-color follow-up below.
+
+- AC-5: PASS — `bun test tests/sessionRailRendered.test.tsx tests/windowChromeContract.test.ts --timeout 15000` passed 46 tests / 390 assertions. Actual light/dark rendering confirmed matching navigation text/icon colors, hover tone and persistent selected fill/marker. Collapse/expand remained functional.
+
+- AC-6: PASS — `bun test tests/composerGeometryContract.test.ts tests/composerLayoutRendered.test.tsx tests/checkoutPickerRendered.test.tsx tests/sceneChip.test.tsx --timeout 15000` passed 29 tests / 148 assertions. Actual compact/document and light/dark controls share the muted toolbar color; stylesheet scope preserves non-neutral semantic variants.
+
+Verdict: verified.
+Residual risk: browser renderer and deterministic component interactions were verified; no native Core/provider turn, remote CI, Windows rendering, merge or release was exercised during local verification. Vite reported existing large-chunk advisories. Initial checks exposed obsolete geometry assertions and test fixture labels, corrected before the passing run. Development reloads invalidated the first draft observation; the stable subsequent expand/collapse check retained the text.
+
+## Contrast follow-up — 2026-09-28
+
+User review rejected the previous visual contrast. The earlier screenshots remain prior layout evidence, superseded for contrast by contrast-light.png, contrast-composer.png and contrast-dark.png in the same screenshot directory. The composer now uses the existing muted surface token, the footer uses rest, primary controls use full foreground, and chevrons no longer attenuate opacity. Permission warning colors and disabled behavior are preserved. Placeholder text is upright.
+
+`cua_repl` inspected actual computed styles and screenshots: light card `oklch(0.954845 0.000289743 none)` with primary text `rgb(32,33,35)`; dark card `oklch(0.319455 0.00766666 none)` with primary text `rgb(242,244,248)`. Placeholder is normal style and fully opaque. Light placeholder `oklch(0.49583 0.00272918 none)`, dark placeholder `oklch(0.74 0.008 260)`. Both schemes show clear input/canvas separation.
+
+`bun test tests/composerGeometryContract.test.ts tests/composerLayoutRendered.test.tsx tests/checkoutPickerRendered.test.tsx tests/shellChromeContract.test.ts --timeout 15000` passed 20 tests / 117 assertions. `bun run lint:styles` and `bun run build:renderer -- --outDir /private/tmp/codetwo-composer-reference/build` passed (lint, types, compilation). Remaining prior behavior evidence is unchanged. Documentation, SDLC and diff checks were rerun after cleanup.
+
+Follow-up cleanup: Vite session 27002 stopped, port 1438 checked, preview tab closed and C2/System restored. Removed the new 49 MB build and build.log. Nine screenshots remain under the existing Codex ownership and next-continuation cleanup checkpoint; prior screenshots document the rejected comparison baseline.
+
+## White-surface follow-up — 2026-09-28
+
+The latest user instruction supersedes the gray fill. The input now keeps the canvas surface (computed `rgb(255,255,255)` in the default light theme), with a composer-only 1px neutral edge and two fixed soft shadows. Dark mode retains the raised surface and a theme-adjusted neutral edge. The shared panel/menu shadows remain unchanged. The edge is painted through the composer elevation token, preserving the existing geometry and block handles.
+
+`cua_repl` checked the actual App at 1280×720 and 800×600, light/dark. Focused and unfocused composer both measured 768×160 with identical white background and box-shadow. Light shadow values: 1px edge, `0 8px 24px rgba(16,24,40,0.1)` and `0 2px 6px rgba(16,24,40,0.04)`. Current evidence: white-full.png, white-narrow.png and white-dark.png under `/private/tmp/codetwo-composer-reference/`. Previous gray screenshots are comparison history, not the final design.
+
+`bun test tests/composerGeometryContract.test.ts tests/composerLayoutRendered.test.tsx tests/checkoutPickerRendered.test.tsx tests/shellChromeContract.test.ts --timeout 15000` passed 20 tests / 117 assertions; `bun run lint:styles` passed. Renderer lint, TypeScript and production build passed after formatting the updated geometry assertion. Documentation/SDLC/diff checks passed after cleanup. Earlier provider and draft behavior evidence remains applicable.
+
+Cleanup: stopped task Vite session 97621; port 1438 has no listener. Closed the preview, reset viewport, restored System theme. Removed the 49 MB build, build.log and the unusable cropped screenshot. The three new screenshots remain under Codex ownership for the next continuation review; no Core or user process was touched.
+
+## Half-pixel edge refinement — 2026-09-28
+
+User requested 0.5px. Updated only the composer edge spread in all light/dark token declarations; fill, shadow blur/offset/opacity and geometry are unchanged. `cua_repl` confirmed the actual white card is `rgb(255,255,255)` and its computed shadow edge is `0px 0px 0px 0.5px`; screenshot: `/private/tmp/codetwo-composer-reference/half-pixel-edge.png`. `bun run --cwd apps/desktop lint:styles`, documentation/SDLC gates and `git diff --check` passed. Existing behavior/build evidence is reused for this CSS-number-only refinement; Vite rendered the current stylesheet successfully.
+
+Cleanup: Vite session 78352 stopped, port 1438 verified released, task tab closed. Only the new screenshot was added to retained evidence under the same owner and next-continuation review checkpoint; no build output created.
+
+## Toolbar-color follow-up — 2026-09-28
+
+The user explicitly chose a uniform soft medium gray. Removed the icons-only toolbar color utility and assigned the existing theme-muted color to toolbar text, buttons, interaction states and icons through one scoped CSS rule. Portalled menus retain their own styling and disabled controls retain their existing opacity and behavior.
+
+`cua_repl` checked the actual App toolbar in light/dark at 1280×720. All six toolbar buttons and their icons share `oklch(0.49583 0.00272918 none)` in light and `oklch(0.718376 0.00647301 none)` in dark. The Open button retained the light muted color while hovered and expanded. Disabled source control kept 0.5 opacity. Screenshots: `/private/tmp/codetwo-composer-reference/toolbar-gray-light.png` and `toolbar-gray-dark.png`. The white composer and 0.5px edge remain unchanged.
+
+`bun test tests/windowChromeContract.test.ts tests/sessionHeaderActionsRendered.test.tsx tests/shellChromeContract.test.ts --timeout 15000` passed 31 tests / 210 assertions. Final CSS passed stylelint. Renderer lint, TypeScript and production build passed. Documentation/SDLC checks and diff inspection passed after cleanup; earlier composer behavior evidence remains applicable.
+
+Cleanup: stopped task Vite session 12272 (exit 130), confirmed port 1438 released, restored System theme and closed the task preview tab. Removed the temporary renderer build and build.log. Retain the two screenshots under the existing ownership and next-continuation checkpoint. No Core or user process was touched.
+
+## Sidebar-color follow-up — 2026-09-28
+
+Extended the existing muted foreground to left window controls, icon navigation and task-sidebar roots/buttons. Status descendants keep their own semantic colors; selection still uses background and the existing marker. No state or behavior code changed.
+
+Actual renderer inspection through `cua_repl` confirmed all 15 visible left controls and their icons share the toolbar tone: light `oklch(0.49583 0.00272918 none)`, dark `oklch(0.718376 0.00647301 none)`. The hovered New task button retained the same light tone. Dark selected Chats retained its neutral fill and fully opaque selection marker. Collapsed the task sidebar, observed the icon navigation remain available, then expanded it. Screenshots: `/private/tmp/codetwo-composer-reference/sidebar-gray-light.png` and `sidebar-gray-dark.png`. Rail rendered tests additionally cover populated task/project lists and state badges; the preview used the empty local renderer dataset.
+
+46 relevant rail/chrome tests and stylelint passed. Reused previous renderer lint/types/build evidence for this CSS-only change; Vite compiled and rendered the current stylesheet. Documentation/SDLC/diff checks passed after cleanup.
+
+Cleanup: task Vite session 55113 stopped (exit 130), port 1438 has no listener, task browser tab closed, System theme and expanded sidebar restored. No Core launched. No build or scratch source created; the two new screenshots remain under Codex ownership until next continuation review. Existing screenshots remain comparison evidence for user refinements.
+
+## Composer-toolbar color follow-up — 2026-09-28
+
+The user's screenshot extends the medium-gray direction to composer configuration, neutral actions and checkout. This supersedes the earlier foreground-label contrast choice. One scoped rule owns neutral Ghost/secondary button color; warning chips are excluded, recording uses destructive, and nonempty submit uses default. Checkout label now inherits its button color. White surface, elevation and geometry are unchanged.
+
+`cua_repl` verified all ten visible neutral buttons, icons and labels share `oklch(0.49583 0.00272918 none)` in light and `oklch(0.718376 0.00647301 none)` in dark. Document-mode controls use the same light tone. Expanded session settings retained the muted color. Screenshots: `/private/tmp/codetwo-composer-reference/composer-gray-light.png` and `composer-gray-dark.png`. Permission/recording/filled-submit exclusions were inspected in source; no real permission grant, microphone recording or provider submission was performed.
+
+29 relevant rendered/contract tests and stylelint passed. The first renderer build identified formatting in the two changed source files; fixed with the repository formatter. Renderer lint, TypeScript and production build then passed. Documentation/SDLC gates and diff checks passed after cleanup. Previous composer draft/attachment/provider evidence remains applicable.
+
+Cleanup: task Vite session 20768 stopped (exit 130), port 1438 checked free, task preview closed, compact mode/System theme restored. Removed the 49 MB temporary build and build.log. Retain the two screenshots under Codex ownership until next continuation review. No Core or user process was touched.
+
+## Cleanup
+
+Removed: task-only `apps/desktop/composer-review.html`, `apps/desktop/composer-review.tsx`, and the 49 MB temporary renderer build under `/private/tmp/codetwo-composer-reference/build`.
+Retained: visual evidence screenshots in `/private/tmp/codetwo-composer-reference/`; frozen-lockfile dependencies and Vite dependency cache in `apps/desktop/node_modules` (1.7 GB) for this checkout.
+Retention owner: Codex.
+Cleanup trigger: review screenshots at the next continuation and remove superseded evidence; dependency/cache lifecycle ends when this worktree is retired.
+Processes: task Vite session 51750 stopped through its launcher; `lsof -nP -iTCP:1438 -sTCP:LISTEN` returned no listener. Both task browser tabs closed, viewport reset, and app preview theme restored to C2/System. No Core process was launched or user process stopped.
+Evidence: `du -sh /private/tmp/codetwo-composer-reference apps/desktop/node_modules`, exact scratch-file listing, launcher exit 130, port inspection, and final `git status --short` inventory. The retained screenshot directory is 136 KB after cleanup.
+
+## Review and release
+
+Approval: local implementation and PR delivery authorized by the user requests recorded in Intent; no human review or merge approval claimed.
+Rollback: See plan.md.
+Release: PR delivery requested; merge and release are not authorized. Remote CI results belong to GitHub and are not implied by local verification.
+
+PR preflight: fetched origin/main at ba9102a8988a4a46e8d827a17aca22f66835e5bb, identical to the implementation base. Reused current rendered/test/build evidence. No preview or build process remains; local screenshots remain review evidence under the recorded retention owner/checkpoint.