hasDirectoryFlow was a plain per-render read: a flow plugin unloading (HMR)
while its dialog was open left flowOpen stuck with nobody to cancel,
permanently disabling the workspace actions. Occupancy now rides
useSyncExternalStore over the hole's registration subscription, and an
empty hole withdraws an active flow; the menu entry also reacts to
activation without a reopen (ds-review-bot).
Union resolutions throughout — the fixture serves both the goal and plan
projection units (catalog gains /goal beside /plan; the retired
goal-fixture sample command yields to the real goal mirror), the mux
baseline spec expects all four unit frames, and the tsconfig paths /
Model Experience allowlist carry both domains' outlets.
Shared-surface conflicts resolve as unions: the fixture serves all four
projection keys (title/todos/permissions/goal) with both the /permission
and /goal command mirrors (the goal-fixture placeholder retires with
master), apps/cli carries both lines' dependency additions, and the README
Model Experience allowlist keeps both entries. The connection specs assert
the four-key baseline and the shifted approval/question replay indices;
the module graph regenerates over the merged dependency set.
An HMR replacement left the old instance's pick promise able to adopt a
second path through the shared owner callbacks; settlements now check a
component-lifetime ref (an injected-face identity change alone keeps the
pending settlement — the host-side dialog is unchanged, and the wire has no
per-request abort). Both settlement arms covered (ds-review-bot).
A navigation settling between the Edit-Path click and the first keystroke
executed navigate's draft reset and closed the editor underneath the user;
the click now bumps the request sequence like draft edits do
(ds-review-bot).
The immediate registration attempt runs after the subscription is
installed; a synchronous failure (declared slot already occupied) escaped
the constructor without returning a handle, leaving a subscription owned by
nothing — later ledger flushes would fire it against a failed fiber.
Construction failure now unsubscribes before rethrowing, with a dedicated
deferred.spec covering both arms (ds-review-bot on the flow registrations;
the effect-level rollback in the flow packages keeps covering earlier
successfully-constructed deferrals).
Three ui-conversation spec conflicts resolve to master's SlotTestRuntime
rewrites. Adaptation to the new outward session face: ISession gains the
command verb (the composer chip and the /permission picker submit through
it), FixtureSession grows the matching fail-loud stub plus the
waitingApproval summary default, and the picker reads the projection
through projections.faceOf (the ProjectionsFace shape) instead of the
retired store getter.
The keyless fixture now carries the goal chain end to end, mirroring the
host: /goal enters the command catalog and its execute path brackets a
goal/change create with the command lifecycle pair; the six mutation verbs
CAS-check the projected current goal and append whole-value changes (the
shared append path broadcasts the session event and the goal projection
frame); the tail-page projections block and the mux-open baseline always
carry the goal key (null before create / after clear). Connection specs
follow: the lifecycle round-trip replaces the not-implemented stubs, and
the mux baseline expects the third unit frame.
Also: GoalBar inline errors use ASCII parens around the code (review
feedback on #842).
- The browse client half mirrors -native's rollback: a pair construction
that throws halfway (declared-but-occupied second hole) disposes the
earlier deferral before rethrowing, so no orphaned slot subscription
outlives the failed fiber (ds-review-bot).
- The TestWorkspaces browse methods gain the coverage CI's per-file gate
requires (defaults + stub overrides, recorded like their siblings).
A declared-but-occupied second hole registers synchronously, so the pair
construction can throw after the first deferral installed its subscription;
that orphan then fires against the failed fiber's inactive context. The
effect now disposes already-created deferrals before rethrowing
(ds-review-bot on the browse twin; same shape here).
master's new ctx.workspaces contract face (IWorkspaces + TestWorkspaces
double) predates this stack's listDirectory/createDirectory; widening the
face is the explicit act of letting the browse flow drive them, and the
double records/stubs them like its siblings.
The runtime's new outward session contract exposes projections as a
ProjectionsFace (faceOf only); the ui-goal verb closure reads the current
value through faceOf('goal').getSnapshot() instead of the store-level get.
Catalogs regenerated after the merge.
- HMR/unmount bumps both request generations, so a listing or creation
settling after disposal neither updates dead state nor issues the
post-create relist.
- A selection whose preview listing fails clears back to the single pane:
the breadcrumb names the level, so the level must be the committing
target (reverses the earlier keep-marked behavior with its test).
The TUI keyless smoke exposed the wrong idle signal: /plan submitted while
the driver is between turns still saw status running (checkpointing), so
the selection queued and the immediate copy never appeared. The commit
gate is now the log's own open-turn fold — plan/mode commits at once
whenever no turn is open, and queues only inside one. Specs drive the
distinction through explicit turn/start / turn/end events instead of a
fake status; the keyless smoke pins the new idle copy on both /plan and
/plan off; regenerated cordis/service catalogs ride along.
The sparkle glyph gained a second consumer (GoalBar beside the Others
tool-row variant), which the duplication gate correctly flags as a clone —
the package-local-until-shared rule its comment recorded has run its
course. Both copies collapse into the shared ic_ds_* family; the icon-set
count test grows to 57.
Inherited sandbox and approval events were part of the constructor seed. Session.firstLiveSeq classifies every constructor event as replayed history, so telemetry adoption skipped these child-only creation facts even though no parent or prior process had exported them.
Capture the parent overrides at the same synchronous delegation boundary, but append the events during the child factory setup while the session is still unpublished. They remain ordered after fork history, persist with the first child batch, and retain last-event-wins behavior while landing on the live side of the telemetry boundary. This uses the existing setup and session append contracts instead of adding another seed category or telemetry special case.
Add regression coverage for exporting an unpublished suffix without re-exporting constructor history, assert the spawn and fork firstLiveSeq boundaries, and restore the public seed documentation to replay/fork history only.
A draft edit can invalidate the selection's preview request; Escape then
left selected set with no child and nothing loading — a half-empty
two-pane view. Cancel now clears the selection when no preview exists
(ds-review-bot round 7).
Editing the draft bumps the request sequence, so a slow lookup that settles
afterwards can neither clear the newer text nor repopulate the view with
the older path (ds-review-bot round 6).
The path editor and the folder-name input carried identical inline IME
handlers — the cross-file clone jscpd flagged against WorkspacePicker; one
component-level guard object serves both.
- Open and New folder disable while a path draft is uncommitted: targetPath
still names the previous selection/listing, and committing against it
while a different path shows in the header adopts the wrong directory.
- The Miller columns keep their own row so a status/error line renders below
them inside the card instead of competing as a third flex item the dialog
clips off-screen.
- Both text inputs (path editor, folder name) carry the IME composition
guard the workspace-name inputs already had: a composing Enter confirms
the candidate, never submits.
Two real branches gain assertions (effectivePermissionPreset's backward
scan over non-preset events; the popup options() throw when the projection
vanished between availability and open — a sync throw, not a rejection).
The empty ui-permission node half joins the ui-model entry on the
client-lane coverage debt list (same pure-UI plugin shape, same TODO).
With no listed level (an unreadable or missing home directory), the
path-edit zone previously disabled forever, stranding the operator on the
alert with only Cancel; it now opens with an empty draft so an absolute
path remains the way forward. Covered by a recovery test.
Two overlong lines wrap (fixture permissionSelectOf signature, apply.ts
type-import list) and three test-side unnecessary assertions drop
(eslint --fix).
gen-cordis-catalog/api, config/persistence catalogs, module graph, and doc
graphs pick up the ui-permission package, the permissions projection key,
and the /permission command; KnobState and PermissionSelect join the
type-link exemptions (owned by the permission package's own docs), and
ui-permission joins the sentence Model Experience allowlist (indirect via
the host command). The ui-conversation and dsh-permission READMEs gain
their zh halves for the approval/chip and projection/command paragraphs;
all three touched pairs re-record.
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.