Align Specter integration with backend/docs/SPECTER_FLOW.md#10
Merged
Conversation
Implement the canonical SpecterSnapshot flow exactly as specified in backend/docs/SPECTER_FLOW.md so the four Specter-consuming desks (Company, Founder, Lead Investor, Round Dynamics) read from one snapshot per run instead of ad-hoc legacy fixtures. backend/lib/sources/specter.ts - Documented HTTP layer: X-API-Key auth against https://app.tryspecter.com/api/v1, the §0 error semantics (NOT_FOUND vs WRONG_PATH vs NOT_PERMITTED vs VALIDATION_ERROR vs RATE_LIMIT) typed as SpecterError, and rate-limit header backoff. - Endpoint wrappers for §1 POST /companies, §2 GET /companies/{id}/people, §3 GET /people/{id}, §5 GET /companies/{id}/similar (+ per-peer GET /companies/{id} fan-out). - Snapshot builder that follows §1 → §2 (founder_info ∪ is_founder merge) → §3 (parallel) → §5 (parallel) → §4 cross-step flags. - §3b derived fields (prior_exits, stealth_history, departed_subject_company) and §4 flags (founder_departed_before_close, ex_founder_now_at_investor, funding_outlier_high|low, web_traffic_dip_during_growth). - §7 investor-interest is explicitly NOT WIRED; Desk 03 falls back to funding.investors[] + highlight tile (matches the spec). - Per-runId memoisation by specter_id / website_url / seed (§6.1 Backend.md / §implementation-notes SPECTER_FLOW). backend/agents/orchestrator.ts + lib/types.ts - Build the snapshot once per run and thread it to every desk via SpecterContext (snapshot, cached, mode). backend/agents/desks/{company,founder,investor,round}.ts - Refactor each desk to consume the snapshot. Citations cite the snapshot fields directly; Founder/Round desks surface §4 flags as REVIEW/INFO banners. Lead Investor desk explicitly notes the §7 NOT WIRED status and uses tier-1 + recency highlights as fallback. backend/fixtures/specter/snapshots/{dex,acme}.json - New canonical SpecterSnapshot fixtures. dex.json mirrors the worked example in SPECTER_FLOW.md verbatim (Dex / meetdex.ai / a16z Seed, Harry Uglow's 26-day pre-close exit + a16z speedrun scout overlap, AgentMail/Coverflow direct-match peers, all three §4 flags). - acme.json provides a backwards-compatible snapshot for the existing clean-acme / bec-acme scenarios after the refactor. - The legacy ad-hoc Specter fixtures (acme-company.json, acme-founders.json, sequoia-interest.json, eu-robotics-rounds.json) are removed. backend/agents/parse-prompt.ts - Add the dex-meetdex seed: prompt-driven recognition of "Dex" / "meetdex.ai", correct stage (seed) / sector (ai_infrastructure) / geography (EU) / lead (Andreessen Horowitz) defaults. backend/MANDATE.md - Add 'andreessen_horowitz' alongside 'a16z' on the tier-1 list so the evaluator recognises the long-form name on the Dex round. backend/scripts/smoke.ts - Add a third scenario for dex-meetdex; assert the verdict is review or hold and that both founder_departed_before_close and ex_founder_now_at_investor surface on the Founder desk. Verified end-to-end: npm run typecheck clean; npm run smoke passes all three scenarios (clean → proceed, BEC → hold + amendment draft, Dex → review with both §4 flags surfaced). Co-authored-by: Mr T <trenchsheikh@users.noreply.github.com>
backend/README.md - New top-level paragraph + dedicated 'Specter source' section that walks through how lib/sources/specter.ts maps onto every section of SPECTER_FLOW.md (auth, error semantics, wired vs NOT WIRED endpoints, snapshot output, caching, fixture fallback). - fixtureSeed table now lists clean-acme / bec-acme / dex-meetdex with the expected verdict and the SPECTER_FLOW.md flags surfaced for Dex. - curl reference updated with the Dex POST and a memo/amend example. README.md (root) - Lead paragraph mentions the SPECTER_FLOW.md alignment explicitly. - Demo-scenarios table adds the Dex (meetdex.ai) row alongside Acme clean / BEC, with the founder-departure + investor-overlap story. - Curl smoke section includes a Dex POST. - Project layout snippet calls out fixtures/specter/snapshots/. Co-authored-by: Mr T <trenchsheikh@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
backend/docs/SPECTER_FLOW.mdwas added in #7 as the spec for how the diligence backend should talk to Specter — endpoints, auth, error semantics, derived fields, cross-step underwriting flags, and the canonical Dex worked example. The implementation inbackend/lib/sources/specter.tswas still the original ad-hoc shim (legacygetSpecterCompany/getSpecterCompanyPeople/getSpecterInterestSignals/getSpecterTransactionscalls against fixture-only paths). This PR brings the implementation in line with the spec.What changed
1.
backend/lib/sources/specter.ts— full rewrite to follow SPECTER_FLOW.mdX-API-Key: $SPECTER_API_KEYagainsthttps://app.tryspecter.com/api/v1(override withSPECTER_API_BASE); typedSpecterErrorfor the four documented error families (NOT_FOUNDJSON vsWRONG_PATHHTML,NOT_PERMITTED,VALIDATION_ERROR,RATE_LIMIT);x-ratelimit-remaining/x-ratelimit-resethonoured.POST /companies, §2GET /companies/{id}/people, §3GET /people/{id}, §5GET /companies/{id}/similar+ per-peerGET /companies/{id}fan-out.is_foundermerge per §2 caveat) → §3 in parallel → §5 in parallel → §4 cross-step flags.prior_exits(regex overexperience.description),stealth_history,departed_subject_company(withdays_before_closemath).founder_departed_before_close,ex_founder_now_at_investor,funding_outlier_high|low, plus theweb_traffic_dip_during_growthinformational yellow flag.funding.investors[]+top_tier_investors/raised_last_monthhighlights, and the desk's facts say so.specter_id/website_url/seedfor the lifetime of the process.DEMO_FORCE_FIXTURES=true, whenOFFLINE_MODE=true, or when a live call fails — so the demo continues to run keyless.The output of
buildSpecterSnapshot()is theSpecterSnapshotobject specified in §8 of the doc, used as-is by the desks.2. Snapshot fixtures
backend/fixtures/specter/snapshots/dex.json— the canonical worked example fromSPECTER_FLOW.md(Dex /meetdex.ai, $5.3M Seed led by Andreessen Horowitz, both founders enriched, Harry Uglow's 2026-04-01 exit 26 days before the 2026-04-27 close, his "a16z speedrun scout" tagline, AgentMail / Coverflow direct-match peers, all three §4 flags pre-computed).backend/fixtures/specter/snapshots/acme.json— backwards-compat snapshot for the existingclean-acme/bec-acmescenarios.acme-company.json,acme-founders.json,sequoia-interest.json,eu-robotics-rounds.json) are removed.3. Desk refactor —
agents/desks/{company,founder,investor,round}.tsAll four Specter-consuming desks now read from
ctx.specter.snapshotinstead of calling Specter functions individually:snapshot.company; cross-checks against Companies House when HQ is UK; non-UK HQs soft-skip the registry call (the Dex case).snapshot.founders(with the §2 founder_info ∪ is_founder merge already applied); surfacesfounder_departed_before_closeandex_founder_now_at_investorfromsnapshot.flagsas REVIEW banners; sanctions / PEP screening still runs against OpenSanctions.funding.investors[], the tier-1 funds list from the mandate, and theraised_last_month/recent_fundingrecency highlights.snapshot.peers(from §5/similar); flagsfunding_outlier_high|lowwhensnapshot.flagsreports them; SPA pro-rata math unchanged.4. Orchestrator + types
agents/orchestrator.tscallsbuildSpecterSnapshot()once per run (afterparsePrompt), then threadsSpecterContext { snapshot, cached, mode }into every desk viaDeskRunner'sctx. The parsed deal is hydrated with the snapshot'sspecter_idanddomainso downstream evaluators stay consistent.lib/types.tsaddsSpecterContextand updates theDeskRunnersignature.lib/contract.tsaddsdex-meetdextoRunRequest.fixtureSeed.5. New
dex-meetdexdemo seedagents/parse-prompt.tsrecognises "Dex" / "meetdex.ai" /fixtureSeed: "dex-meetdex"and defaults stage =seed, lead =Andreessen Horowitz, sector =ai_infrastructure, geography =EU.MANDATE.mdaddsandreessen_horowitzalongsidea16zon the tier-1 list so the mandate evaluator matches the long-form name on the Dex round.scripts/smoke.tsruns the Dex scenario after clean / BEC and asserts that bothfounder_departed_before_closeandex_founder_now_at_investorsurface on the Founder desk.6. README updates (root + backend)
Evidence
npm run typecheckis clean.npm run smokepasses all three scenarios:proceed(6/6 desks pass, wire queued).hold(Wire desk BLOCK on lookalike + young domain + DKIM fail; amendment-PR draft generated).review(Founder deskflagwith both §4 flags surfaced as REVIEW banners; verdict summary "1 flag(s); 5 pass(es)" — exactly matching the §4 expectation that this should drive Desk 02 to REVIEW without blocking the deal).Captured live SSE stream and memo for the Dex run via curl:
{"type":"verdict","verdict":{"action":"review","confidence":0.45,"summary":"REVIEW — 1 flag(s); 5 pass(es)"}}founder_departed_before_close: Harry Uglow (Cofounder & CTO) left 2026-04-01, 26 days before Seed close 2026-04-27andex_founder_now_at_investor: Harry Uglow's current tagline references "a16z speedrun scout"; a16z and a16z speedrun are listed investors in the closing round.Artifacts attached:
GET /api/memo/{runId}response after the Dex run.Risk notes
_not-foundprerender error is unchanged onmain; not introduced by this PR. Typecheck and smoke are the load-bearing CI gates here.Slack Thread