- docs: fix cross-class note links (../feature/...), mirror the membership
reuse criterion in README.zh.md and the connectWorkspace architecture
note (EN/ZH/sidecars re-recorded)
- test(client): stray + member blank at the same path — the scan skips the
stray and reuses the member
- test(web): re-registration after delete now mints a fresh blank session
(the old cwd-only reuse is gone), so the account is no longer empty;
assert the retained seeded session is still not re-adopted
Both editors live in `ui-models` and list the same thing, so they now share
one row shape rather than diverging when #1368 lands: a bordered entry per
model, id and display name on the row, and the capacities behind the row's
own disclosure. The context window is joined there by the per-model output
cap the adapter just gained; both read a decimal K/M suffix.
The shared class names carry this file's token spellings, not that branch's.
`--dsw-alias-border-subtle`, `--dsw-alias-text-tertiary`, and
`--dsw-alias-text-primary` are undeclared, so they resolve to the light-mode
literals in their fallback slots — the defect this section was moved off. A
styles test now rejects any `--dsw-*` name the token sheet does not declare,
so the next editor to name one fails instead of shipping a light-only
surface.
The keystroke buffer is now per capacity field rather than per row, since a
row holds two of them.
The hover affordance framed only the strip right of the crumbs. The bar itself
now carries the outline and the padding in both modes, so hovering previews
exactly the field the click produces and nothing resizes when the two swap.
One keystroke moved the view twice: deleting a separator first narrowed the
pane the draft had just walked away from, then replaced it with its landing.
The tail now filters only the LAST pane — the one whose level the path names —
so a pane on its way out holds still until its landing arrives.
Also from the review round: the walk waits both legs out instead of taking the
submitted-navigation bound (a speculative scan has nothing waiting on it, and
a tail keystroke aborting a slow parent leg would otherwise strand the
two-pane view); a level keeps answering the directory text that produced it,
so `..` segments and Windows forward slashes filter and stop rescanning; the
release-on-miss rule counts displayable rows, so it survives `hidden` ever
meaning more than dot-prefixed; and the editor's 250ms rest joins the other
two constants on the remote-recalibration list.
Skipping the draft-following scan whenever ANY pane happened to list the
directory was the cheaper rule and the wrong one: erasing a segment left the
level being typed on the LEFT, with its own child pane still standing to its
right, so the two panes stopped reading as "where I am, and where I came
from".
The pane arity is now the invariant the editor maintains — the last pane lists
the level the path names, its parent sits beside it, and only a display root
lists alone. Only that last pane's own tail costs no scan; every other
directory part re-lands.
The draft-following scan replaced the panes with one wide level, so typing a
path collapsed the dialog's Miller view — the thing the dialog is. It now
lands through the same selection-anchored landing every navigation uses:
target and parent legs as one frame, the target re-selected in its parent
level, its children on the right. Typing a path moves the Miller view exactly
as a crumb jump does.
One landing shape, two callers: `land(path, {closeEditor, announce})` is what
`navigate` and the draft-following scan share. A submitted path closes the
editor and announces failures; the speculative scan keeps both to itself and
re-parks the focus its swap dropped.
A level a pane already lists still needs no scan at all — the filter alone
answers the draft — so erasing back into the parent's own path keeps both
panes and only moves the filter.
ds-review-bot round one. Keying the debounce on the directory part the draft
named left two states with no recovery until the operator crossed a separator:
a keystroke that superseded an in-flight scan never re-armed one, and an edit
after a rejected submission released the hold with no timer left to release.
The wait is now keyed on the draft itself and decides its target when it
fires, reading the panes through a ref so a landing cannot re-arm it (a host
answering with a differently spelled path would otherwise scan forever).
A landed scan that unmounts the row a keyboard operator Tabbed onto re-parks
focus on the still-open editor; the Modal has no focus trap. That a walked-to
level survives closing the editor is now stated in the README, the Agent Note,
and the module contract. The new e2e stages its own beta directory so running
it alone sees the tree its assertions describe.
The Select Workspace Directory dialog hid its one route into typing a path
behind an invisible click target, and once the editor opened the panes stayed
on whatever level was listed when it opened — so the typed text and the list
under it disagreed for the whole edit.
The edit zone now carries a pencil glyph at the bar's right edge and lights in
the editor's own footprint on hover/focus (the bar keeps one height across the
swap). While editing, the panes follow the draft: a directory part no pane
lists is scanned after a 250ms rest and lands in place, so typing deeper
descends and erasing segments steps back up without leaving the editor, and a
final segment nobody matches releases the prefix filter instead of emptying
the pane it is being spelled into. The draft-following scan is speculative and
silent on failure; Enter still owns the view from submission until landing and
remains the only path that surfaces an error.
Agent setup may await while a mutable contribution registry changes. The previous subagent path validated and committed its provisioning batch inside the setup callback. A revocation queued after that callback returned therefore treated the installation as resident and released it, even though AgentLoop had not published the child yet. AgentLoop could then admit and announce a child whose required capability had already disappeared.
Introduce AgentSetupCommit as the optional synchronous result of create and resume setup. AgentLoop now awaits setup, invokes that commit with no intervening asynchronous boundary, and only then enters the Session and Agent registries. A commit failure follows the existing private-transaction rollback, so neither identity is published and the caller can reuse the id.
Keep continuable-subagent installations provisional until this publication commit. Contribution removal still releases every installation immediately, but now marks an unpublished batch invalid so its commit rejects with ACTIVATION_SETUP_REVOKED. Once the commit succeeds, later removal remains ordinary live revocation.
Cover create and resume ordering, resume commit rejection and identity reuse, and an assembled microtask revocation that leaves only the parent Agent and Session. Update the public JSDoc, architecture flow, package contracts, current Agent Notes, Chinese counterparts, pairing records, and generated Cordis API to describe the new boundary.
Validated with the four focused Agent/subagent test files (91 tests), the isolated assembled regression, targeted TypeScript project builds, generated Cordis API freshness, export JSDoc verification, scoped translation pairing, Markdown wrapping, and Mermaid parsing.
ds-review-bot round 1 on the repository-plugin runtime:
- a manifest-declared skill root absent or non-directory in the installed
package now fails the plugin load (skill-local treats a missing root as
legitimately empty, which silently mounted a skill-less plugin)
- includeDefaultRoots: false no longer inherits $DSH_BUNDLED_SKILL_DIR, so
isolated repository providers see only their explicit roots
- prepared wrapper baseUrl schema requires the file: scheme, failing hostile
URLs at the declared validation boundary
- preparedPath reuses format.ts's isOutside; SERVER_NAME_PATTERN is exported
and pinned equal to dsh-mcp-client's, with the restatement justified (the
prepare bin keeps a zod-only module graph); the unexplained `as never`
cast now carries its schemastery rationale
- the import-free wrapper assertion also rejects dynamic import(
- the headless fixture wrapper is regenerated by the real prepareDshPlugin
and a drift test pins fixture == generator output
- prepareDshPlugin JSDoc states the non-atomic publish repair contract
- inline the collect() identity wrapper now that both streams use the
seam's diagnostic-tail shape
- resolve the packaged rg path lazily at the first call (memoized):
@vscode/ripgrep resolves its platform package at module evaluation, so a
static import turned a missing/corrupt platform package into a Loader
composition failure instead of the documented per-call SEARCH_FAILED
- classify synchronous spawn-creation throws (a NUL in argv, an abort
racing the pre-check, a rejected resolution) into SEARCH_FAILED /
SEARCH_ABORTED instead of leaking raw errors
- correct the stderrMaxBytes contract: the stderr excerpt is embedded in
SEARCH_* error messages, not hidden from the model
- export virtualManifest and pin its three acceptance paths (prefix hit,
pnpm-11 truncated-name content-scan fallback, both miss) with fixture
unit tests
Tests: rg-path.spec.ts (resolution failure + memoized rejection),
tools.spec.ts spawn-creation classification, notices spec virtualManifest.
Merges master (default-workspace-write-ui, installer-adopt-checkout,
remove-scoped-bash, goal-clear-single-flight, frontend-plugin-loader,
install-interface-choice, …).
Conflict resolutions:
- apps/cli/tests/shipped-composition.e2e.ts: keep the fixed glob/grep
roster assertion; master's workspace-write composition now confines
tool-bash, so the escalation pair is pinned present (my earlier
absence pin is superseded) together with master's permission facts.
- even-out-shipped-tool-rosters note: both sides edited it; the merged
text keeps both sets of changes and the pair is re-recorded.
- delete the singleQuote shell-quoting helper and its bash-spawning tests
(no in-repo consumers; no shell layer exists anymore)
- drop spill from both collect streams: the tool never reads a raw spill
path, and a lossy stdout read is a pure SEARCH_RAW_OUTPUT_OVERFLOW error
- prepend --no-config so a host RIPGREP_CONFIG_PATH cannot inject a --pre
preprocessor into the unconfined spawn
- promote graceMs and stderrMaxBytes to validated Config fields (defaults
SEARCH_GRACE_MS / SEARCH_STDERR_MAX_BYTES) instead of inheriting
bash-local's config
- correct the grep tool's JSDoc seam reference (bash -> subprocess)
- drop the dead exit-127/command-not-found classification branch
glob/grep now run the @vscode/ripgrep binary via ctx.subprocess with a plain
argv vector: no system rg install, no shell layer, unconditional registration.
The load-time command -v rg probe and the bash-seam coupling are removed;
timeouts ride the cooperative exec.signal plus the seam's terminate
escalation. The fs-glob-sampling ACP snapshot executes the real packaged
binary against an mtime-pinned fixture. Adds the packaged-ripgrep-search
Agent Note, updates the roster-note facts and both shipped-composition e2es,
and regenerates the doc catalogs and third-party notices (surfacing
pre-existing manifest drift plus the new @vscode/ripgrep row; the notices
generator also learns pnpm 11's truncated virtual-store names).