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