|
| 1 | +--- |
| 2 | +title: "Entity-Card Edit & Remove Affordances — every visible property gets a micro-button pair" |
| 3 | +lede: "The org card views and edits in place, but only additively — a misclicked ➕ today required a direct database write to undo. Every entry and identity property the card shows grows ✎ and × micro-buttons." |
| 4 | +date_created: 2026-07-24 |
| 5 | +date_modified: 2026-07-24 |
| 6 | +authors: |
| 7 | + - Michael Staton |
| 8 | +augmented_with: |
| 9 | + - Claude Code on Claude Fable 5 |
| 10 | +semantic_version: 0.0.0.1 |
| 11 | +tags: |
| 12 | + - Spec |
| 13 | + - Augment-It |
| 14 | + - Org-Workbench |
| 15 | + - Workspace |
| 16 | + - Canonical-Layer |
| 17 | +status: Draft |
| 18 | +--- |
| 19 | + |
| 20 | +# Entity-Card Edit & Remove Affordances |
| 21 | + |
| 22 | +## Why Care? |
| 23 | + |
| 24 | +The operator ruling that shaped the org card — "no two loops, two apps; |
| 25 | +one screen that views AND edits in place" — was only half-honored. The |
| 26 | +card edits in one direction: additive. Today (2026-07-24) the operator |
| 27 | +accidentally ➕'d `alpha.school` as Princeton University's identity URL, |
| 28 | +and the only undo was an agent session doing a raw SurrealDB `UPDATE`. |
| 29 | +The additive discipline that protects canonical data from agents also |
| 30 | +locks the *human* out of corrections — exactly backwards for the |
| 31 | +human-in-the-drivers-seat model: agents propose, the operator disposes, |
| 32 | +and disposal includes *removing what's wrong*. |
| 33 | + |
| 34 | +The rule this spec sets: **anything the card renders, the operator can |
| 35 | +edit; anything the operator (or an accepted candidate) put on a list, the |
| 36 | +operator can remove.** Micro-buttons, in place, no other surface. |
| 37 | + |
| 38 | +## Scope |
| 39 | + |
| 40 | +1. **List entries** — org card's three `AdditiveList`s (identity/social |
| 41 | + links, pulse streams, corpus items) and the person-card lists that |
| 42 | + reuse the component. Each row gets: |
| 43 | + - **✎ edit** — patch `kind` (all lists), `name` (streams — already |
| 44 | + shipped via `organization.streams.update`), and `url` itself. |
| 45 | + - **× remove** — delete the entry, inline confirm ("remove |
| 46 | + alpha.school from Identity & social links?"), no modal. |
| 47 | +2. **Identity block** — the `<dl>` properties: complete/conventional |
| 48 | + name (rides the existing `resolver.update_org`, which the workbench |
| 49 | + never exposed), aliases (add ✕-chip removal), domains (same chip |
| 50 | + pattern). |
| 51 | +3. **Out of scope** — People rows (affiliation edges have their own |
| 52 | + promotion/rating flows), and merge/dedupe (gh #30, its own surface). |
| 53 | + |
| 54 | +## Decisions |
| 55 | + |
| 56 | +- **D1 — Removal is a first-class capability, not a UI trick.** New |
| 57 | + verbs, mirroring the add pair shapes, all match-by-URL (the de-facto |
| 58 | + entry key everywhere — dedupe, scan, streams.update precedent): |
| 59 | + `organization.links.remove` / `organization.links.update`, |
| 60 | + `organization.streams.remove`, `organization.corpus.remove`, |
| 61 | + and the person twins (`person.links.remove`, `person.corpus.remove`). |
| 62 | + Served by record-surrealdb-resolver beside their add siblings; 30s |
| 63 | + Cloud round-trip budget. |
| 64 | +- **D2 — URL edits are remove+re-add server-side, patch-shaped on the |
| 65 | + wire.** `*.update {org_slug, url, patch: {url?, kind?, name?}, client}` — |
| 66 | + the handler swaps the entry in place so `added_at` provenance survives |
| 67 | + a typo fix. (`organization.streams.update` keeps its current shape; |
| 68 | + the new verbs generalize it.) |
| 69 | +- **D3 — Corpus removes detach the entry, never delete content.** |
| 70 | + `organization.corpus.remove` pulls the entry off `org_corpus`; fetched |
| 71 | + markdown in the per-client corpus filesystem and `content_items` rows |
| 72 | + stay (other records/clients may reference them; storage is subscale — |
| 73 | + the redundancy ethos). A later sweep can garbage-collect orphans if it |
| 74 | + ever matters. |
| 75 | +- **D4 — Removal leaves a trail.** Every remove writes one observation |
| 76 | + (`predicate: 'entry_removed'`, object = the URL, `client` tag, actor |
| 77 | + attribution) so canonical history keeps what-was-there-and-when without |
| 78 | + keeping the wrong data live. Accumulate observations, not overwrites. |
| 79 | +- **D5 — Micro-buttons are hover-revealed, confirm inline.** Row rest |
| 80 | + state stays as today; hover (or row focus) reveals `✎ ×` at the row's |
| 81 | + right edge, sized to the existing `.ow-plus` class. × flips to an |
| 82 | + inline "remove? yes / keep" pair — the Search-Results-Queue dismiss |
| 83 | + confirm pattern, no dialogs. The streams ✎ generalizes to all lists. |
| 84 | +- **D6 — Wire discipline unchanged.** Slugs/uuids only, `client` on |
| 85 | + every call, `augment-it:entity-updated` broadcast after every mutation |
| 86 | + so the card, roster counts, and any open search-rail cards refetch. |
| 87 | + |
| 88 | +## Implementation sketch |
| 89 | + |
| 90 | +1. **Capabilities** — handlers in record-surrealdb-resolver (links.ts / |
| 91 | + streams / corpus modules beside the adds), capability map + timeout |
| 92 | + entries in `services/workspace/src/capabilities.ts`. |
| 93 | +2. **AdditiveList** — `onedit` generalizes from streams-only to all |
| 94 | + lists (kind editing everywhere, name where `nameable`); new |
| 95 | + `onremove` prop with the inline confirm; hover-reveal CSS. |
| 96 | +3. **OrgCard / PersonCard** — wire the new verbs; identity block gains |
| 97 | + name-edit (via `resolver.update_org`) and alias/domain chip removal. |
| 98 | +4. **Proof** — extend `prove-augment-from-db-capabilities.mjs` with the |
| 99 | + remove/update round-trips against the Aspen safe target; browser |
| 100 | + drive: add a wrong link, edit its kind, remove it, watch the card and |
| 101 | + roster counts refetch. |
| 102 | + |
| 103 | +## Open questions |
| 104 | + |
| 105 | +- [ ] Does × on a stream that has corpus items scanned from it warn |
| 106 | + ("this stream fed 12 corpus items — they stay")? Lean yes, same inline |
| 107 | + note, no gate. |
| 108 | +- [ ] Alias/domain removal via `resolver.update_org` sparse patch or |
| 109 | + dedicated verbs? Lean update_org — it already owns the identity block. |
| 110 | + |
| 111 | +## Related |
| 112 | + |
| 113 | +- [[Augment-From-DB-Flow]] — the card this corrects; its D-series set the |
| 114 | + additive discipline this spec now balances. |
| 115 | +- [[../plans/Workbench-Usability-Sweep-Corpus-Visibility-Stream-Editing-Affiliation-Promotion]] — |
| 116 | + the `streams.update` match-by-URL precedent (#26). |
| 117 | +- [[Search-Results-Queue-Remote]] — the inline-confirm pattern D5 copies. |
0 commit comments