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.
#1381 landed the bar's horizontal position; this branch decides when it is
drawn. The e2e keeps both scenarios and the golden carries both palettes'
pointer-state readings alongside the new edge-offset lines.
Review findings from the first round:
- ui-settings renders its full-viewport panel as a fixed-position DESCENDANT
of the sidebar column, so `pointerleave` never fires when the pointer moves
onto it and the bars stayed drawn after it closed. Leaving is now decided
against the column's box from a document-level pointermove that exists only
while the bars are drawn; the element's own leave stays for the pointer that
leaves the window.
- The rebind gate judges the rule rather than each declaration, so a pair that
mixes `transparent` with an l2 hover no longer passes, and the elevation
half compares whole values instead of token shape.
- Hiding no longer exempts a sheet from the elevated-surface rebind check.
- The e2e polls the reveal before reading a colour for the golden, and pins
that a pointerless scroll draws no thumb.
The session list overflows after a handful of sessions, and its scrollbar
was drawn permanently in a column that is at rest most of the time.
SidebarRoot now tracks the pointer over the whole column and rebinds
ui-theme's scrollbar indirection pair to `transparent` while it is
outside, keeping the thumb for 2s after the pointer leaves so it does not
blink out on the way past. Rebinding colour leaves the list's
`scrollbar-gutter: stable` reservation in force, so revealing the bar
moves no row.
ui-theme's gate now states the widened contract: a rebind targets an -l2
token pair or `transparent`, and nothing else.
Node >= 21 exposes a global `navigator` reporting the machine's own language,
so gating detection on `navigator` let a non-browser boot of the client tree
resolve to `en` instead of the documented fallback; `window` is the browser
test. `navigator.languages` is spec-required but absent on some embedders and
older WebViews, where spreading it would throw at boot, so the walk tolerates
its absence and `navigator.language` covers that host.
The per-spec pin boilerplate collapses into one suite-level
`usePinnedBrowserLanguages('zh-CN')`, which owns the rationale in
dsh-client-test-runtime, and the English-browser e2e scenario now clears the
console warnings channel too — its page has no closing inventory spec.
A first visit resolved to Chinese regardless of the browser: LocaleService
read `dsh.locale` and fell straight back to `zh` when nothing was stored,
ignoring the languages the browser already states it reads.
The initial locale now resolves through three ordered sources — the persisted
preference, then `navigator` (first entry of the ordered language list whose
primary subtag names a shipped locale, so `zh-Hans-CN` -> zh and `en-GB` ->
en), then `FALLBACK_LOCALE`. An explicit choice still wins and nothing writes
the detected locale back to storage, so "has the user chosen?" stays a
question only the stored value answers.
Specs asserting the shipped Chinese copy now state the browser they assume:
the web e2e scenarios open their page with `locale: ZH_BROWSER_LOCALE`, and
package specs pin it through the new `pinBrowserLanguages` test helper.
`settings-chrome.e2e.ts` gains an English-browser scenario as the
assembled-app proof.
Each package ships its zh/en dictionaries as satisfies-typed pairs (zh is
the key-set source of truth; en is checked complete against it), merges its
namespace into LocaleNamespaceMap, and declares locale: NS at register —
components read the framework-injected typed t seat instead of a
hand-carried inject member. Overlapping verbatim words (retry, submit,
submitting) drop out of package dictionaries in favor of the shared common
vocabulary; the question composer stores validation feedback as dictionary
keys so shown feedback follows a locale switch.
Registrations declare a dictionary namespace (locale: NS) and the renderer
synthesizes a typed t prop for the entry's component from the installed
LocaleFace; the seat binding is re-derived per locale revision, so a language
switch hands out fresh t references and memoized consumers re-render through
ordinary shallow comparison. LocaleNamespaceMap is the declare-merge table
(namespace -> dictionary key union); TranslateNS<'ns'> is the
namespace-addressed translate type (namespace keys plus the shared common
vocabulary), carried by the t seat and by the locale service's typed bind.
LocaleService implements the face (lookup ns -> common -> zh -> key,
revision-carrying snapshots with subscriber isolation) and installs it
through the boot-once slots.installLocale seam, mirroring the renderer
install. The typed register(ns, {zh, en}) overload checks each dictionary
against the namespace's key union and requires every shipped locale, so a
missing or extra key and an unbalanced translation are compile errors.
Dictionary registration bumps the face revision without emitting
locale/change — the event now means exactly 'the active locale switched',
so registration-heavy boot cannot storm event listeners.
First consumer of the runtime's single-slot mounting: the real apply mounts
on its own fiber, renderSlot('sidebar', ...) captures exactly the sidebar
slot's output, and update() re-renders the collapsed rail in place. The
.snap files carry semantic class names and svg fingerprints only.
- The hero workspace chip is a selector: no-live-selection states (cold
start, workspace deleted from the sidebar after the list is ready) now
render a "Choose workspace" placeholder (closed-folder icon) instead of
resurrecting the deleted folder name via the session cwd; the cwd-derived
name still bridges the initial list load. Stale pending picks clear when
their workspace leaves a ready list.
- The expanded sidebar wordmark starts a new session (visuals unchanged,
pointer cursor only); the collapsed rail logo keeps its expand toggle.
- The centered hero composer stack gains a 32px foot for visual balance.
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.
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).
ui-sidebar shell landed on master (#643: geometry-only shell, the
browsing region moved to the sidebar.workspaces slot); this branch's
sidebar.settings foot seat re-applies on top — the hardcoded foot row
becomes the seat, the seat rides the railIn crossfade, and the shell
spec's renderSlot stub key-splits region vs settings.
Sidebar session list grows the figma 239-10458 feature set and the
workspace/session browsing region moves wholesale into ui-workspace:
- Group-by menu (WorkSpace / In one list): flat mode lists every session
top-level, strictly newest-first; the choice persists across reloads.
- Session rows get a 500ms hover detail card (title / relative time /
status line) and a ... menu (Rename / Fork session / Delete session,
visual-only for now); workspace headers get ... with Rename (wired) and
Delete workspace (visual-only).
- workspace.rename RPC: trims, rejects duplicate titles on the create
chain (workspace-name-conflict), no-op on same title; modal dialog with
client-side duplicate pre-check.
- workspace.insertSessionBefore RPC (DOM-insertBefore semantics, omitted
anchor appends): HTML5 drag reorder of root sessions inside a workspace
group; order truth stays host-side, the view refreshes from the
response/changed frame.
- Activity pinning removed: the session/event touchSession chain is gone;
workspace accounts are manually owned (new sessions prepend, explicit
reordering only). Contracts and tests updated, api catalog regenerated.
- ui-sidebar reduced to the column shell (brand, fold state machine, New
Session, Settings) exposing one sidebar.workspaces hole with a two-fact
owner share {wide, expandSidebar}; ui-workspace owns the whole region
(header, search, grouped/flat lists, dialogs, drag) plus the picker via
a shared WorkspaceCreateFlow. The old sidebar.workspace picker slot and
its deferral indirection are gone.
- ui-primitives: Menu gains label entries, danger rows, and
closeOnPointerLeave; new HoverCard (portaled, open-delay, disabled
guard). Hover card and row menu never coexist.
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.