Skip to content

PR3: Path B + agent swarm + MVI router (mocks)#4

Open
Regantih wants to merge 7 commits into
pr4/chat-and-rich-memofrom
pr6/path-b-and-swarm
Open

PR3: Path B + agent swarm + MVI router (mocks)#4
Regantih wants to merge 7 commits into
pr4/chat-and-rich-memofrom
pr6/path-b-and-swarm

Conversation

@Regantih

Copy link
Copy Markdown
Owner

PR3: Path B + Agent Swarm + MVI Router (mocks)\n\nImplements the full scaffolding for data-room link ingest, the five-agent swarm, and the MVI cost-aware model router. No real API keys are used. Every live service is hidden behind an interface and substituted with a clearly-labeled mock that is guard-gated against production use.\n\n---\n\n## What's in this PR\n\n### 1. Storage abstraction (lib/)\n- lib/storage.tsStorageClient interface: putFile, getSignedUrl, listDealFiles\n- lib/storage.mock.tsMockStorageClient: writes to /tmp/deallenz-mock-storage/{deal_id}/, returns file:// signed URLs. Throws at import time in NODE_ENV=production without USE_MOCKS=true.\n\n### 2. MVI Model Router (lib/)\n- lib/llm.tsModelRouter interface + CostEntry / CostLedger types + resolveTier() + estimateUsd() pure helpers.\n - Tier mapping: cheap (Haiku/4o-mini) → mid (Sonnet/4o) → deep (Opus/o1)\n - Downgrade ladder: if cost_budget_usd is set and the estimated cost exceeds it, the router steps down one tier (deep→mid→cheap) until under budget.\n- lib/llm.mock.tsMockModelRouter: returns deterministic [MOCK]-prefixed stub responses, realistic token counts, and estimateUsd()-computed costs so the cost-transparency UI renders with plausible numbers.\n\n### 3. Agent swarm (lib/swarm/)\n- lib/swarm/agents/researcher.tsResearcherAgent: web + doc retrieval (research task type)\n- lib/swarm/agents/analyst.tsAnalystAgent: financials, unit economics, market sizing (classify + analyze). Computes Rule of 40 deterministically from MoM when available.\n- lib/swarm/agents/risk.tsRiskAgent: risk register + red-flag summary (analyze)\n- lib/swarm/agents/writer.tsWriterAgent: 14-chapter memo, 3 chapters per parallel batch (write)\n- lib/swarm/agents/critic.tsCriticAgent: 14-point QA rubric, 6 blocking items, approved flag (critique)\n- lib/swarm/orchestrator.tsSwarmOrchestrator: plan → dispatch (parallel) → gather → compose → critique → finalize. Emits granular ProgressEvents. Aggregates CostLedger across all agents and attaches to returned DealRecord.\n\n### 4. Path B — link ingest (api/)\n- api/ingest-link.ts — Framework-agnostic handler. Accepts { url, deal_id }, validates (HTTPS only, deal_id format, URL parseable), classifies source type (Google Drive folder/file, Dropbox folder, Notion page, generic webpage), enqueues job. Returns { ok, job_id, source_type } or structured error.\n- api/ingest-queue.mock.tsMockIngestQueue: in-memory queue with realistic status transitions (queued → processing → done) and simulated per-source-type delays. Result always contains { _mock: true } to make mock nature unmistakable.\n\n### 5. submit.html — Path B UI\n- New "Path B — Data room link" section in the sidebar.\n- Input field + Ingest button (disabled until a company name exists in the live deal record).\n- Live job cards: show source type badge, status (Queued / Processing / Done / Failed), per-status note.\n- Honest error states: if the backend isn't running (404/405), the UI explains exactly why and where the code lives — no fake success.\n- Polling loop for job status updates.\n\n### 6. CostLedger JSON shape (for PR#4 UI)\nDocumented in docs/ARCHITECTURE.md. Shape is:\njson\n{\n \"cost_ledger\": {\n \"deal_id\": \"pqc-bank\",\n \"entries\": [ { \"call_id\", \"agent\", \"task_type\", \"model\", \"tokens_in\", \"tokens_out\", \"usd_cost\", \"duration_ms\", \"timestamp\" } ],\n \"total_usd\": 0.412,\n \"total_tokens_in\": 18400,\n \"total_tokens_out\": 5500\n }\n}\n\nThe cost-transparency UI in PR#4 reads deal.cost_ledger from the deal JSON.\n\n### 7. docs/ARCHITECTURE.md\nFull architecture reference covering: storage abstraction, MVI router tiers + pricing, orchestrator phases, agent contracts, critic rubric, Path B sources + job lifecycle, mock guards, environment variables, and file map.\n\n### 8. Supporting files\n- tsconfig.json — strict TypeScript + NodeNext module resolution\n- .env.example — all variables documented, real secrets never committed\n- docs/DECISIONS.md — D-021 through D-026 appended\n\n---\n\n## No fake data / no placeholder URLs\n- Mock classes are gated: throw in production unless USE_MOCKS=true\n- All stub responses are prefixed [MOCK] and include a note explaining what real integration is needed\n- MockIngestQueue result always sets _mock: true in extracted_fields\n- Path B UI shows real error messages when the backend is absent — never fakes a success toast\n\n---\n\n## Test notes\n\n### TypeScript compilation\nbash\nnpm install typescript @types/node\nnpx tsc --noEmit\n\nExpect zero errors. All files compile clean with strict mode.\n\n### Path B UI (static HTML)\n1. Open submit.html in a browser.\n2. Type a company name in chat (e.g. "We're looking at Acme Finance").\n3. Observe the Ingest button becomes active once a name is extracted.\n4. Paste https://drive.google.com/drive/folders/example and click Ingest.\n5. Observe the honest error: "The /api/ingest-link endpoint is not running in this environment" (expected — no backend running).\n6. Paste a non-HTTPS URL → see the client-side validation error before any network call.\n\n### MockModelRouter\ntypescript\nimport { MockModelRouter } from './lib/llm.mock';\nconst router = new MockModelRouter('test-deal');\nconst resp = await router.route({ deal_id: 'test-deal', agent: 'researcher', task_type: 'research', prompt: 'test' });\nconsole.log(resp.content); // starts with [MOCK]\nconsole.log(router.getCostLedger().total_usd); // realistic number\n\n\n### MockIngestQueue\ntypescript\nimport { MockIngestQueue } from './api/ingest-queue.mock';\nconst q = new MockIngestQueue();\nconst job = { id: 'j1', deal_id: 'd1', url: 'https://example.com', source_type: 'generic_webpage', status: 'queued', queued_at: new Date().toISOString(), attempts: 0 };\nawait q.enqueue(job);\nq.onJobUpdate('j1', j => console.log(j.status)); // logs: processing, then done\n\n\n---\n\n## Acceptance checklist\n\n- [x] lib/storage.tsStorageClient interface with putFile, getSignedUrl, listDealFiles\n- [x] lib/storage.mock.tsMockStorageClient writing to /tmp, guard for production\n- [x] lib/llm.tsModelRouter interface, CostEntry, CostLedger, resolveTier(), estimateUsd(), tier catalogue\n- [x] lib/llm.mock.tsMockModelRouter with deterministic stubs, realistic costs, simulated latency, production guard\n- [x] lib/swarm/agents/researcher.tsResearcherAgent with typed output\n- [x] lib/swarm/agents/analyst.tsAnalystAgent with deterministic Rule-of-40 computation\n- [x] lib/swarm/agents/risk.tsRiskAgent with risk register + red flags\n- [x] lib/swarm/agents/writer.tsWriterAgent with 14-chapter manifest + parallel batching\n- [x] lib/swarm/agents/critic.tsCriticAgent with 14-point rubric (6 blocking)\n- [x] lib/swarm/orchestrator.tsSwarmOrchestrator plan/dispatch/gather/compose/critique/finalize + ProgressEmitter\n- [x] api/ingest-link.ts — validated handler, source classifier, enqueue\n- [x] api/ingest-queue.mock.tsMockIngestQueue with status transitions + listeners\n- [x] submit.html — Path B section with link field, job cards, honest error states\n- [x] CostLedger JSON shape documented for PR#4 UI consumption\n- [x] docs/ARCHITECTURE.md — swarm, MVI, CostLedger, storage, Path B all documented\n- [x] No real API keys in any file\n- [x] No fake data shown to end users\n- [x] No placeholder URLs or "Awaiting enrichment" copy\n- [x] All mocks gated behind NODE_ENV !== 'production' OR USE_MOCKS=true\n- [x] tsconfig.json strict mode added\n- [x] .env.example documents all variables\n- [x] docs/DECISIONS.md updated D-021 through D-026\n\n---\n\n## What lands next\n- PR#2 — Real Supabase StorageClient implementation\n- PR#4 — Cost-transparency UI on deal.html (reads cost_ledger JSON shape defined here)\n- PR#5 — Real LLM adapters (lib/llm.anthropic.ts, lib/llm.openai.ts) + real data-room connectors (Drive API, Dropbox SDK, Notion API, Firecrawl)\n

