Mel's is where everyone pulls in and places an order — and Phase 2 is where editors place theirs. The sidebar grew an Add Tags row; it opens the White Rabbit picker lifted verbatim into a block-scoped modal: selected pills up top, an add-group bar, black group pills, and the collapsible group panels with the two-level checkbox tree. Picking existing tags and hitting Save to Block mints a tag block and its ordered TagBlockItems — atomically, in one transaction. Pills drag-reorder (native HTML5 DnD) and the order persists on save.
Phase 2 — Mel's Drive-In: Studio tag-block authoring
0 LOVES
3 VIEWS
Where orders get placed: the White Rabbit picker lifted verbatim into a block-scoped modal — pick existing tags, drag-reorder the pills, save atomically. The legacy post-form picker removed; usage counts learn about posts; delete-in-use refused. Shipped 2026-06-09.
The picker, as designed
The Add/Edit Tags modal — the White Rabbit picker in a block scope: selected pills, add-group bar, group pills, and the two-level checkbox tree. Mockup rendered with the live Studio CSS.
What shipped — and the deltas
- The legacy picker was removed, not disabled. The old post-form
pt-*card, its CSS, and its JS came out entirely — its element IDs collide with the new block-scoped modal, so it could not survive as a live duplicate. Stronger than the plan's "disable." - The data-loss guard. With the picker gone, a normal save submits no
tag_ids— andUpdatePostused to clear the tag edge unconditionally. It now syncs only whentag_idsare submitted, preservingPost.tagsuntouched for the Phase 7 migration. - Atomic create.
CreateTagBlockopens its own transaction (the genericCreate()is not transactional): block + N ordered items commit together or not at all — with the same audit row. - FK-safe delete. Tag items are
Required+NoAction, so the generic block delete would FK-fail on a tag block; it now clears the items first, in a tx. (Collection blocks never hit this — their edges areSetNull.) - Heading edits stay in the gear.
SetTagBlockItemsis items-only; the block settings modal owns heading/divider and leaves items untouched — no crossed wires between the two edit surfaces.
Guardrails & proof
- The cross-tenant IDOR, closed. The picker tree was always site-scoped, but the legacy submit trusted the POSTed
tag_ids.CreateTagBlockandSetTagBlockItemsvalidate every picked id viaBelongsToSite— a forged id from another site's tag is refused. - Usage learned about posts. The tag usage count now includes posts-via-blocks, and a nested Used In page lists the referencing posts with links — so an editor can unlink before deleting. Deleting a tag still in use is refused (
ErrTagInUse). - Proof: the named test matrix green (create-assert, foreign-site IDOR, the legacy-picker-preserves-
Post.tagsregression, request binds, reorder route); full Studio suite green; then an operator-present live smoke — create, re-pick, delete, both reorders, gear/heading, pill and group removal, empty-validation, hidden toggle, multi-group pre-check, multiple blocks per post, Used-In, delete-in-use refusal — all passed. - Honest gap: the "no tag groups configured" empty state was never live-tested (no dev site has a post and zero tag groups). It is template-guarded, identical to the proven legacy picker's guard.