ssh is the last panel that took the center column over at the DOM level. It
now registers a row in the shell's own panel list (sidebar.panellist) and a
keyed page in the layout's main slot, like the task board and the skill
center, so the shell owns the row box, label, highlight, rail and switch.
That retires the whole takeover apparatus:
- shared/client/panel-mount-core.ts and shared/client/sidebar-entry-core.ts
are deleted along with every synced copy; ssh's mount.tsx, sidebar-entry.ts,
panel-mount-core.ts and sidebar-entry-core.ts go with them. The
body-mutations hub now serves only the aggregate shell and the usage card.
- The dsh-panel-activate handshake and PANEL_FAMILY occupancy table are gone:
the layout's keyed main slot is the single occupancy authority, so the board
and the skill center no longer coordinate with anyone.
- The terminal survives the move because the PTY session is host-owned (see
the previous commit): the layout unmounting a deselected page costs the view
its xterm instance, not the remote shell, and the terminal tab reattaches by
session id. The panel's tab, connect request and session id live in the
controller rather than in component state.
Tests move with the design: the takeover specs (center-panel-lifecycle,
center-column-css, sidebar-entry, the layout specs, board/ssh coexistence) are
replaced by native-panel-registry specs that drive the real SlotCore, plus a
controller-state spec for ssh's view state.
Validation: pnpm typecheck; pnpm test (ssh 202, task-board 561, skill-explorer
121, shared 107, all packages green); pnpm test:scripts 342/342;
pnpm test:standards; pnpm docs:check; pnpm i18n:check; pnpm aggregate:check;
pnpm libs:check; pnpm market:check.
A Host reload composes the new loader entries before the old ones are torn down, so the reloaded aggregate row asked for a package name the previous entry still held, its mount was dropped, and the previous entry then released the name with nobody left to take it: the row stayed listed as active with no degraded record and served no Host routes until a restart. The guard now queues the refused mount with its own context and replays it one microtask after the release, and a waiter whose fiber dies first is dropped instead of replayed. The Symbol.for registry keeps its Set shape because the satellite repositories compile their own copy of this guard.
Move every family manifest, the plugin scaffold and the shared workspace
package onto the 0.1.7-rc.2 cohort, together with the surfaces that state
the same fact:
- the dsh.engines.dsh floors and the matching @deepseek-ai/dsh host peers
- the release-age exclusion ledger and the two packageExtensions keys
- the root README badges, the CI/release mount-smoke pins and the
docs/publish-prep.md and docs/plugins.md prose
Two rc.2 facts were verified against the published artifacts rather than
inferred from the version number:
- the shell's frozen static module table is unchanged (the same nine
specifiers in the dsh-web-frontend rc.2 dist bundle), so
shared/web-platform.ts keeps its list and only its provenance comment
moves; the new dsh-client-shortcuts is an ordinary client plugin, not a
static module
- @deepseek-ai/dsh-client-ui-primitives@0.1.7-rc.2 imports a further
undeclared face, @deepseek-ai/dsh-util-code-language, so the
primitives packageExtensions patch gains that pin
The satellite peer floors in pnpm-lock.yaml still read >=0.1.7-rc.1: they
belong to the satellite packages and move when those repositories release
the aligned version.
Move every consumed @deepseek-ai family specifier (272 across 23 manifests),
the dsh.engines.dsh floors and the matching @deepseek-ai/dsh host peers, the
plugin scaffold, the release-age ledger and the packageExtensions keys, the
desktop host payload pin with its regenerated hoisted lock, the root lock,
the CI and release mount pins, the README host badge, the cohort-version
source literals and the cohort prose in docs.
Regenerate the official token contract from the 0.1.7-rc.1 surfaces (293 to
299 names, additive, contractVersion stays 1) and verify the browser platform
seed against the rc.1 shell bundle. Harden the shell-isolation real-boot spec
so it only accepts an app-boot copy whose own non-optional peers resolve: this
cohort pulls an incomplete copy into the workspace for the first time, through
dsh-agent-preset-registry.
Record the cohort decision, the official release-notes and compare evidence,
the published-surface delta and the native-first overlap inventory in
.agents/notes/implemented/architecture/2026-09-23-sdk-cohort-0.1.7-rc.1.
The alpha.2 host import of npm undici costs Node built-in fetch its
content-encoding decompression. shared/host/http.ts now owns the rule as
withIdentityEncoding(init), and every host fetch that parses a remote body
adopts it: the market installer (manifest and assets), the plugin-manager
npm-registry probe, the remote-web-ui registry/GitHub probes and inner
app-shell fetch, and the usage provider probes.
Bump every @deepseek-ai family specifier (258 specifiers across 25
manifests), every dsh.engines.dsh floor (22 packages), cordis to ^4.0.4
and schemastery to ^3.18.4. Align the shared ledger, the desktop host
runtime seed (overrides, exclude list, regenerated lockfile) and the root
lockfile, plus the CI and release mount pins, the cohort-version docs and
the source literals that name the cohort.
The floors had been written as ">=>=0.1.7-alpha.2"; repair them to
">=0.1.7-alpha.2" so the family-engine regex gate passes again.
dsh-market/lib is rebuilt because its committed bundle embeds the resolved
cordis and schemastery paths; the Skin Center output is rebuilt in the
token-contract change that follows.
The official family published 0.1.7-alpha.1 while the alpha branch stood on
0.1.6-alpha.2, and the machine's DSH moved to the new cohort. Four changes in
the release reach this family:
- The settings subsystem is now keyed by the profile entry: ctx.settingsScope,
SettingsScope* and SettingsProvider.installSection/register are gone, replaced
by ctx.configForms, ConfigForm* and SettingsForms. Every family host half
carries its settings on its own Config with volatile fields, every browser
half binds through the webUiSettings service (which resolves a family
namespace to the owning profile entry id and then binds natively, with the
loopback bridge as the fallback), and the shared card/form layer speaks the
new contract including its boolean refusal answers.
- The agent-preset domain became declarative: directory discovery is gone, a
preset is a declaration a plugin registers at runtime. dsh-preset-center now
declares installed presets from its host half (install = declare) and
dsh-liangshen declares its own preset instead of syncing files into
~/.dsh/.agent-presets.
- Session V4 moved the tool-result failure flag to the message root; dsh-pet
read the removed content block and silently rendered failed turns as
successes.
- The client design system renamed its icons and reshaped SessionListState;
the affected call sites and fixtures follow.
Cohort: every consumed @deepseek-ai/dsh-* range, the scaffold, the README
badges, the CI and release mount-smoke pins, the desktop runtime pin and both
lockfiles, and the docs move together; the vendor pins follow the cohort's own
release (cordis 4.0.3, cosmokit 1.8.4, schemastery 3.18.3,
cordis-plugin-include 1.0.8, cordis-plugin-loader 1.0.4). The workspace
packageExtensions restore @deepseek-ai/dsh-util-workspace-path, which
dsh-client-ui-primitives now imports from its emitted lib while declaring no
dependencies.
scripts/e2e-mount.sh, scripts/e2e-mount-rewrite and its test, and
scripts/publish-legacy-aggregate.mjs pass GNU tar --force-local on the
Windows/MSYS lane, where a C:\ output path is otherwise read as a remote host.
The mount smoke does not pass on this workstation: the local dsh CLI is a
symlink into a DSH checkout whose built lib/ predates this cohort's own
multi-file dsh.bundle.patch support, so profile boot crashes on the official
dsh-web-app array before any family row is evaluated. That is host-side build
staleness outside this repository; the lane passes once the DSH checkout is
rebuilt. Recorded with the rest of the decision in
.agents/notes/implemented/architecture/2026-09-22-sdk-cohort-0.1.7-alpha.1.md.
Adapt dsh-web to the experimental @deepseek-ai/dsh-* 0.1.6-alpha.2 cohort in the
same isolated alpha worktree and DSH home, so the accepted 0.1.5-rc.1 stable
environment, its profile tree and the global dsh CLI stay untouched.
Cohort: every consumed range, the plugin scaffold, the root README badges, the
CI and release mount-smoke pins, the desktop runtime pin and its lockfile, both
release-age exclusion lists and the desktop overrides block move to
0.1.6-alpha.2; the declared host floor becomes dsh.engines.dsh >=0.1.6-alpha.2.
cordis, cosmokit and schemastery keep their independent support pins.
@deepseek-ai/dsh-client-ui-workspace joins the repository exclusion list and the
packages that navigate gained it as a devDependency, because the family now
consumes it directly.
Adaptation of the SDK delta - the multi-instance Client Session model:
- SessionListState.current/currentAddress and SessionSummary.completed are gone,
and ISessions.open()/openSubagent()/clear() are removed. View selection now
belongs to the workspace UI, which owns the main-area reference and publishes
it as retainedBy.mainView; navigation is ctx.uiWorkspace.openSession(target).
- shared/client/main-session.ts adds mainViewSessionId(byId), the catalog-level
reading of that marker, and sync-shared copies it into the seven consumers.
Ten call sites across eight packages read the removed faces; two of them
(git-graph auto-isolation, session-archive) type-checked against local
structural doubles and never appeared in the compiler's error list, so the
inventory was built by searching for the removed names rather than by
trusting tsc.
- dsh-task-board's framework-free controller port changes from a list snapshot
to { current(), open(), subscribe() }; the wiring resolves current() from the
marker. dsh-git-graph navigates new worktree sessions through the workspace UI.
dsh-pet, dsh-plugin-manager, dsh-doctor, dsh-liangshen, dsh-session-id and
dsh-session-archive read the marker instead of the removed selection.
Upstream packaging defect: dsh-client-ui-primitives@0.1.6-alpha.2 imports
simple-icons from its emitted lib/index.js while the published manifest still
declares no runtime dependencies; packageExtensions restores it at the exact pin
the official source resolves (16.31.0), beside the existing diff/shiki/micromark
restorations. No installed file is patched.
Native-first: no family row registers the new sidebar panel slots, and the
alpha.1 to alpha.2 slot-key comparison is additive only. dsh-plugin-manager
already extends the official Plugins section through settings.plugins.tab rather
than mounting a parallel page, and that stays the right native extension. The
aggregate declares no external npm plugin, so the new runtime plugin dependency
resolution has no external hard-binding to unmask. The inject contract records
dsh-client-ui-workspace as an approved inject module with its rationale.
Testing: pnpm typecheck, sync-shared:check, skin-center:check, community:check,
libs:check, build, test, test:scripts, test:desktop, runtime-deps:check,
aggregate:check, emoji:check, test:standards, docs:check, i18n:check and
market:check pass, and pnpm install --frozen-lockfile --ignore-scripts resolves
the lockfile. The alpha host in ~/.dsh-alpha is upgraded to 0.1.6-alpha.2 and
every profile @deepseek-ai copy is a symlink into it (235 ok, 0 warn, 0 fail);
dsh --profile alpha-web --dump-config composes the family rows with the retired
ui-settings-unarchive-sessions row.
Adapt dsh-web to the experimental @deepseek-ai/dsh-* 0.1.6-alpha.1 cohort on a
permanent alpha branch with its own worktree and DSH home, so the accepted
0.1.5-rc.1 environment, its profile tree and the global dsh CLI stay untouched.
Cohort: every consumed range, the plugin scaffold, the root README badge, the
CI and release mount-smoke pins, the desktop runtime pin and its lockfile all
move to 0.1.6-alpha.1; cordis, cosmokit and schemastery keep their independent
support pins. Both release-age exclusion lists and the desktop overrides block
follow, and stale rc-only entries are dropped rather than carried.
Adaptation of the SDK delta:
- agent/session-start was removed and agent/created became async serial.
dsh-tool-describe-image awaits its resting verdict inside the listener, so
the tool mask lands before the first request's toolset is assembled, and the
listener never rejects because a rejecting listener now aborts agent
creation. dsh-liangshen merges its two stale, mis-shaped registrations into
one correctly-shaped listener.
- ctx.codeRuntime was renamed to ctx.ptcRuntime; reading the old key would have
silently disabled PTC staging in the liangshen preset.
- dsh-tools now statically imports its new required peer dsh-sandbox, carried
as a root host face; dsh-client-ui-primitives imports diff, restored through
packageExtensions.
- The deprecated synchronous session readers still ship, so existing uses are
recorded rather than migrated.
Native-first: dsh-session-archive replaces its private requireState/setState
workaround with the new public workspace.unarchiveSession verb, while keeping
the inventory, batch operations, cascade physical delete and auto-maintenance
policies that the native archived-sessions page does not provide. The native
SSH remote-workspace provider is recorded as complementary to dsh-ssh, which
covers host management, SFTP, tunnels and cluster execution instead.
Validation: the full CI-equivalent gate sequence passes in the alpha worktree
(typecheck, sync-shared, skin-center, community, libs, build, test,
test:scripts, test:desktop, runtime-deps, aggregate, docs, i18n, market), and
dsh --profile alpha-web --dump-config composes the alpha host with the family
rows from the isolated home. Live GUI acceptance against a running alpha
instance remains open and is recorded in the Agent Note.
Family plugins each installed their own document.body subtree MutationObserver,
so per-mutation cost grew with the number of installed plugins during chat
streaming. Add shared/client/body-mutations.ts: one page-wide observer
registered on globalThis under Symbol.for, delivering accumulated records to
subscribers at most once per animation frame. sidebar-entry-core,
panel-mount-core and the aggregate shim subscribe to it instead of observing
body themselves.
skin-center: cache the composer seat and skip the unchanged
--dsh-composer-height write, coalesce mutation-driven height and
conversation-content checks to one per frame, and write backdrop markers only
when they differ. The aggregate caches the [data-dsh-frame] lookup instead of
re-querying per mutation batch.
Controlled Chromium harness, 301 frames over 5 s, mean of 3 runs: body observer
callbacks 1503 -> 902, callback time 237 ms -> 2.9 ms, ScriptDuration
69.6 ms -> 13.6 ms, TaskDuration 984 ms -> 725 ms; LayoutCount unchanged
(natural per-frame paint layout). See
.agents/notes/implemented/bug-fix/2026-09-11-combined-plugin-mutation-cost.md.
The 0.1.5-rc.1 dsh-web-frontend frozen module table adds the
dsh-client-ui-dockkit row next to the existing platform seed. The seed
list and its mirror contract move together; no repository client bundle
imports the new module yet, so artifacts are unchanged.
The DSH loader mounts every patch row as one transactional group: a single
plugin that fails to import or start rolls back the whole family and aborts
'dsh web'. Redefine the fault unit to one plugin.
- scripts/aggregate.mjs emits each family insert row with the aggregate's own
never-failing shell module as the row name and the real plugin package in
the row config (config.plugin, original config under config.config);
dsh-i18n and all external npm rows keep mounting directly
- @linxin666/dsh-web-all main entry is the shell: it imports the real module
at start time, contains import/shape/activation failures to that entry
(logged + recorded), and mounts the real plugin as a nested plugin so
cordis service scoping, lifecycle, and retraction semantics stay intact
- loopback-only GET /api/dsh-web-all/degraded reports the degradation ledger
for doctor/monitoring consumption
- shared/host/run-guarded.ts (+ synced copies in the four packages with
in-process HTTP/poll faces) converts fire-and-forget rejections into logged
errors against the host's installFailLoud whole-process exit
- tests: shell-isolation.spec.ts runs the real installed host boot through
start-failure / import-failure / no-webServer scenarios plus the control
case proving today's direct-mount shape still kills the boot
Evidence: pnpm typecheck, pnpm test (22 packages), pnpm docs:check,
pnpm i18n:check, pnpm aggregate:check, pnpm test:scripts (234), and full
pnpm build all pass. Bundle-layer change: needs a dsh web restart.
GUI verification showed the L1 sidebar rows of task-board, dsh-ssh and
dsh-skill-explorer staying Chinese while their settings sections switched
language: those rows read a package-local t/tt helper that picks zh/en
from documentElement.lang, which the SDK Language switch never updates
(the rows also stayed Chinese under English — proven by a live switch
test). The module-level helpers now prefer a wired SDK translate seat
(ctx.locale.bind(NS), set in apply once the dictionaries register) and
fall back to the document-language pick only when unwired; the shared
sidebar-entry core gains an optional refresh subscription (ctx.locale
.subscribe) that re-applies label/aria-label/tooltip on locale changes,
and the board/panel mounts re-render an open view on the same signal.
Verified live: switching to Русский flips the rows to Доска задач /
Центр навыков without a reload.
Bump the whole @deepseek-ai cohort to the dsh-v0.1.2-alpha.2 preview:
rebuild the cohort tarball store from the upstream tag, realign cordis
to 4.0.2 per the trimmed peer set, migrate every settings consumer onto
the ctx.settings service seam (installSection), declare the severed
dsh-client-ui-session type-graph edge for git-graph, and refresh the
CI cohort cache, inject-contract pin, docs badges, and cohort-line
comments.
The 0.1.2 SettingsScope.mutate never rejects: a refused mutation recovers
with a fresh Host view and resolves, so save() reporting success on
resolution dropped the user's staged drafts on any Host refusal. save()
now keeps the single atomic mutate but judges every planned write against
the settled snapshot (set: the user layer holds the value; unset: the
field is gone; one miss fails the whole save and keeps the drafts), while
still surfacing bridge-scope rejections. Secret sets are judged by
settlement because the Host strips role('secret') fields from every wire
view and the snapshot exposes no secrets sidecar (documented). The fake
scope in the tests now encodes the real resolve-on-refusal contract, and
the package copies are regenerated by sync-shared.
- CardForm.save dispatches one scope.mutate(path-ops) instead of the
removed per-field compat batch surface; either every write lands or the
failure surfaces with its host rejection message
- the describe-image tests import the CallId brand as ToolCallId under
the 0.1.2 llm faces and drop the client-runtime test imports
- the pet and shared fake scopes gain the mutate contract member
- pet store engine imports move to dsh-client-store (same defineStore
contract, promoted upstream into the frozen platform table)
- settings scope types via dsh-client-ui-settings/client; sessions via
api-session-controller/client; slots merge via client-ui-renderer/client
- test mocks follow the new module paths; shared settings-form spec
(now live under the spec include) covers the migrated shared source
- shared devDependencies gain dsh-client-store for its own spec run
- doctor: settings scope via ui-settings/client, store engine via
dsh-client-store, sessions port via api-session-controller/client,
slots merge via client-ui-renderer/client; inject list follows the
runtime roster
- git-graph: same remap; SessionSnapshot.composerPhase is gone, the
blank empty-log mirror plus openState now drives the branch-selector
seat; worktree session launch moved to ISessions.create
- shared: platform table mirrors the 0.1.2 frozen module table (client-store
in, client-runtime out), preset drops RUNTIME_STORE_EXEMPTION and the
dead host-apiproxy branch; the platform contract spec becomes runnable
(renamed to *.spec.ts so the shared vitest include matches)
- aggregate: freeze dsh-aionui-panel for this cohort (discontinued,
unbuildable without client-runtime) pending the maintainer's final call
- sync-shared: settings-form shared source migrated, copies resynced
Git-graph, pet, and skill-explorer carried byte-identical fence logic.
The decision now lives in one shared source distributed as generated
sync-shared copies; each package keeps a self-describing thin wrapper.
Canonical tests added under shared/tests; wrapper specs stay as wiring
tests.
Dual-publish the final @linxin666/dsh-web-ui-all for the two-release transition window, then stop automatically. Plugin-manager recognizes the legacy aggregate and performs a transactional CLI migration. Doctor introduces autoMigrate (default true) and migrates before DSH launch, preserving bundle order and verifying --dump-config before boot. Add compatibility audit gates, Agent Notes, and isolation evidence.
The seat machine was removed with the DSH Market hub; delete its shared test and drop the community-plugins consumer from the settings trio manifest (the package no longer ships a client half).
- dsh-market client becomes the DSH Market hub: one settings.section entry
(id dsh-market) declaring the dsh-market.tab child slot; the Store card
registers as the first tab panel
- skin-center / pet / community-plugins cards register into the hub tab via
the shared two-seat machine (shared/client/settings/market-tab.ts + synced
copies) and fall back to their own first-level section standalone; the
seat logic is unit-tested with fake slots
- skin on-demand plan: the skin-center npm package ships only blue-fantasy
(files whitelist narrowed); the other 17 skins stay in the repo as the
market/gallery source and install on demand into /Users/zcl/.dsh/skins
- fresh installs seed blue-fantasy as the active skin (host seed in
active-state); an update whose active skin vanished still falls back to
the stock look browser-side (existing boot path)
- tests: shared market-tab seat spec, market client-apply + hub component
specs, community client-apply dual-wiring spec, active-state seed specs,
npm-files invariant in skin-asset-dirs
Re-verified the platform seed table and dsh.client.inject module set against
the 0.1.1-rc.2 dist and shell composition (no module-list change; slots is a
frozen static module without a ./client export) and updated the version
annotations. Add the upgrade handoff/validation record to docs/archive.
save() snapshotted only the field names it was writing and then deleted each landed field's staged entry by key, so an edit staged for the same field while the write was in flight was silently discarded — the newer draft snapped back to the just-saved value.
Snapshot the staged entry (not just the name) before the write and delete a key only when its entry is still the one this save started from; a draft staged mid-save (which replaces the entry) survives. Sync the shared copy to all consumers and cover the same-field case with a test.
Evidence (verified at HEAD c11480d7, maintainability survey):
- Only maid-atelier had a typecheck script, so the root pnpm -r typecheck
gate skipped the other 10 leaf skins and skin-center entirely: skin TS
errors were invisible to CI (vitest transforms without type checking).
- blue-fantasy lacked tsconfig.json.
- shared/package.json declared vite-tsconfig-paths but shared/vitest.config.ts
never referenced it (all skins that use it declare it themselves).
Change:
- 10 leaf skins + skin-center now run tsc -p tsconfig.json (+ vitest program
where present) under the root typecheck gate; blue-fantasy gained the
standard skin tsconfig.json; the skins gained the typescript devDependency
the scripts need (skin-center also gained @types/node/@types/react/
@types/react-dom for its node + React sources). Root pnpm -r typecheck
passes for the whole tree.
- shared/package.json drops vite-tsconfig-paths.
Reversed finding (recorded): @testing-library/dom was also flagged dead in
dsh-git-graph/dsh-pet/dsh-remote-web-ui, but removal broke their test runs —
@testing-library/react v16 imports it at runtime and pnpm strict resolution
requires the direct declaration, so those three keep it (investigation
evidence, not a change).
Verification:
- pnpm -r typecheck green across the monorepo (previously 12 packages were
unchecked); per-skin tsc runs green; shared/pet/git-graph/remote-web-ui
test suites green after the dep edits; pnpm-lock.yaml regenerated.
Evidence (verified at HEAD ac6c42cf, duplication survey):
- dsh-ssh, dsh-task-board, and dsh-skill-explorer each carried a full copy of
the same MutationObserver sidebar-row injection (root discovery, new-session
anchoring, family-block ordering, whole-tree rebuild fallback, self-heal,
active highlight): 45-70% identical per pair, ~160 lines each. A shell DOM
change had to be patched in three places; the per-copy deltas were only the
icon, i18n, CSS module, click action, active state, and family list.
Change:
- The DOM logic now lives exactly once in shared/client/sidebar-entry-core.ts
(mountSidebarEntry(options)), delivered to the three packages as generated
copies via the sync-shared manifest (packages/*/src/client/sidebar-entry-core.ts).
Each package keeps a ~35-line wrapper that supplies its icon/copy/css/toggle/
active bridge and its original family selectors and 'before'/'after'
position, so rendered ordering and behavior are unchanged. The idempotency
guard (task-board's DOM-level dedupe) now protects all three rows.
- sync-shared.test.mjs tracks the new copies (49 total, client 23).
Verification:
- pnpm typecheck + pnpm test + pnpm build for dsh-ssh (98), dsh-task-board
(226+1 skip), dsh-skill-explorer (46), shared (48 incl. 3 new core tests:
mount+click, idempotency skip, active highlight), pnpm test:scripts (99),
sync-shared --check clean. GUI-level shell behavior deferred to the
merge-phase web QA pass.
dsh-live-stats (real-time token estimation and TPS projection) is no
longer supported. Delete the package, drop its tests and vitest config,
remove it from the web-ui-all aggregate manifest, the settings bridge
allowlist, sync-shared consumers, GitHub issue/PR templates, and all
docs; shrink the lockfile by exactly the removed importer. Update the
mount-once and sync-shared script tests to the reduced copy set.
Gates: typecheck, test, test:scripts, docs:check, aggregate:check,
gallery:check, skin-center:check, community:check, runtime-deps:check,
build all green.
Aggregate generator now rewrites child row ids to web-ui-* (stripping the
ui- prefix) and validates uniqueness, so installing the family bundle next
to a standalone plugin no longer trips the loader's duplicate-entry check.
A shared shared/host/mount-once.ts module (sync-shared synced into 12 family
packages) makes the host apply a no-op when the same plugin loads twice;
browser half is deduped by package name. Docs synced: root README FAQ,
web-ui-all README trio, plugins.md, packages/AGENTS.md; regenerated
dsh-skins and dsh-web-ui-all patches and skin-center lib; archived the
verification snapshot in docs/archive.
shared/host/poll-guard.ts is a bounded, non-overlapping, backoff-capped
poll loop; shared/host/dsh-home.ts centralizes DSH_HOME resolution. Both
carry unit tests in the shared package. The sync-shared manifest now
mirrors poll-guard into git-graph and aionui-panel and dsh-home into pet
and liangshen; adoption wiring lands with each package's lane.
Extract the byte-identical settings card trio (settings-form.ts,
PluginSettingsCard.tsx, settings-card.module.css) that pet, task-board,
remote-web-ui, live-stats, and describe-image each carried, into
shared/client/settings as the single source of truth. A new
scripts/sync-shared.mjs mirrors the trio into the five consumers with a
generated-file header, and its --check mode (wired into test:scripts)
fails on drift. The shared superset keeps every variant behavior:
number-field constraints, per-field in-flight save survival, choice
fields, and the focus-visible styles. The shared package also carries
the first CardForm unit tests (11).