fix(host,client): gate the picker affordance on the advertised kind; reject non-absolute browse paths

ds-review-bot round 2. The workspace UI never consulted the advertised
directoryPicker kind: under a browse (or merge-added) backend it still
rendered 'Open local folder…' and called pickDirectory(), which the host
answers with directory-picker-unavailable. The create flow now reads
directoryPickerKind() per menu open and renders the dialog affordance only
under 'dialog' — browse (until its in-app browser UI lands) and unknown
kinds hide the entry, realizing the seam's documented default; a keyless
workspace-flow snapshot pins the hidden entry over the browse fixture.

The browse backend also resolved wire paths, silently rebasing '' or
relative parents under the host process cwd; both primitives now reject
non-absolute explicit paths with their business codes, and the seam JSDoc
carries the contract.
This commit is contained in:
creatixchu
2026-07-28 17:39:15 +08:00
parent 6e299cd55a
commit cd7aa3c7d8
20 changed files with 146 additions and 37 deletions

View File

@@ -174,6 +174,20 @@ it('locks the composer in the New Session view state until a Workspace is chosen
`)
})
it('hides the Open-local-folder entry under the fixture host\'s browse picker capability', async () => {
boot('?fixture=empty')
await findLockedComposer()
fireEvent.click(workspaceChip())
const menu = await screen.findByRole('menu')
// Flush the advertised-kind read (fixture describe resolves in microtasks):
// the fixture serves `browse`, whose in-app UI is not wired yet, so the
// dialog affordance must not render — only the create action remains.
await act(async () => {})
expect(within(menu).getAllByRole('menuitem').map(item => visibleText(item)))
.toEqual(['Create a new workspace'])
})
it('selects the recent Workspace and opens its blank Session on first load', async () => {
boot('?fixture')