The Web picker collapsed onto the directory flow (see the one-route-to-add-a-workspace
Agent Note), leaving workspace.create({ name }) with no product consumer. Delete the
whole feed line: the wire schema's name member and WorkspaceApi spelling, the gateway's
workspaceRoot config/default and the mkdir branch, the client seam that carried the name
(WorkspaceCreateInput, WorkspacesService.create, intentName), the dsh web
--workspace-root flag, and the fixture's name handling. workspace-name-conflict stays as
workspace.rename's duplicate-title error.
A row's stored profile could not tell a hand-declared gateway from a
shipped provider whose models someone narrowed — both look identical from
outside the adapter — so the Models page had no way to mark the routes a
deployment added itself.
The directory entry now carries `declared`, answered by the owning adapter
against its own installed catalog, and the page renders a Custom tag from
it. Absence stays "this adapter draws no such distinction" rather than
"shipped", so a route no adapter claims is labelled neither way.
Also records the default-route work's Agent Note and the e2e evidence for
all three changes: the composer switch writing the section, and the Models
page declaring a route with its own reasoning effort.
- index.ts: RESERVED_ERROR_MEMBERS JSDoc now says `__x__` (non-empty
middle), matching DUNDER_MEMBER and the derivative docs; and
PORTABLE_RESERVED_WORDS is described as covering portable *target*
languages (Python is a later-PR backend, not yet shipped).
- types.ts + type-equiv docs: CodeBindingNamespace.global points to
RESERVED_BINDING_GLOBALS by name with an `e.g.` sample rather than
enumerating the set (single home), no longer implying `__debug__` is a
seeded slot.
- Agent Note: separate `__debug__` (CPython compile-time constant) from
the seeded bootstrap globals.
- README (en/zh): document the four exported exclusion sets and the
portable identifier/naming contract — no cross-package link needed.
- Regenerate cordis services catalog for the shifted source line.
- Agent Note: correct the stale worker-adoption paragraph — the worker
consumes the seam constants directly by name (no RESERVED_WORDS /
RESERVED_ERROR_PROPERTIES re-alias) — and describe DUNDER_MEMBER as
`__x__` (non-empty middle).
- types.ts: document RESERVED_BINDING_GLOBALS on CodeBindingNamespace.global
(names like `__dsh_main__` pass the identifier rule but are still refused)
and the non-empty-middle dunder rule on memberNameProperty; propagate to
the type-equiv derivative docs (both languages, re-recorded).
Update the type-equiv blocks in docs/core-data-structures/code-runtime
(both languages, re-recorded) to mirror the new CodeBindingNamespace /
CodeBindingErrorClass JSDoc, and regenerate the cordis services catalog
for the shifted source line. Keeps the worker-only intro until the
Python backend package exists.
`ui-question`'s node half called `ctx.tools.register` on the host context.
`ScopedLayers.merge()` combines the global layer with the agent's exact-scope
layer, and an unscoped registration lands in the global one — so the tool
reached every agent no matter which preset composed it. `core-web`, sold as a
two-tool benchmark surface, really presented three.
Rendering a question is a host UI capability; having the tool is an agent
capability, and only a preset decides that. The node half is now empty and the
`tool-ask-user` row moved into the preset that wants it. The TUI keeps its own
row, having no presets.
The composition tests now assert the global tool layer is EMPTY, which is the
invariant that would have caught this: any tool outside a preset reaches every
agent. The browser lane's composition, seeded-history, and hermetic-skill
assertions address their registries through a composed agent for the same
reason — those services are per session now, and the host cannot resolve an
`isolate` realm by name.
The shipped host composition moved: `base.cordis.yml` and `web.cordis.yml`
are the dsh-base and dsh-web-app patch layers now. The gate still opened the
old paths and crashed on ENOENT — a gate that cannot read its inputs proves
nothing, loudly or otherwise.
Retargeting it also surfaced what the move implies for ownership: the web
bundle carries the roster and its browser plugin rows now, so the bundle's own
manifest is what must declare them. The gate's existing bare-plugin check said
so as soon as it could parse the file again.