Skip to content

Commit 3d4d5ce

Browse files
mpstatonclaude
andcommitted
feat(search-results, person-resolver, org-workbench): Accept keeps didi's rationale — agent_search_rationale lands on the edge
The team-crawl queue cards show didi's per-candidate reasoning ("Leads thought leadership and communications…") and Accept discarded it — the exact judgment the crawl was paid to produce. It now persists on the affiliations edge as agent_search_rationale (the relevance precedent: pairing-scoped, client-tagged via the edge) and surfaces back in the People reveal with a 🤖 prefix, deduped against the person's own headline. person.affiliate gains the optional field: set at RELATE on new edges; on existing edges filled only if absent (additive enrichment, never clobbers an earlier accept's context). organization.affiliations projects it; both TeamAccept paths (direct create + gate) pass the card's context line through. Verified: tsc + both svelte-checks clean; live NATS round-trip (affiliate with rationale → organization.affiliations returns it) on throwaway rows, deleted to zero residue. Closes #59. Files changed: - services/record-surrealdb-resolver/src/person-resolver.ts - apps/search-results/src/lib/search-client.ts - apps/search-results/src/TeamAccept.svelte - apps/org-workbench/src/lib/types.ts - apps/org-workbench/src/PersonCard.svelte Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RW28dw3kQAKXr2ZNefCukE
1 parent 2530a9f commit 3d4d5ce

5 files changed

Lines changed: 39 additions & 3 deletions

File tree

apps/org-workbench/src/PersonCard.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@
101101

102102
<div class="ow-person-card">
103103
{#if person.headline}<p class="ow-person-headline">{person.headline}</p>{/if}
104+
{#if person.agent_search_rationale && person.agent_search_rationale !== person.headline}
105+
<p class="ow-person-headline" title="didi's rationale from the team crawl that surfaced this person">
106+
🤖 {person.agent_search_rationale}
107+
</p>
108+
{/if}
104109

105110
<p class="ow-affiliation-row">
106111
<span class="ow-gate-note ow-affiliation-label">

apps/org-workbench/src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ export type AffiliatedPerson = {
8484
headline: string | null;
8585
role: string | null;
8686
relevance: string | null;
87+
// didi's crawl reasoning, persisted on the edge at Accept (gh #59).
88+
agent_search_rationale: string | null;
8789
personal_links: ShapedLink[];
8890
personal_corpus: ShapedLink[];
8991
personal_corpus_count: number;

apps/search-results/src/TeamAccept.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@
9595
person_uuid: applied.person_uuid,
9696
org_slug,
9797
role: row.person.role,
98+
// The card's context line is didi's judgment — persist it on the
99+
// edge instead of losing it at Accept (gh #59).
100+
agent_search_rationale: row.person.headline ?? null,
98101
client,
99102
source: sourceFor(row),
100103
});

apps/search-results/src/lib/search-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ export async function affiliatePerson(args: {
120120
person_uuid: string;
121121
org_slug: string;
122122
role?: string | null;
123+
// didi's per-candidate reasoning — persisted on the affiliation edge as
124+
// agent_search_rationale so Accept doesn't discard it (gh #59).
125+
agent_search_rationale?: string | null;
123126
client: string;
124127
source?: string;
125128
}): Promise<void> {
@@ -128,6 +131,7 @@ export async function affiliatePerson(args: {
128131
org_action: 'match',
129132
org_slug: args.org_slug,
130133
role: args.role ?? null,
134+
agent_search_rationale: args.agent_search_rationale ?? null,
131135
client: args.client,
132136
source: args.source ?? 'search-results',
133137
})) as { ok: boolean; error?: string };

services/record-surrealdb-resolver/src/person-resolver.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,11 @@ export type PersonAffiliateInput = {
524524
// D4 domain matching (the bio-page promotion path supplies the bio's host).
525525
org_domain?: string;
526526
role?: string | null;
527+
// didi's per-candidate reasoning from a team crawl ("Leads thought
528+
// leadership…") — persisted on the affiliation edge so Accept doesn't
529+
// discard the judgment the crawl produced (gh #59). Edge-scoped like
530+
// relevance: it's about the pairing, and client-tagged via the edge.
531+
agent_search_rationale?: string | null;
527532
client: string;
528533
source?: string;
529534
};
@@ -566,8 +571,15 @@ export async function applyPersonAffiliation(
566571
if (!hasEdge) {
567572
await db.query(
568573
`RELATE $person->affiliations->$org SET
569-
kind = $role, client_access = [$client], added_at = time::now();`,
570-
{ person: person.id, org: org.id, role: input.role ?? null, client: input.client },
574+
kind = $role, client_access = [$client], added_at = time::now()
575+
${input.agent_search_rationale ? ', agent_search_rationale = $rationale' : ''};`,
576+
{
577+
person: person.id,
578+
org: org.id,
579+
role: input.role ?? null,
580+
client: input.client,
581+
...(input.agent_search_rationale ? { rationale: input.agent_search_rationale } : {}),
582+
},
571583
);
572584
affiliation_created = true;
573585
await createObservation(db, {
@@ -577,6 +589,14 @@ export async function applyPersonAffiliation(
577589
source,
578590
client: input.client,
579591
});
592+
} else if (input.agent_search_rationale) {
593+
// Existing edge: fill the rationale only if absent — additive
594+
// enrichment, never clobbering an earlier accept's context.
595+
await db.query(
596+
`UPDATE affiliations SET agent_search_rationale = agent_search_rationale ?? $rationale
597+
WHERE in = $person AND out = $org;`,
598+
{ person: person.id, org: org.id, rationale: input.agent_search_rationale },
599+
);
580600
}
581601
}
582602

@@ -948,6 +968,7 @@ export type AffiliatedPerson = {
948968
headline: string | null;
949969
role: string | null; // the affiliation edge's `kind`
950970
relevance: string | null; // passes through as written by the rating loop
971+
agent_search_rationale: string | null; // didi's crawl reasoning (gh #59)
951972
personal_links: ShapedLink[];
952973
personal_corpus: ShapedLink[];
953974
personal_corpus_count: number;
@@ -966,7 +987,7 @@ export async function listOrgAffiliations(
966987
if (!org) throw new Error(`organization not found: ${input.org_slug}`);
967988

968989
const affRes = await db.query(
969-
`SELECT kind, relevance,
990+
`SELECT kind, relevance, agent_search_rationale,
970991
in.person_uuid AS person_uuid, in.name ?? in.full_name AS name, in.headline AS headline,
971992
in.personal_links AS personal_links,
972993
in.personal_corpus ?? [] AS personal_corpus,
@@ -984,6 +1005,7 @@ export async function listOrgAffiliations(
9841005
headline: (r.headline as string) ?? null,
9851006
role: (r.kind as string) ?? null,
9861007
relevance: (r.relevance as string) ?? null,
1008+
agent_search_rationale: (r.agent_search_rationale as string) ?? null,
9871009
personal_links: (r.personal_links as ShapedLink[]) ?? [],
9881010
personal_corpus: (r.personal_corpus as ShapedLink[]) ?? [],
9891011
personal_corpus_count: Number(r.personal_corpus_count ?? 0),

0 commit comments

Comments
 (0)