← Corpus / augment-it / plan
Didi crawl — three targets (links, streams, team members), the relevance brief, and staged team ingest
One `organization.crawl` capability over Anthropic's server-side web_search, driven by a per-workspace, operator-editable relevance brief.
- Path
- plans/Didi-Crawl-Three-Targets-Relevance-Brief-And-Staged-Team-Ingest.md
- Authors
- Michael Staton
- Augmented with
- Claude Code on Claude Fable 5
- Tags
- Plan · Augment-It · Org-Workbench · Search-And-Add · Didi-Chat · Crawl · Relevance-Brief
Didi crawl — the v1.2 build
Spec reference
Implements [[../specs/Augment-From-DB-Flow]] §v1.2 (v0.1.2.0). Composes with
[[Didi-Chat-In-Org-Workbench-Verify-Team-Page-Into-People-Objects]] (which
keeps the chat-rail context plumbing). Branch: rebuild/turbo-rsbuild.
Decisions (closing the spec’s v1.2 open questions, grounded in a three-way code read)
- Crawl substrate: Anthropic server-side web_search in prompt-runner —
NOT the social-search packs.
prompt-runneris the LLM gateway (workspace never touches the SDK, by invariantchat.ts:8-10), and it already wiresweb_search_20260209with thepause_turnresume loop (request.ts:27-30,anthropic.ts:66-70). One model call composes queries, searches, filters by the brief, and returns structured candidates — the packs/connectors stay the manual 🔍‘s substrate. The social-search option (7 social packs + firecrawlScrape + Haiku fallback) is the documented fallback if model-driven quality disappoints. - Brief storage: server-side, resolver-owned (shipped ahead of this
plan):
relevance_briefstable,client.brief.get/setcapabilities. The crawl handler reads the brief itself over NATS (single source of truth for button and chat doors); the UI editor is a workbench panel. - Candidate UI: search-and-add crawl mode for links/streams — the
Phase-5 scan-mode precedent exactly (
crawl?: booleanon the envelope, a third fire path, same ResultsList/ResultRow, per-row ➕ already routed byverbFor(target)). Team members do NOT go through search-and-add — they stage on the workbench’s people reveal with per-row accept (no-candidate rows flow:person.applycreate +person.affiliate; ambiguous rows open the candidate gate — the didi-chat plan’s step 4 discipline). - Doors: crawl is a header-level action (the whole list is the
subject, unlike per-entry scan): AdditiveList gains an optional
oncrawlheader button (links + streams lists); PeopleReveal gains a “crawl team” header button; chat door =organization.crawladded toCHAT_CAPABILITY_NAMES+ a verb-slab entry (focused-org context plumbing stays with the didi-chat plan). - Org context is fetched server-side, not stuffed into envelopes: the
crawl handler NATS-requests
organization.detail(name, domains, existing list URLs for dedupe) andclient.brief.get, then prompts.
Capability contract
organization.crawl → organization.crawl.requested (prompt-runner),
timeout 300s. Input { org_slug, target: 'links'|'streams'|'team', client, max_results? }. Replies:
- links/streams:
{ ok, provider: 'didi-crawl', results: ConnectorResult[] }(url/title/content — content carries the model’s one-line relevance reason; deduped against the org’s existing URLs server-side). - team:
{ ok, people: [{name, role, headline?, linkedin_url?, bio_url?}], filtered_note, source_urls[] }— selection per the brief’s people policy (default: all major leadership + all team members covering Education & Workforce Development and related strategies/topics), with the filtered-out count named, never silent.
Steps
- prompt-runner:
crawl.ts—registerCrawlHandler(nc): fetchorganization.detail+client.brief.getover NATS; per-target system prompt (org identity + existing URLs + brief + strict JSON output contract);buildRequestwithtools:['web_search'];runPrompt; tolerant JSON extraction (fence-stripping); dedupe vs existing; reply. Register in prompt-runnerserver.ts. - workspace: capability map + 300s timeout;
organization.crawlintoCHAT_CAPABILITY_NAMES+ a WORKBENCH slab line describing the three targets and args. - search-and-add crawl mode:
crawl?: trueonSearchRequestDetail(both type mirrors);crawlSearchwrapper;crawlModederived + third fire path + status bar arm in App.svelte (scan-mode pattern verbatim). - org-workbench doors: AdditiveList
oncrawlheader prop (🤖); OrgCard passes it for links + streams (envelope{entity, target, seed_term:'', crawl:true}); BriefPanel (header toggle → textarea →client.brief.get/setwrappers). - Team staging: PeopleReveal “crawl team” button →
crawlOrg(team)→ staged rows (name · role · links) with per-row Accept/skip; accept →person.candidates→ empty:person.apply(create) +person.affiliate(role, source = team-page URL); non-empty: inline candidate gate (pick match or create).filtered_noterendered under the staged section. - Verify: typechecks + svelte-checks + builds; rebuild prompt-runner,
workspace, resolver containers; live smoke of
organization.crawlagainst the Aspen safe target if NATS reachable, else operator walk-through: Gates Foundation → crawl links (candidates appear, ➕ one) → crawl streams → crawl team (staged people per policy) → edit the brief and re-crawl to see selection change. - Changelog + gh issue close + spec revision note.
Non-goals (this pass)
- Chat-rail focused-org context and conversational triggering UX — the didi-chat plan owns it; this pass only makes the capability chat-legal.
- Headshots/enrichment of staged people (person-enrichment owns).
- Corpus-target crawl (search-and-add’s manual flow already covers corpus; revisit after the metadata work in the corpus-items issue).