refactor(web): drop the permission RPC pair and turn-anchoring machinery

The session.permissions/setPermission unary pair, the PermissionOption wire
DTO, the client Session wrappers, and the fixture/fake mirrors all leave the
wire: the read side moves to the 'permissions' session projection and the
write side moves to the /permission command in follow-up commits, so the
web protocol gains no permission methods at all.

The pendingSwitches + prompt-submit flush + hasOpenTurn move also goes.
Knob events no longer need turn enclosure: the persistence scanner keeps
standalone events after the last turn/end as part of the preserved prefix
(remove-synthetic-log-only-turns), none of the three knob invariants demand
an open turn, and the setters append bare events. An idle switch commits
immediately; hasOpenTurn stays a user-approval private fold (its audit pair
is the one contract that still requires enclosure).

The old PermissionSelect chip and its mount-time fetch die with the RPCs
(the resident composer broke the mount-once assumption); the projection-fed
replacement lands with the Access seat swap.
This commit is contained in:
imccyu
2026-07-28 21:35:26 +08:00
parent a66d1e335f
commit c6b552e817
36 changed files with 37 additions and 561 deletions

View File

@@ -22,6 +22,6 @@ None; this package neither assembles nor sends a provider request.
## Known Limitations and Deferred Work
- **State dots have three live data states (running/amber approval-waiting/none)** — the done/error sources arrive with notifications; the four-color primitive is already wired.
- **State dots have two live data states (running/none)** — the done/error/amber sources arrive with P-II approvals and notifications; the four-color primitive is already wired.
- **Group-by menu ships by-workspace only** — Update/Status grouping strategies are drawn without specs and deferred.
- **"New task completed" unread marking is local viewing state** — completion-time > last-seen never reaches the host.

View File

@@ -94,17 +94,4 @@ describe('SidebarRoot shell', () => {
expect(b.regionOwner().wide).toBe(false)
expect(screen.getByRole('button', { name: 'Open sidebar' })).toBeTruthy()
})
it('waiting-approval shows the amber warning dot and outranks the running ring', () => {
mount(
summary({ id: 'blocked', title: 'blocked one', cwd: '/p', running: true, waitingApproval: true, updatedAt: 2 }),
summary({ id: 'busy', title: 'busy one', cwd: '/p', running: true, updatedAt: 1 }),
)
act(() => { fireEvent.click(screen.getByText('p')) })
const blockedRow = screen.getByText('blocked one').closest('[role="treeitem"]')!
const busyRow = screen.getByText('busy one').closest('[role="treeitem"]')!
expect(blockedRow.querySelector('[data-state="warning"]')).toBeTruthy()
expect(blockedRow.querySelector('[data-state="ongoing"]')).toBeNull()
expect(busyRow.querySelector('[data-state="ongoing"]')).toBeTruthy()
})
})