feat(workbench): approval-gated assistant writes + framed bridge client (0394 phase 2) - #656
Merged
Conversation
added 3 commits
July 28, 2026 21:01
… phase 2) Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
Preview removed for PR #656. |
crs48
added a commit
that referenced
this pull request
Jul 29, 2026
Every box in exploration 0406 is checked — 23/23 implementation, 10/10 validation — so the filename flips to `[x]`. The arc, for the record: #641/#642/#644 (port + core extraction), #648 (AI panel on desktop), #650/#651 (chrome behind injection points, first Electron mount), #652 (desktop surfaces in the shell), #653 (flag removed, bespoke shell deleted), #655 (cold-open back under the 0.52s baseline), #656 (approval-gated writes — the last blocked item). The desktop and web apps now render one shell from one module, verified live at every step. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This was referenced Jul 30, 2026
crs48
pushed a commit
that referenced
this pull request
Jul 30, 2026
…shim check The visual-capture `capture` job has failed its Build Storybook step on every PR since #656: `views/ai-chat-write-tools.ts` imports `writeModeFor` from `@xnetjs/plugins`, and the storybook browser shim never got a matching export. `pnpm check:storybook-shim` stayed green through all of it. Its walker only knew the aliases in `.storybook/workspace-aliases.ts`, and `@xnetjs/workbench` has no entry there, so it dropped the specifier and never reached the importing module — while rollup resolved it through the workspace link and failed. A gate that cannot fail on a real breakage teaches everyone to ignore red. The walker now resolves `@xnetjs/*` the way the build does: aliases first, then the package's own `exports`/`main`, mapping `dist` entries back to the source they are built from. A specifier it cannot resolve is a hard failure rather than a silent drop, so the graph cannot quietly shrink again. It also seeds the preview and manager entries, not just stories. Coverage goes from 873 to 1177 modules, which turned up a second real break (`AgentAuditRecorder`, also a value import) plus ten type-only names the shim should mirror. Verified with `pnpm build:stories`, the same command the capture job runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
crs48
added a commit
that referenced
this pull request
Jul 30, 2026
…shim check (#661) ## What broke The `capture` job in `.github/workflows/visual-capture.yml` has failed its **Build Storybook** step on every PR since #656. Pre-existing on `main` (verified at `bcec607b1`), unrelated to the PR that surfaced it (#658). ``` "writeModeFor" is not exported by ".storybook/shims/xnet-plugins-browser.ts", imported by "packages/workbench/src/views/ai-chat-write-tools.ts". ``` `.storybook/workspace-aliases.ts:29` aliases `@xnetjs/plugins` to the browser shim, and 0394 phase-2 (#656) added a `writeModeFor` import the shim never got a matching export for. ## Why the gate didn't catch it `pnpm check:storybook-shim` stayed green the whole time — "873 modules walked, 114 shim exports". Its walker only knew the aliases parsed out of `workspace-aliases.ts`. **`@xnetjs/workbench` has no entry there**, so the walker silently dropped the specifier and never reached the importing module — while rollup resolved it through the workspace link and kept bundling. Everything past that point went unchecked. Per AGENTS.md: *"A gate that cannot go green teaches everyone to ignore red."* The inverse is worse — a gate that cannot go **red** on a live breakage. ## What changed **`.storybook/shims/xnet-plugins-browser.ts`** — adds `writeModeFor` / `WriteMode`, matching `packages/plugins/src/ai/connectors/types.ts:101`, plus the names the widened walk turned up. **`scripts/check-storybook-shim.mjs`** — resolves `@xnetjs/*` the way the build does rather than only via aliases: - aliases first (Vite applies them first), then the package's own `exports`/`main`; - `dist` entries map back to the source they're built from, so the walk doesn't stop at an artifact that may not be present; - an `@xnetjs/*` specifier it *cannot* resolve is now a **hard failure**, not a silent drop — the graph can't quietly shrink again; - seeds `.storybook/preview.tsx` and `manager.tsx`, which the build compiles too. Coverage: **873 → 1177 modules walked**. That turned up a **second real break** — `AgentAuditRecorder` in `views/ai-chat-ceremony.ts:32`, also a value import — plus ten type-only names the shim should mirror anyway. ### On "walk the full module graph instead" Rejected as framed. The story graph *was* nearly the whole build graph; the defect was that the **resolver was weaker than Vite's**, not that the seed set was too small. Matching resolution (plus the two extra entry seeds) closes the class without needing to run rollup to find out. ## Verification | Check | Result | | --- | --- | | `pnpm build:stories` (the capture job's exact command, after `turbo run build --filter='./packages/*'`) | ✅ `Storybook build completed successfully` | | `pnpm check:storybook-shim` | ✅ 1177 modules walked, 128 shim exports | | Regression proof — remove `writeModeFor` from the shim again | ✅ now **fails** with `· writeModeFor (…/ai-chat-write-tools.ts)`; the old walker passed | | Pre-push full suite | ✅ 11331 passed, 4 skipped | Labelled `skip-changelog`: dev tooling, nothing a user notices. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Exploration 0394's deliberately-deferred phase 2, built with the design pass it asked for — and the unblocking of 0406's last implementation item.
The in-chat approval ceremony (
ai-chat-ceremony.ts): write tools ride the existing risk-tieredAgentAuditRecorder(0337). Reads bypass (no audit-node flood from searches); low-risk writes execute with an audit trail; medium parks on the one-time code —APPROVE <code>typed in chat rides the real nonce machinery (SHA-256 match, TTL, single-use); high/critical refuse the chat path structurally (no nonce exists) and need the deliberate in-app approval, stamped with the operator's DID, with the change reviewed before the button enables. Denial and expiry resolve the model's tool call honestly — the turn never hangs. 9 ceremony tests + 5 gating tests.Doubly-gated write tools (
ai-chat-write-tools.ts): the tier must bereliable(writeModeFor= agentic) AND the operator must flip the new "Allow edits" toggle (default off). The badge escalates honestly: reads workspace → searches workspace → edits with approval.The framed bridge client (
ai-bridge-frames.ts): the panel's bridge tier now speaks/v1/agent/stream— the endpoint #623 shipped and nothing consumed. Tool activity, per-turn cost, and permission asks arrive structured instead of flattened away.Desktop MCP ceremony:
agent-mcp-server.tsnow passesagentAudit, closing a real hole — a bridged agent could previously runxnet_apply_page_markdownwith no gate but the system prompt.Verified live on the desktop: with writes armed, a chat turn through the bridged Claude Code created a real Page node in the desktop SQLite store (
Ceremony Test 0394), and the panel showed "Last turn · $0.1581 · 495 output tokens" — cost frames only exist on the framed wire, so that line is proof of the new transport. With writes off, the agent still refuses (verified in the same session).Doc movement: 0394 → 9/10 impl, 3/7 val. 0406 → 23/23 impl, 9/10 val (only the cold-open item remains, in flight separately).
🤖 Generated with Claude Code