Skip to content

Commit ce2101c

Browse files
mpstatonclaude
andcommitted
new(context-v, issues): two workbench gaps — gated org-create from the UI, and non-destructive entity merge (stub)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UvYzx7vDWeafnkAi2nEQeb
1 parent 8822496 commit ce2101c

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
title: "Merging organizations or people — when two objects turn out to be the same entity, dedupe non-destructively"
3+
lede: "Sooner or later two rows ARE the same org (or person) — different imports, different spellings, one with a domain and one without. There's no merge anywhere: nothing re-points edges, unions the lists, or absorbs the loser's slug into aliases. Non-destructive but effective dedupe probably deserves its own microfrontend."
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+
- Issue
13+
- Augment-It
14+
- Organizations
15+
- Persons
16+
- Dedupe
17+
- Merge
18+
- Canonical-Layer
19+
status: Open · Stub
20+
---
21+
22+
# Merge organizations or people — a stub
23+
24+
## The need
25+
26+
Duplicates are inevitable at capture time — the candidate gates reduce them
27+
but can't eliminate them (operator picks "create" when the match was real,
28+
two imports spell one org two ways, a person exists once with an email and
29+
once with a LinkedIn URL). Today discovering a duplicate is a dead end:
30+
nothing merges. The two rows drift apart, each accumulating links, corpus,
31+
streams, affiliations, and observations the other doesn't have.
32+
33+
## Shape of the thing (jotted, not designed)
34+
35+
- **Non-destructive.** The loser is absorbed, not deleted: slug → the
36+
survivor's `aliases[]` (the `updateOrg` rename precedent), arrays union'd
37+
(URL-dedup'd, the additive discipline's existing keys), observations
38+
preserved with their provenance, `client_access` union'd. A tombstone or
39+
redirect pointer so anything stamped with the old slug/uuid still resolves.
40+
- **Effective.** Affiliation edges re-pointed to the survivor (dedup'd
41+
against existing edges), opportunities re-stamped (the `updateOrg` fan-out
42+
precedent) — after the merge there is ONE row to find.
43+
- **Human-driven.** Per [[human-in-drivers-seat]]: the operator picks the
44+
survivor and resolves field conflicts (which `complete_name` wins?);
45+
side-by-side compare, not an auto-merge score.
46+
- **Perhaps its own microfrontend.** A compare-and-merge surface (two cards,
47+
field-by-field pick, one confirm) doesn't fit inside the org card; a
48+
dedicated remote fits the one-surface-per-job pattern the workbench
49+
already follows.
50+
51+
## Deliberate contrast to keep
52+
53+
Opportunities are **never auto-merged** by design (duplicates across record
54+
sets are intentional — see the Grilling-on-DB-Resolver decisions). This merge
55+
is for the CANONICAL layer only: organizations and persons, where one
56+
real-world entity should be one row. The opportunity rule stands.
57+
58+
## Open questions
59+
60+
- [ ] One merge verb (`entity.merge`?) or per-table
61+
(`organization.merge` / `person.merge`)?
62+
- [ ] Duplicate DISCOVERY — does the merge surface also hunt candidates
63+
(same domain, near-name), or only act on pairs the operator brings?
64+
- [ ] What does the tombstone look like — a row with `merged_into`, or an
65+
alias entry alone?
66+
- [ ] Observations: re-point subject/object to the survivor, or leave them
67+
citing the absorbed row and resolve through the tombstone?
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
title: "The Org Workbench can't create an organization — and creation must sit behind a 'be sure there is no match' gate"
3+
lede: "The workbench's front door only finds orgs that already exist; there's no way to mint one from the UI. When creation lands, it can't be a bare form — the operator should be shown every plausible existing match (name, slug, alias, domain) and create only past an explicit gate, same discipline as adding a person."
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+
- Issue
13+
- Usability
14+
- Augment-It
15+
- Org-Workbench
16+
- Organizations
17+
- Candidate-Gate
18+
status: Open · Jotted
19+
---
20+
21+
# Create an organization from the Org Workbench — behind the gate
22+
23+
## The gap
24+
25+
The Org Workbench opens with `OrgSearch` — find an existing org, load its
26+
card. If the org isn't in the canonical layer yet, the operator's only paths
27+
are indirect: the CSV/record-resolution flows, or (since today) creating one
28+
as a side effect of promoting a bio link
29+
([[Person-Bio-Pages-Are-Affiliation-Signals-Not-Just-Identity-Links]]).
30+
There is no first-class "create an organization" in the workbench itself.
31+
32+
## The rule: try to be sure there is no match
33+
34+
Creation is the dangerous half of match-or-create — a casually-minted org is
35+
tomorrow's duplicate. The gate discipline that already governs persons
36+
(`AddPersonInline`: candidates ALWAYS shown, create is an explicit choice
37+
past them) applies verbatim:
38+
39+
1. Operator types a name (+ optional domain/URL).
40+
2. Candidates surface from every signal the resolver already scores — slug
41+
(100), domain via D4 `domains[*].domain` (90), fuzzy name (60), aliases —
42+
not just the autocomplete's name-contains.
43+
3. Zero candidates still gates: an explicit "No match — create
44+
‹name›" action, never a silent create on submit.
45+
4. Create seeds the thin row the existing paths seed (name → slug,
46+
`client_access`, and — per today's promotion work — a domain when one was
47+
given, so the new org is domain-matchable from birth).
48+
49+
## Existing machinery (nothing new server-side, probably)
50+
51+
- `resolveOrgRow(action:'create')` — the create verb half, already defends
52+
against slug races and (since today) seeds `domains[]`.
53+
- `findCandidates` — the scored candidate path (`resolver.candidates`);
54+
`searchOrgs` is the lighter autocomplete. The gate wants the scored one.
55+
- `AddAffiliationInline` — shipped today; its gate → create shape is the
56+
closest UI precedent. An `OrgCreateInline` (or a mode of `OrgSearch`'s
57+
empty-result state) is the org-first sibling.
58+
59+
## Open questions
60+
61+
- [ ] Where does it live — `OrgSearch`'s "no results" state growing a gated
62+
create, or a standing ➕ beside the search box, or both?
63+
- [ ] Does workbench-create take a domain up front (recommended — it powers
64+
the strongest match signal AND seeds the new row)?
65+
- [ ] Minimum viable row: name + domain + client — or also kind/notes?

0 commit comments

Comments
 (0)