Review follow-ups: the three new notify loops (currentProvideInfo
subscribers, ui-skill lexicon listeners, late-registration controller
setup) now contain per-callback failures so one faulty consumer cannot
starve the rest, abort the list projection pass, or poison the source
roster with no disposer; controller lexicon polling drops a throwing
source with a console record like the candidate path. The ui-slash
README (both languages) now states the late-registration warm and the
subscribeLexicon contract, and the scenario suite drives a typed /name
token gaining its decoration when the roll settles with no further
input.
provideInfo(id)/maybeProvideInfo(id) lost their last external caller
when the renderer host switched to the currentProvideInfo observable;
both become private (tests assert through the public projection). The
reprojection method's name now says what it does — re-derive and
publish on change — and matches the field family it maintains.
A provider roster change under a stable current id rematerialized every
scope's bundle but nothing notified React: SessionProvider resolved the
bundle from a current-id subscription only, so mounted entries kept the
obsolete hook/prop schema until an unrelated re-render. The sessions
service now owns an atomic currentProvide observable fed by both current
writes and roster changes; the renderer host exposes it as
sessions.provide, replacing the current/provideInfo/maybeProvideInfo
trio, and both providers subscribe to it.
An omitted `todos` on a tail request was treated as "no projection carried"
and preserved the prior value. Every installWindow caller is a tail request
(doOpen, its gap re-pull, repairGap; loadOlder prepends without it), which the
host answers with the full-log projection or omits only when the log holds no
todo/write — so the field's absence is the authoritative empty list. A live
write whose host crashed before persisting therefore left the rolled-back plan
on screen indefinitely; the assignment now clears it on the next open or
resync. Widened the parameter to an explicit `| undefined` so the two meanings
cannot be conflated again, and updated the JSDoc at both declaring seams plus
the bilingual README/note pair.
The session/queued frame no longer carries steering — AgentMessage no longer
has the field. The client derives it from the same ordered turn boundaries the
host saw (a frame arriving while a turn is open joined the steering FIFO).
- 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.
- Mount WorkspacesService.startInitialSelection in the runtime apply (the
one-shot baseline follower shipped in 98633b5aa without a caller): a
restored current session wins, an explicit clear stays cleared, a failed
connect retries on the next baseline projection.
- Cover the policy in client-apply and the assembled workspace-flow
snapshot; startup now lands in the recent Workspace's blank session, so
the draft-carry scenario starts from the hero directly.
- Bring docs along: startup-selection paragraphs in the session-scope RFC
note (both languages), bilingual README pairs for the four new client
packages, doc-graph regeneration with client-declared events exempt from
the dispatcher requirement (client dispatch sites are structurally
invisible to the host-side ts.Program), and pairing re-records.
- 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.
eslint --fix autofixes plus manual repairs: max-len line splits
(fake-api handlers, notifier/slots JSDoc, spec signatures), charAt over
non-null-asserted indexing in slash detect/menu cores, Array.from for
code-point capping, typeof assertions for unbound-method in specs,
generic getByRole for the send-button cast, effect disposer void-wrap in
command register, and dropped unused type imports.
Responding to ds-review-bot round 2 on #653: the tool/code-dispatch event
is appended at settlement, so using its time as callTime fabricated a
zero-duration call for duration-aware consumers — it is now null (start
unknown) per the ToolResultNode contract, pinned in the session spec. The
README's codeDispatches section described the PR3 running→settled
lifecycle a stack ahead of this tree; it now documents the settled-only
index this PR ships (the running shape lands with the start event in
#658, which already merges cleanly over this).