`pnpm test` is the vitest run only since the local pre-commit flow took typecheck over, so neither PR checks nor release builds type-checked anything, and the pr-checks comment still claimed the single test run covered it. Both workflows now run `pnpm run typecheck` beside lint, and the comment says what the vitest run actually covers.
The matrix skipped itself on every PR: pr-checks.yml never fetched the
baseline tags and never set DSH_REPO, so skipReasons() always reported a
missing checkout and describe.skipIf dropped the whole per-baseline block —
the registry apply, the wire keys, the checkpoint JSON gate, cold restore
and every client seam probe. Only the release workflow fetched them, so
the compatibility proof compatibility.md claims for "every pnpm test" was
a no-op until a tag was cut.
Both workflows now fetch the tags and export DSH_REPO, and a skip under CI
fails the run instead of hiding behind green: a missing prerequisite there
is a broken gate, not an absent one. The two test runs collapse into one,
which also brings the lint gate the release workflow had and PRs did not.
The matrix's own vocabulary guard compared a hand-copied constant against
hand-copied baseline lists, so it could never see a drift in the fold — and
had drifted: approval/asked, approval/decided and session/end-seed are
folded but declared by no baseline. It now reads the fold's case labels out
of src/host/fold.ts, so a new fold case cannot go unprobed, and the history
probe pins the `records` envelope historyPage's rowsOf rejects without, not
just the remote's service name.
The supported set is now the two V4-generation lines (0.1.7-rc.2+ and
0.2.0-rc.2+): the manifest's dshReleases, the peer floors (>=0.1.7-rc.2,
cordis ^4.0.4 — both lines vendor 4.0.4), the devDependency pins, the
baseline matrix, the release workflow's tag fetch, and the compatibility
docs.
With every supported line on session format V4, the V3-only paths leave:
the fold reads the tool result's first-class message alone (source.callId
pairing, message.isError — the admission validation refuses any row whose
data.error outlives it); the settings.register host face module is gone
(the Config-form entry schema serves the preferences); the client drops
the settingsScope/settings.plugin.item card seat, the retired sessions.open
jump fallback, the legacy icon spellings, and the settings-dialog leg of
the settings jump; the history page's tool/result mapper reads the V4
message (its V3 wrapper reads were dropping result content on supported
lines); the cache probe rides the 2-arg cachedSnapshot face. The baseline
gate stays: a below-floor harness composes the fallback units and the
upgrade prompt, and an undetectable version still fails open.
Verified: pnpm run lint:fix && pnpm run test && pnpm run build green (1918
tests, 100% per-file coverage); the compat matrix passes against the real
dsh sources at both baseline tags; live on dsh 0.2.0-rc.2 the plugin
composes, the Plugins-page preference card renders and round-trips a
write, the insights warm-up fills the heatmap, and the session jump and
Context tab work end to end.
Fires on issue creation; currently the only blocked pattern is
'dsh-plugin.org' (case-insensitive). Matching issues are closed as
not planned with an explanatory comment. Add further conditions by
appending to the patterns array.
The setup step claimed to read the pnpm version from `packageManager` in
package.json, but this plugin package defines no such field, so
pnpm/action-setup aborted with "No pnpm version is specified" eight seconds
into every external PR's run (#102, #103, #107) — no PR check ever reached
the test step. Pin the version explicitly, mirroring release.yml.
Add dsh-v0.2.0-rc.2 to the supported set alongside the 0.1.5-rc.1 and
0.1.7-rc.2 lines: the manifest's dshReleases, the baseline matrix, the
release workflow's tag fetch, and the compatibility docs.
Every seam the plugin rides is spelled as at 0.1.7-rc.2 - the projection
registry contract, the platform module table, the right Sidebar seats and
guide-entry contract, the session-jump verb pair, the detail channel RPC
faces, the Plugins page card slot over the configForms transport, the
balance settings faces, the step-boundary guard, and the durable event
vocabulary (still the V4 log generation, no v4-to-v5 migration). Cordis
stays 4.0.4 and the dsh peers stay >=0.1.5-rc.1.
Verified on the new tag: all 25 compat-matrix probes pass against the real
harness sources and the vendored cordis, `pnpm run lint:fix && pnpm run
test && pnpm run build` is green (1837 tests, 100% per-file coverage), and
on a disposable DSH_HOME the 0.2.0-rc.2 CLI installs dsh-context@0.60.0
into exactly one composed config row and removes it back to zero.
Also refresh the docs' verification stamp (plugin 0.58.0 -> 0.60.0).
The supported range is now two baselines - dsh-v0.1.5-rc.1 (session
format V3) and dsh-v0.1.7-rc.2 (V4) - enforced by the runtime gate at
BASELINE_DSH_VERSION = 0.1.5-rc.1; below it the fallback units and the
upgrade modal take over (0.1.2/0.1.3 hosts were previously served by the
real folds).
The pre-V3 log shapes are dead code on every supported harness: the
running harness migrates historical logs on every read path (or refuses
the session wholesale), and 0.1.5-rc.1 rejects a request/header carrying
header.system outright. The fold therefore drops the V0/V2-only paths -
the header.system envelope system prompt, assistant/chunk floods,
{start, end} replacement endpoints, tool/code-dispatch - and the state
fields only they wrote (systemsFromHeader, the stepStart decode
accumulators). stateVersion stays 20: no supported log folds
differently, and pre-V3-era cached rows are discarded by the projection
cache's identity gate (and the strict stepStart sub-schema) instead of
served. The headers unit folds metadata-only epochs (envelope
systemTokens write dropped; the v1 cached-row read-compat and its pinned
stateVersion 1 stay); replaceRangeOf speaks startSeq/endSeq only; the
browser's epoch fetch maps request/header to tools and system/message to
the prompt text.
tests/baselines.ts re-fits to the two entries (the right Sidebar seam is
required on both), the compat matrix drives both tags' REAL sources
(canonical logs re-shaped to V3, the hostile probe renumbered past them),
and the version fixtures pin the new floor. Manifest: dshReleases
{0.1.5-rc.1, 0.1.7-rc.2}, dsh peers >=0.1.5-rc.1 (satisfies the 0.1.7
admission check under includePrerelease), devDeps pinned to the
0.1.5-rc.1 surface plus the ui-primitives runtime closure the client
lane inlines (test-only), and the release workflow fetches the two tags.
docs/compatibility.md is rewritten around the two-line matrix; README,
AGENTS.md and the issue template follow.
Also riding this move, from the audit pass: the fold re-proves an inject
source's form before it books the event record (the harness validates
`kind` but not `form` - a hostile one previously failed the strict
wire/state schemas on every delivery, the #44 freeze class), the headers
unit guards a data-less envelope (it has no try/catch of its own), the
schemastery schema annotations drop the explicit z<T> wrappers the 3.18.4
Mode generics reject, and the version-history note records why
stateVersion is deliberately not bumped.
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).
- Use English-first bilingual labels across every field.
- Require reproduction steps and split expected vs actual behavior.
- Add environment and affected-surface fields; make screenshots optional.
- Auto-apply the bug label and the [Bug] title prefix.
- Refresh the version placeholder and note the supported dsh range.
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