Every runtime seam the plugin rides is source-identical from
0.1.7-alpha.2, so no code changes; the supported V4 baseline moves
from 0.1.7-alpha.2 to 0.1.7-rc.1 (the alphas leave the matrix). rc.1
adds the harness-side plugin peer-compatibility admission gate
(evaluatePluginCompatibility); the plugin's >=0.1.2-rc.1 dsh peers
satisfy it under the gate's includePrerelease check, so the bundle
composes with no exemption. The release workflow fetches the rc.1 tag
for the compat matrix and points the plugin checker at the npm next
dist-tag (0.1.7-rc.1).
dsh 0.1.7-alpha.2 introduces no breaking change for the plugin: the
vendored cordis 4.0.3 -> 4.0.4 bump is version-metadata only, the
durable log stays at V4 with an unchanged event vocabulary, and every
seam the plugin rides (session-projection registry, right-Sidebar tab
and navigation faces, connection RPC, settings namespace, agent-loop
pre-step, ui-primitives icons and markdown labels, the plugins.bundle
config slot) is source-identical or additively extended. The remaining
deltas are internal to the harness: an optional turnWindow on history
page requests, client-module batch-failure recovery, and host-side
tool-result retention (the already-documented deferred image-offload
area).
Per the one-representative-per-generation convention, replace the
alpha.1 baseline with alpha.2 across the declared releases, the seam
matrix, the release workflow's tag fetch, and the compatibility page.
The compat matrix now boots the real alpha.2 sources on cordis 4.0.4.
npm's 0.1.5-rc closure is broken upstream: dsh-web-app 0.1.5-rc.3 was
published while the dsh-client-ui-sidebar-documentpreview rc.3 it
requests never was, and every ^0.1.5-rc range (including `latest`'s own)
resolves to it — so installing the checker's `@deepseek-ai/dsh@latest`
fails with ETARGET before any check runs. Pin the checker to `alpha`
(0.1.7-alpha.1), whose closure is complete and which is the release this
cut declares compatibility with.
Add the V4 baseline (session format 4, cordis 4.0.3): the fold's switched
event families are unchanged, so its entry rides the V3 seam face with a
per-generation settings surface — the `register` face and the retired
`settings.plugin.item` slot are asserted BOTH ways (present through V3,
absent on V4+), and the modal dock probe now pins the sidebar-leading
grid track the measure actually reads instead of one generation's exact
template. New ICON_SEAMS probe: every icon the client renders must exist
in at least one spelling per tag, so a platform rename fails the matrix
instead of the browser. Declare the release in the manifest, fetch its
tag in the release workflow, and document the V4 generation with its
known deltas (deferred: the preferences card's Plugins-page migration,
image-offload accounting).
The supported V3 line follows the newest release: the baseline becomes
v0.1.5-rc.1 (tag, vendored session package) and the declared release map
names 0.1.5-rc.1. The release workflow fetches the new tag, and the
disposable-profile install/uninstall was verified for real against the
published 0.1.5-rc.1 CLI (install OK -> 1 composed row -> uninstall OK ->
0 rows).
At 0.1.5-rc.1 `SidebarRightGuideEntry` regained its optional `description`
line, so the guideEntry probe pins order/title/description/icon and the
now-pointless "absent" assertion plumbing is dropped.
Every support/capability label states the 0.1.5-rc.1+ floor;
`0.1.5-alpha.x+` remains only as the V3 LOG-generation label (the format
first appeared at 0.1.5-alpha.1). The docs record the verified matrix and
the seam movement across alpha.1/alpha.2/rc.1.
The supported V3 line follows the newest release: the baseline becomes
v0.1.5-alpha.2 (tag, vendored session package) and the declared release
map names 0.1.5-alpha.2. The release workflow fetches the new tag.
The alpha.2 client seams are pinned in the matrix: the conversation
surface moved under the keyed `main` panel (`main.conversation`) while
the plugin's `conversation.view` seat keeps its name, and
`SidebarRightGuideEntry` dropped its description line. tests/baselines.ts
therefore gains a guideEntry probe (the fields present plus the dropped
copy field asserted absent).
Every support/capability label states the 0.1.5-alpha.2+ floor;
`0.1.5-alpha.x+` remains only as the V3 LOG-generation label. The docs
record the verified matrix, the two moved seams, and the install row
that still waits on the CLI's npm publication (the `alpha` dist-tag
lags the release).
Each line carries its own durable-event vocabulary (assistant/chunk and tool/code-dispatch are V0/V2-only; system/message and tool/ptc-dispatch are V3-only), so Baseline.foldEventTypes replaces the single global list and a matrix test asserts the union still covers every fold case. The release workflow fetches both new tags before pnpm test.
The install/runtime compatibility floor moves from 0.1.1-rc2+ and
0.1.2-alpha2+ to 0.1.2-rc.1+. The 0.1.1 line and the 0.1.2-alpha previews
leave the support matrix, so every pre-0.1.2 fallback they existed for is
removed outright:
- client history: drop the legacy `connection.api.sessions.history` face,
the `result`-nested envelope, and the `events` rows field — the gateway
`remote.session.page` face and its ClientResult envelope are the only
served shapes.
- client image loader: drop the pre-0.1.2 `conversation.resolveImage` face;
`uiConversation.imageUrl` only.
- client node join: drop the pre-0.1.2 `useSession.nodes` seat;
`useChat`'s ChatSnapshot `legacy.nodes` only (UseSessionLike removed).
- tests/baselines.ts: single baseline `v0.1.2-rc.1`; HostSeam dropped (the
registry generation hard-codes header-passing init and seq-0 eager
seeding, now mirrored with the exact `init(header, inheritedEventCount)`
arity); ClientSeam slimmed to the consumed probes.
- deps: devDeps re-pinned to the 0.1.2-rc.1 generation (types compile
against the real rc.1 surfaces); cordis ^4.0.2 and schemastery ^3.18.2
per the rc.1 peer requirements; dsh-scope/dsh-invariants declared as dev
deps; peer floors >=0.1.2-rc.1; dshReleases table trimmed.
- release workflow fetches only the dsh-v0.1.2-rc.1 tag; docs updated
(AGENTS.md, README, docs/compatibility.md).
- verified against the rc.1 sources: the tool-attribution map (names and
packages, incl. the documented `subagent_fork` alias), MCP naming, the
page/imageUrl/legacy.nodes faces, and the registry contract.
Add the two heads of the declared 0.1.2 lines as matrix baselines: the
preview window's head (dsh-v0.1.2-alpha.5, where the DSH STORE
latest-three compatibility window currently ends) and the next-channel
release candidate (dsh-v0.1.2-rc.1). Their seam faces verified identical
to alpha.2, so the generation's client faces collapse into one shared
CLIENT_0_1_2 constant.
The new baselines surfaced a real staging drift: since 0.1.2-alpha.3 the
session-projection sources import SessionLogOffset/SessionSeq from
@deepseek-ai/dsh-session as runtime values, so the compat driver now
installs each baseline's own vendored dsh-session release next to cordis
(Baseline.session).
The release workflow fetches the two new baseline tags before testing.
docs/compatibility.md records the per-release verification evidence,
including the 2026-09-03 disposable-profile install/uninstall matrix
across 0.1.1-rc.2, 0.1.2-alpha.3/4/5 and 0.1.2-rc.1 (ref
AI-Scarlett/DSH-Store#439).
Drop the test:bundle / test:compat scripts. The bundle smoke moves into
the `compat` vitest project (tests/compat/bundle.spec.ts): the
__ModuleLoader__ handoff, the CSS channel, slot/trigger registrations,
and HMR safety over the BUILT lib/client.js — so `pnpm test` runs the
whole verification (host, client, built artifacts, real-code compat
matrix) in one pass with the per-file 100% coverage gate. The
built-artifact specs skip cleanly when lib/ is absent; the release
workflow builds before Test, so they always run there.
Also drop the standalone Bundle smoke step from the release workflow
and refresh the docs/comments that referenced the removed scripts.
tests/baselines.ts is the single source of truth for the supported dsh
baselines and their seam faces. Three layers verify them:
- always-on host registry-contract drivers over the REAL projection
definitions (plain-JSON cache-write gate, wire delivery, checkpoint
restore, init-header tolerance) — the recurring host incidents,
- always-on client-face matrix (nodes seat, image loader service,
history face + envelope, markdown chrome, full tab render) — the
recurring client incidents,
- the `compat` vitest project: stages the REAL harness sources at each
baseline tag, boots the tag's registry on the cordis release that line
vendors, applies the BUILT plugin, and probes slots/faces/vocabulary/
platform table from the tag's sources. The release workflow fetches
the baseline tags before `pnpm test` so the matrix always runs there.
Adding a future baseline = one tests/baselines.ts entry + one fetched
tag; a failing probe names the seam to re-fit.
The source-level suite needs no lib/ artifacts, so PR checks test
first and build afterwards solely to feed scripts/bundle-smoke.mjs;
the release workflow gains the same smoke step between test and
publish.
Compress the CSS-channel note in tsdown.config.ts, the vitest lane
explainer and the release workflow header; fix the install-hooks header
(git only consults .git/hooks when core.hooksPath is unset); slim the
social-preview generator and bundle-patch comments.
* feat: add PR checks workflow for automated testing and validation
* feat(client): hide the composer while the Context tab is active
The view ring mounts one view at a time, so the tab's .lc-root existing
inside the shell scrollport is exactly 'Context tab active' — a single
:has() rule hides the sticky composer seat and hands its height to the
dashboard. display:none keeps the seat mounted, so drafts survive the
round trip.
Interactive takeovers (approval / question / plan-review panels) replace
the bar inside the same seat; keep the seat visible while one is present
so a pending decision is never hidden behind the focus mode.
Closes#11
* ci: build before test in PR checks
Client tests mount the built bundle (viewBed.ts reads lib/client.js), so
pnpm test without a prior build fails with ENOENT in a clean checkout.
One job instead of two: the dsh-plugin-checker step now runs right after
checkout inside publish, dropping the needs: handoff (saves a runner
spin-up per release; the check still gates every later step).
Mirror dsh's own lint setup (oxlint 1.76.0 pinned exact, oxlint-tsgolint
7.0.2001 with options.typeAware, correctness category off, hand-picked
rule list; jsPlugins: eslint-plugin-sonarjs + @stylistic/eslint-plugin
with the official style profile: indent 2, semi never, single quotes,
comma-dangle multiline, max-len 140). Scope mirrors official too: src TS
only, generated/bundled output (lib, *.js, *.mjs, *.config.ts) ignored.
Wiring: pnpm run lint / lint:fix scripts, plus a Lint step in the
release workflow between Build and Test.
Fixes applied (runtime behavior unchanged; verified against dsh
0.1.0-rc.7 and 0.1.1-rc.2 wire shapes, see below):
- autofix pass: arrow-parens, comma-dangle, eol-last, indent, quotes
- restrict-plus-operands: 31 string+number concatenations -> template
literals (restrict-template-expressions allows numbers)
- no-unused-vars: drop unused imports/destructured names
- no-unnecessary-type-conversion: drop redundant String() where the
field is already string-typed
- no-unsafe-*: type the fetch-json boundary in latestVersion, narrow
unknown before spread/cast instead of trusting any
- no-unnecessary-condition, three honest categories:
* boundary sanitizers (services.timelineOf/headersOf, RawBlocks,
headers.recordOf) now read through Record<string, unknown> instead
of casting first, so the defensive checks are type-meaningful again
* post-sanitize consumers (contextView/headline/assemble/browser)
drop the fallback-empty-array on collections the sanitizer
guarantees
* array-index-undefined gaps (fold turn attach, headers dedupe) use
.at() so the past-the-end guard is real to the type system
- no-non-null-assertion: stackedBar reserve carries the checked max
- no-floating-promises: void the fire-and-forget version check
- intentional-contract suppressions with rationale: react.ts keeps its
injected require (closure-factory loader contract)
- type-honest hardening kept deliberately: MessageSource.sections
entries stay nullable (foreign plugins author snapshot sources) and
isInjection still rejects a null source — foreign message payloads
must never crash the fold
Cross-version audit (0.1.0-rc.7 vs 0.1.1-rc.2, message parsing focus):
- core/session types.ts: only additive optional interrupted on
assistant/message (unread by us); every event payload we fold
(request/header, request/context, tool/call, tool/result,
user/message, assistant/message, compaction/*) is byte-identical
- llm types.ts / message.ts / content.ts block shapes: identical
- token-meter estimate.ts: identical, and official has NO null-entry
guards on content blocks — our removed guards match official trust,
our retained fallback-empty-string / typeof reads stay a leniency
superset
- deriveEventMessage (core/session surface.ts): identical
- ui-conversation chat-nodes contract: unchanged; our client reads are
minimally-typed unknowns by design
- tool/call callId is a branded string in both versions, so the
narrowed typeof guard drops only an impossible coercion