* fix(web): make the plan chip a business-accent control at row height The chip borrowed the warn-state pair for a plain mode flag, so it read as a warning, and its 2px vertical padding left it 4px shorter than the + and select controls beside it. It now uses the business accent pair, takes the row's 28px height, and carries Chinese copy; the unused chip.off.* dictionary entries had no surface and are dropped. * docs(web): re-record the plan chip README pair * docs(web): state the plan chip's close glyph and drop stale copy claims The READMEs described the chip without the × the component renders, and the zh side still quoted the English accessibility string after this branch gave the namespace its own Chinese one. * test(web): pin the plan chip's accent tokens, height, and Chinese copy The lifecycle-chrome lane asserted the chip's warn pair; it now asserts the business pair plus the 28px row height, and a zh plan-active golden records the 「计划」 label the chip shows under the Chinese locale. * fix(web): color claimed command tokens with the business accent The draft's claimed token still used the warn label color while the plan chip moved to the business pair, so a claimed /计划 read orange above a blue chip. The claim decoration and the tests asserting its inline style now use state-business-primary. * fix(web): keep the composer control row on one line at narrow widths The fixed-width mode chips and send circle left the row's 12px gaps deciding between a slack first line and a wrapped trailing group: beside the plan chip, a long model name moved the model trigger and send circle to a second line while the first line still had room. Narrow rows now use 8px gaps, which holds the row together down to the icon-only model trigger. * fix(web): drop the context ring in the narrow composer row With plan mode on and a long model name, the 28px occupancy ring plus its gap was what pushed the trailing group onto a second line. The ring yields at the same row width that already hides the access-mode label, so the fixed controls keep one line; the reading stays reachable from a wider window. --------- Co-authored-by: Turtle <[email protected]>
apps/web browser e2e
English | 中文
These tests boot the real web composition in-process and drive it with a real Chromium over real HTTP. The lane's mechanics — modes, fixtures, goldens, and the deliberate composition divergences from dsh web — are documented in scaffold.ts and the browser e2e Agent Note.
Completion observations
State-sensitive cases use Workspace, admission, attachment, and model-stream barriers to separate visible intermediate states from completed operations. Details close waits for frame transitions; archive verification assigns an explicit title to the seeded Session and follows that identity across reload. See the CI fixture synchronization decision.
These are Host-face tests
They type-check in the root tsconfig.host.json, not in the Client aggregate, because they read Host services directly: ctx.connection, the Host SessionStore, and ctx.sessionProjectionCache. Driving a browser at runtime does not make a file part of the Client program — the two faces merge Cordis Context under the same keys with different services, so one program cannot see both. Moving these files into the Client aggregate makes every Host-service access fail to compile.
Do not import @deepseek-ai/dsh-client-* here
Importing a Client package — a value or a type — pulls its whole TypeScript project, and every project it references, into the Host build graph. That has bitten this lane once already: four Client consumer packages reference api/remotes' Client face, which cannot compile until Host tsdown has generated @deepseek-ai/dsh-goal/remote, so the Host build phase ended up waiting on an artifact it produces itself.
When a scenario needs a Client-owned constant or pure function, mirror it here instead, next to the commented-out import that names the source module. A drift then surfaces as a missed selector or a stale mirrored value — a loud failure, never a silent pass. scaffold.ts follows this rule for the welcome-notice namespace, acknowledgement field, version, and asserted Chinese copy.
The built-client harness is the exception. assembled-boot.ts imports AppWebEntry, the boot-manifest type, and RemoteMock; assembled-remote.ts imports the Client test runtime's default responses and RemoteMock. These packages are explicit project references for booting the real shell against a test-owned carrier. The chat scenarios mirror conversationContextKey in support.ts instead of importing its Client owner.
Nothing mechanically enforces this rule; keep it in review.