Plan 7 drives a browser before it merges
The header fix this commit used to carry is gone: the word-processor session landed the same correction on main as 1859534, and did it better. Theirs names both symptoms and says explicitly that neither is the diagnosis, which generalises the point mine made about the quoted error being stale, and it narrows the open CI question to "may a gate step run unsandboxed" rather than "can this run here". Taking theirs whole rather than reconciling two paragraphs we agree about. What is left is the consequence for this plan, which is not a comment but a requirement. Plan 7's UI is the exact shape a green gate cannot see: a disabled menu item that must become enabled, a NodeView that shows a preview and then swaps it for the real image, and an upload that can fail in front of a person. 250db1c's merge went 44/44 to 39/44 on that drive with the gate green throughout, including a real WCAG SC 1.4.10 failure at 320px. So D3 and the risk table now require a browser drive before merge instead of leaving it to judgement. With the trap that comes with it. Enabling insert-link and insert-image gives each command a second home, and menu items precede the toolbar in document order — so a bare [data-docs-command="insert-link"] returns the MENU item, which sits inside a closed <details> with a zero-size rect, every click lands at the page corner, and the failure surfaces thirty seconds later as "timed out waiting for the link". Drive selectors scope to [docs-toolbar] or to the dialog's own root. clickSelector now refuses a zero-size target and refuses to pick the first of several matches, but only for code that goes through it. Verified rather than asserted: the drive runs from here at 44/44, exit 0, on 68273a4 plus this branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+22
−0
docs/superpowers/plans/2026-09-02-images-links-tables.md+22 −0
CI passed — run details
diff --git a/docs/superpowers/plans/2026-09-02-images-links-tables.md b/docs/superpowers/plans/2026-09-02-images-links-tables.md| index a342cb8..670109e 100644 |
| --- a/docs/superpowers/plans/2026-09-02-images-links-tables.md |
| +++ b/docs/superpowers/plans/2026-09-02-images-links-tables.md |
| @@ -380,6 +380,27 @@ disabled**, reserving `insert-link`, `insert-image` and `insert-table` in |
| `toolbar.mjs`'s command table. Implement against those names and no menu |
| changes are needed — the items simply become enabled. |
| +**Every one of these tasks is browser-driven before it merges, and the |
| +drive is scoped.** `scripts/browser-drive.sh`, sandbox off. Two things |
| +make this non-optional here rather than a nicety: |
| + |
| +- The UI surface is exactly the shape a green gate cannot see: a |
| + disabled state that must become enabled, a NodeView that shows a |
| + preview and then swaps it for the real image, and an upload that can |
| + fail in front of a person. `250db1c`'s own merge took the drive from |
| + 44/44 to 39/44 with the gate green throughout, including a real WCAG |
| + SC 1.4.10 failure at 320px. |
| +- **`insert-link` and `insert-image` will exist in two places** the |
| + moment they are enabled — the menu bar and the toolbar — and menu |
| + items come first in document order. So a bare |
| + `[data-docs-command="insert-link"]` selects the menu item, which lives |
| + inside a closed `<details>` with a zero-size rect, and every click |
| + lands in the page corner. Scope every drive selector to |
| + `[docs-toolbar] …` or to the dialog's own root. `clickSelector` now |
| + refuses a zero-size target and refuses to pick the first of several |
| + matches, so it will say so rather than time out — but only if it is |
| + the thing doing the clicking. |
| + |
| ### Task 6 — the sealing key, the blob store and the route |
| **Blocked on Paul's key decision.** Then `models.go` plus a migration for |
| `document_blobs`; `blobs.Sealed` over `blobs.S3FromEnv()` in |
| @@ -424,6 +445,7 @@ but not silence. |
| | `validate: "string"` on `image.src`/`link.href` | All | A raw update with a non-string attr makes y-prosemirror **delete the type**. Pre-existing, but links and images widen it from "attrs the editor never writes" to "attrs every paste writes". Not this plan's to fix; recorded so it is not rediscovered as a mystery. | |
| | Two `prosemirror-model` copies | Task 1 | `npm ls prosemirror-model` after install. Breaks silently. | |
| | Two branches inventing the same vocabulary | All | Discussion #20 (`worktree-import-documents`) builds DOCX import, which produces these same nodes. Agree names and attrs there first. | |
| +| A green gate on UI a browser would have failed | Tasks 3, 5, 8, 9 | `scripts/browser-drive.sh` with the sandbox off, before each merge, with selectors scoped per D3. Not optional: this is the one surface the gate is blind to. | |
| ## What this plan does not do |