Copy link
Copy Markdown
Owner Author

⚠️ Superseded by PR #7

This PR (PR3: Path B + agent swarm + MVI router, branch pr6/path-b-and-swarm) has been superseded by PR #7 (pr7/real-wire).

What happened

PR #7 was branched from pr7/auth-upload-db (which itself was based on pr4/chat-and-rich-memo, the same base as this PR). When building PR #7, all content from this PR was ported and upgraded:

This PR's content Status in PR #7
lib/storage.ts (StorageClient interface) ✅ Ported
lib/storage.mock.ts (MockStorageClient) ✅ Ported + hardened prod guard
lib/llm.ts (ModelRouter interface) ✅ Ported + updated model IDs (claude-sonnet-4-20250514)
lib/llm.mock.ts (MockModelRouter) ✅ Ported + hardened prod guard
lib/swarm/ (all 5 agents + orchestrator) ✅ Ported — now wired to real AnthropicModelRouter
api/ingest-link.ts ✅ Refactored to dependency-inject the queue
api/ingest-queue.mock.ts ✅ Ported + hardened prod guard
Path B UI in submit.html ✅ Carried through

Additionally, PR #7 adds what this PR deferred to "PR#5": a real Anthropic LLM adapter (AnthropicModelRouter), a real Supabase-backed jobs queue (SupabaseJobsQueue), and 4 database migrations.

Recommended action

This PR will be auto-closed when PR #7 merges to main. No manual action needed — the merge order is: PR #8 (docs) first, then PR #7 (everything else).

If you wish to close this PR now, use "Close pull request" below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant