The Plugins page declares three slots as children of its main entry: `plugins.item` lists an official plugin's page in the Official group, `plugins.bundle.config` renders a bundle's own form on its page, and `plugins.row.config` gives a row a configure control opening the row's page. Every entry renders in the `summary` and `page` views the page passes as owner props; the page draws the title, icon, and crumb and projects the ledgers beside its store, naming no configurable plugin. `ui-settings-plugins` registers its four host-plane pages into `plugins.item` while the Host serves their namespaces and withdraws them when it stops; the forms keep edits until a save and drop them on leaving the page, so the discard control and the unsaved marker are gone. The Settings section becomes the Built-in plugins shell around the inventory tab, and `settings.plugin.item` is retired. The Official group opens with the optional bundles the installation ships, tagged Beta where the feature is one and without an official tag, followed by the configuration pages. Auto review leaves the optional bundles and the CLI's dependencies; the install guide names it as its example spec. The version tag is a capsule like the other tags. The `plugin-config` web lane drives the pages through the real wire and opens a community fixture's row page after switching its bundle on; the settings lanes re-record the renamed navigation entry.
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.