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
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.