1504 hand-written ranges pointing at workspace members become workspace:^, so
pnpm pack substitutes each member's real version at publication: sibling
peerDependencies follow the family version instead of being pinned at ^0.0.1,
and a reference to a vendored package follows that package's own line. Without
this, publishing 0.0.2 ships peer ranges naming a version that does not exist,
and 0.0.1-rc.1 does not satisfy ^0.0.1 either.
It also retires ranges that had gone stale against the workspace: ^4.0.0-rc.6
for a 4.0.0-rc.7 checkout, ^3.17.0 for schemastery 3.18.0.
workspace:* stays where an exact published version is the point, which is how
the Landlock entry pins its platform packages.
A workspace constraint now requires the protocol, so a new package cannot
reintroduce a hand-written range. The same constraint caught packages/boot/cmdline
arriving on master without the publishable trio, which this change completes.
Every package under packages/, apps/, and vendor/ drops "private": true and
declares publishConfig.access "restricted": the repository now states which
packages it publishes instead of deciding it at publish time. Each one also
declares its repository and directory, which is how a consumer of a private
package reaches its source.
The Landlock packages move to restricted with them. They have never been
published, so nothing anonymous depends on them today, and the whole
@deepseek-ai scope stays private.
The workspace constraint that required every package to be private now applies
to non-members only, and asserts the publishable trio on each release member.
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.
Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.
The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.
Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
Delete design-session citations (decision/audit/plan ordinals, stack
positions), change narration, review choreography, and reviewer-addressed
justification from comments, JSDoc, docs, READMEs, Agent Notes, tests, and
generator templates; restate every affected fact as current-state contract
prose. Fix generated docs at their sources and regenerate the catalogs and
cordis-surface regions; re-paste type-equiv blocks; update every bilingual
counterpart and re-record the pairs. Record the citation rule in the
committed-artifact-citations Agent Note.
Public snapshot state stays in the store engine's plain-data vocabulary
(immer drafts reject Sets without the MapSet plugin, which stays off):
manager/service/contract carry readonly SessionId[] in Host order, and
the tree derivations build their own transient Set — the
expandedProjects pattern. Membership-unchanged installs still keep the
array reference for Object.is short-circuits.
WorkspaceListState gains archivedSessionIds (ReadonlySet, replaced only
on membership change), installed as full snapshots from the list
baseline, the unary echo, and the changed frame. Archiving the current
session clears the selection into the New Session view state. Test
doubles (test-runtime, fake APIs, fixture client) follow the widened
IWorkspaces/IApiClient faces.
Feature packages now reach these domains through interface types only:
ISession/SessionFace (identity + prompt/cancel/loadOlder + the useSession
snapshot source), ISessions, IWorkspaces, ILayout, IConversation, and the
existing SlashServiceContract now actually mounted on Context.slash. The
concrete services implement their face; wire-pump and assembly entry
points stay on the classes. The provide-channel materialization and
current-projection logic moves into SessionProvideChannel so the
production service and the client test runtime share one implementation.
The workspaces service consumes sessions through the narrow SessionsPort.
Hand fixes for the findings --fix cannot touch, mirroring the fixes
already applied on the fe-docs feature branch (same file, same shape)
so its eventual rebase resolves cleanly:
- restore the return the no-confusing-void-expression autofix ate in
useAbsentSnapshot (typed S | undefined; hook call kept for hook-order
stability, undefined returned explicitly);
- re-type DOM queries the no-unnecessary-type-assertion autofix broke:
getByRole<HTMLButtonElement>(...) generics instead of the removed
as-casts (the eslint program and the client tsconfig aggregate
disagree about these casts; the generic form satisfies both);
- justified eslint-disable for the deliberate legacy paths: keyCode 229
IME-composition detection, execCommand clipboard fallbacks, lib.dom
clipboard optionality, and the any-typed Reflect.get/this probes in
test fakes;
- drop the dead react/no-danger directive (eslint-plugin-react is not
loaded, so the rule never applied) keeping its shiki rationale;
- delete the tautological 'Z' comparison and the renameTarget null
check already implied by renameBlocked;
- css-module non-null assertions replaced by type widening
(Button className, TAG_CLASS Record) per the established pattern;
- misc: max-len comment wraps, void generic drop in the deferred test
helper, unused type imports, floating selectWorkspace promises voided,
member-delimiter newlines in inline type literals.
Mechanical --fix output over the newly linted .tsx files (indent,
arrow-parens, comma-dangle, member-delimiter-style, unnecessary type
assertions), plus the three generic-arrow test hooks converted to
function declarations up front: the comma-dangle fixer strips the
<T,> disambiguation comma and turns them into parse errors otherwise.
- Extract the shared New Session action into WorkspacesService.startSession
(sidebar button and workspace browser both delegate; recent-Workspace
targeting and the no-workspace clear live in one place).
- Fold the chip-insertion transaction shared by insert-ref and paste-upgrade
into one InputMachine helper.
- Share the fixture's session-not-found guard across the sessionId-addressed
catalog routes.
- Drop the AppFrame baselines-ready loading gate (user ruling: the bare
status line reads worse than the shell's own pending rendering); both
column occupants mount from first paint.
- Keep ConversationSession mounted for blank sessions (chrome-less) so the
draft-persistence mirror stays bound in the hero; hero typing reaches the
chat store again.
- Restore the baselines-ready gate in AppFrame: empty boot snapshots no
longer flash the New Workspace hero before either baseline lands.
- Commit ordinary sends through the machine (send-committed event +
Shell.commitSend): undo can no longer resurrect already-sent content on
the default-sink path.
- Give the production InputMachine a real wall clock so the typing-run
merge window actually expires.
- Coalesce concurrent connectWorkspace creates per workspace: the summary
has no cwd until the host frame lands, so a second New Session inside
that window minted a duplicate hidden blank session.
Test-side catch-up with the session-maybe conversation architecture: the
provide channel's descriptor shape and maybeProvideInfo in fakes, the shared
chat-store handle asserted on conversation.session (the session-maybe shell
carries no store), startSession fakes exposing the workspace list snapshot,
strict session slots declining (not throwing) without a session, AppFrame's
removed empty seat and loading gate, and the hero draft asserted on the
machine (the chat-store mirror binds with ConversationSession). Plus three
lint fixes (max-len split, boolean-compare, arrow-parens/unbound-method).
The ui-layout apply bench provides a real LocaleService before the
theme plugin boots (ui-theme now injects slots/locale to register its
Appearance row); drop locale's unused clsx dependency.
Replace optional chains on always-present globals with the repo's
typeof guards (store.ts precedent), drop the non-null assertion by
failing loud on an impossible registry miss, and fix two arrow-parens
slips; cover the no-localStorage boot path in both service suites.
Add the browser Settings surface as slot-composed plugins over new
preference services:
- Rename dsh-client-i18n to dsh-client-locale (locale is the domain
name); LocaleService adds getLocale()/setLocale(id), immutable
snapshots, a locale/change event, and dsh.locale persistence.
- ThemeService owns the light/dark/system preference (default system),
resolves system via prefers-color-scheme, publishes theme/change
snapshots, persists dsh.theme, and no longer touches the DOM;
ui-layout's ThemePresenter applies resolved snapshots
(body[data-ds-dark-theme] + alias tokens) and cleans up on dispose.
- ui-sidebar drops the phase-1 settings dropdown/modal; the foot renders
the new sidebar.settings slot with the column state.
- New ui-settings shell occupies sidebar.settings: foot trigger row and
the centered 1080x700 panel (figma 501:29947) with 24% mask, close
button / mask click / Escape all closing, and a 188px nav projected
from the settings.section list slot it declares. Nav labels are
registrant-localized; sections re-register on locale change, so the
ledger version is the shell's only subscription.
- ui-settings-general registers the General section: Permission and
Tool Call skeletons, live Language (locale menu) and Appearance
(Light/Dark/System cubes following the persisted preference); its
slot store mirrors both service snapshots via apply-side listeners.
- ui-settings-models registers the Models nav entry with an empty
content column.
- Portaled menus pin z-index above modal overlays (a menu anchored
inside the settings dialog rendered underneath it and was
unclickable).
- theme/data/list-pen icons in ui-primitives; settings copy ships as
zh/en dictionaries; fixture manifests gain the settings rows.
Every client plugin package carries dshClient ({platform, inject,
immediately?}) and emits lib/client.js through the shared clientBundle
preset; exports["./client"] points at the bundle. The infrastructure
tier (connection, runtime, ui-theme, i18n, hmr) declares immediately: true
in its manifest — absent means lazy. The bundle purity gate covers all
nine packages: platform modules stay external, INLINE_SAFE wire layers
inline, any other cross-plugin value import is a build error. Migrations
that rule forced: scopeOf became a SessionsService method and
transportError moved into dsh-host-apiproxy's wire layer; the store
engine stays in runtime under a documented temporary exemption
(TODO(webload/store-rehome)).
- The real-browser collapse smoke tracked the old chrome: visible HARNESS
text (the wordmark svg is aria-hidden now), an 'Expand sidebar' label
(renamed 'Open sidebar'), a 300px settle (default is 280), and an
immediate focus assert (rail search defers focus past the slide). The
case now tracks the brand span, polls the deferred focus, and uses the
current labels and width.
- Tooltip treated hover and focus as one trigger: leaving with the mouse
dropped the bubble of a still-focused anchor (and vice versa). The two
triggers are tracked independently; the bubble hides only after both
clear. Spec pins both orders.
- The ui-sidebar README and the bilingual collapse note still described
the retired geometry morph; both now state the slide + crossfade
contract, the fixed-width (never-conceding) sidebar, and the rail's
whale-mark/tooltip chrome.
Fixed sidebar width (never concedes), figma session-row rail (16px twist +
status slots, triangle arrows, mount fade), exact brand wordmark svg with
tooltip'd rail controls, form controls inheriting the app font stack, and
inlined the twist button reset since the tsdown CSS pipeline drops composes.
The collapsed rail becomes a 56px icon column (24px controls between 16px
paddings) carrying expand, new session, search, and new workspace — each
aligned with its expanded counterpart; rail search expands the sidebar and
focuses the search box. Collapse/expand now animates: the frame transitions
grid-template-columns (and the surviving handle its left) on the deepsuite
sider curve — --ds-ease-in-out over --ds-transition-duration-slow, supplied
by ui-theme's base sheet. Transitions pause during drags (data-dragging on
the frame, set for the whole gesture) and under prefers-reduced-motion.
A closed sidebar previously resolved to a zero-width grid track, clipping
the only toggle and the settings entry with no visible recovery; the closed
preference persisted across reloads, locking the sidebar shut.
- columns.ts maps the closed preference (width 0) to a fixed 60px
SIDEBAR_COLLAPSED rail through every step of the concession solve;
closed details still resolve to zero width.
- AppFrame derives data-sidebar-collapsed and the sidebar slot's collapsed
owner prop from the persisted preference instead of the resolved track
width, and drops the resize handle while collapsed.
- SidebarRoot reads the owner collapsed prop; the expanded-only body is a
separate component that unmounts while collapsed (dropping its sessions
subscription), leaving the expand toggle and Settings in the rail.
- The keyless web smoke gains the ui-sidebar bundle (six real bundles) and
pins the 60px rail collapse/expand round through the assembled client.