Built in public · Changelog
How Supernova learned the job.
Product panorama
One deal, one continuous operating loop.
Seventeen agents live in the product today, two squads (7 sales-side, 10 delivery-side), propose-only, a named human at every gate. Everything below ships; nothing is a mock.
Sell
Daily Brief, Email Drafter, Call Notes, Deal Check, Meeting Prep, Promise Tracker and Enrich run the deal desk, evidence attached.
Spec
A won deal births the project; Persona Researcher and Spec Writer read the real corpus and draft the use-case tree, every requirement quoting its source.
Model
Data Modeler, Role Definer and Page Architect ratchet ontology, roles and the page atlas forward, one approved artifact feeding the next.
Design
Designer codifies the design language; Mock Builder renders one mock per page wearing the approved tokens.
Run
E2E Writer specs the tests; Tech Stack Advisor and Impact Analyst guard the plan; delivery runs five-state stories, bugs, QA gates and release exit criteria.
Audit
Every run is a session you can open: model calls, tool calls, cost, the audit trail behind every artifact.
20
Jul 2026
Monday
3 shipped
Agents10:11 ISTThe agents get a front door, and the CEO gets a board that retires the timesheet
Two mirrors on the same truth landed together. First, the agent team got a public face: /agents (linked from the landing nav) walks the real 17-agent roster in its two squads - seven on the…
The agents get a front door, and the CEO gets a board that retires the timesheet
Two mirrors on the same truth landed together. First, the agent team got a public face: /agents (linked from the landing nav) walks the real 17-agent roster in its two squads - seven on the…
Two mirrors on the same truth landed together. First, the agent team got a public face: /agents (linked from the landing nav) walks the real 17-agent roster in its two squads - seven on the sales floor, ten on the delivery line - each card naming what the agent actually does rather than what a brochure wishes it did, the tools-and-ledger story told straight (an agent here is a tool loop whose every run opens a session you can read back call by call), and the promise made plainly: zero black boxes, and the operator is you - agents propose, the human disposes. Meeting Minutes and What's Next stand on the page too, marked as still in the shop, because a roster that hides its gaps is a brochure, not a page about a real product. Second, /admin/command grew two bands for the CEO, because Vinay's questions are not an SDR's: he asks where every engagement stands and what the company actually did this week. The Delivery portfolio band answers the first - every active project in one sweep: phase, go-live runway, the active sprint's progress, blocked count, open bugs, open gates. The work record · last 7 days answers the second, and it carries the bigger idea: it is the TIMESHEET REPLACEMENT. Every story move, QA run, bug hop, and plan change already lands in the audit spine the moment it happens, so a person's last seven days can be READ instead of asked for - the timesheet becomes a report, not a form, and nobody spends Friday reconstructing what the system watched them do all week. The concept generalizes hard (git commits and agent sessions join the record next, then a per-person drill-down), and it only works because the spine rule held from day one: every feature feeds the audit trail, so the audit trail can now feed the CEO.
Agents09:51 ISTThe Spec Writer earns its tool belt, and quotes get checked by a machine
The glue thesis lands on its first delivery engine. The Spec Writer is no longer one stuffed prompt: src/lib/ai/spec-tools.ts gives it a working belt - read_document pages the FULL corpus…
The Spec Writer earns its tool belt, and quotes get checked by a machine
The glue thesis lands on its first delivery engine. The Spec Writer is no longer one stuffed prompt: src/lib/ai/spec-tools.ts gives it a working belt - read_document pages the FULL corpus…
The glue thesis lands on its first delivery engine. The Spec Writer is no longer one stuffed prompt: src/lib/ai/spec-tools.ts gives it a working belt - read_document pages the FULL corpus text in 6k-char pages (follow "MORE FOLLOWS" to END OF DOCUMENT; the old 15k truncation is gone), search_corpus keyword-hits across documents + timeline + promises, list_promises, list_existing_use_cases (which turns "Draft again" from blind appending into gap-filling), and save_use_case, the write-back that validates and inserts ONE use case WITH its stories and tests in a single transaction, so an appended use case can never again land story-less. The runner (agent-spec.ts) is a SONNET toolRunner, 30 iterations max, with two mandatory phases: READ the authoritative documents end to end and build a coverage list of every in-scope module, then SAVE one use case per module. And because "quote the corpus" used to be a polite request, a deterministic QUOTE VERIFIER now sits inside save_use_case: any source.quote not found verbatim in the corpus (normalized for whitespace and curly quotes) is rejected back to the agent with an error it must fix - grounding enforced by code, not by prompt. First live run, project 38 (SalarySe): 11 model calls, 22 tool calls, the agent read the signed agreement end to end, correctly treated the 8 existing use cases as ground to fill around rather than duplicate, and saved exactly one new use case, "Operate Talent-on-Demand Staffing, Onboarding and Commercial Governance", with 3 stories, 4 tests, and a verbatim Service Agreement quote. The whole run reads off /admin/ai sessions like a transcript, which is exactly what the ledger was built for.
Agents09:51 ISTAgent #17 answers "what do we build it with", and the landing stops guessing
The tech-stack question becomes an artifact instead of a hardcode. Tech Stack Advisor (src/lib/ai/agent-stack.ts) reads the corpus, ontology, and NFRs and proposes the stack layer by layer…
Agent #17 answers "what do we build it with", and the landing stops guessing
The tech-stack question becomes an artifact instead of a hardcode. Tech Stack Advisor (src/lib/ai/agent-stack.ts) reads the corpus, ontology, and NFRs and proposes the stack layer by layer…
The tech-stack question becomes an artifact instead of a hardcode. Tech Stack Advisor (src/lib/ai/agent-stack.ts) reads the corpus, ontology, and NFRs and proposes the stack layer by layer into a new tech_stack_proposals table - versioned DRAFT -> APPROVED, layers jsonb where every choice carries a plain rationale a client could read, a corpus quote wherever the client's own documents NAME the technology, and a corpus_named flag (a named integration is FIXED and quoted verbatim; where the corpus is silent, the house default with the why). The TL approves on the Design page, and once a proposal is APPROVED the scaffold ships docs/TECH-STACK.md into the repo, retiring the assumption that every client gets Next+Tailwind+Drizzle because that is what the generator knew. The build also settled the agent-tools DOCTRINE in one sentence: an agent gets TOOLS wherever it reads the CLIENT's world (corpus, web) and stays single-shot where it transforms OUR structured artifacts; the conversion queue is Persona Researcher, Tech Stack Advisor (done), Impact Analyst. And the landing page was re-synced to the real product story - 17 agents, the delivery journey, the roles - so the marketing finally describes the app that exists rather than the one first imagined.
19
Jul 2026
Sunday
1 shipped
Agents00:34 ISTThe core strengthens: every agent run becomes a session you can open
The first brick of the glue horizon, foundation-first as the user called it (schema, then UI, then the core). The ledger was already half-true: llm_calls records every model call with a…
The core strengthens: every agent run becomes a session you can open
The first brick of the glue horizon, foundation-first as the user called it (schema, then UI, then the core). The ledger was already half-true: llm_calls records every model call with a…
The first brick of the glue horizon, foundation-first as the user called it (schema, then UI, then the core). The ledger was already half-true: llm_calls records every model call with a trace_id. What was missing was the RUN as a first-class thing. Now withLlm() itself opens an agent_sessions row before the work and closes it after - COMPLETED with totals or FAILED with the reason - so no agent can forget to ledger itself, and agent_tool_calls records every tool invocation (name, input summary, output preview + length, sequence) through the same funnel the Writer's tool belt already used. All three tables share the trace_id, which means /admin/ai now carries an Agent sessions table (one row per run: outcome, calls, tools, tokens, time, cost) and each row opens /admin/ai/sessions/[trace]: the whole run reconstructed as a timeline - tool calls with their inputs and previews interleaved with model calls wearing purpose, model, tokens, latency, and stop reason. Proven live: fired Deal Check through the AE surface and read its 7 get_email calls and 3-phase loop (investigate → score → critic) off the drill-down. When the next "the regenerate failed" report lands, the answer is a click, not an absence. gatherCorpus also now returns its untruncated parts (documents, timeline, promises) - the doorway the Spec Writer's tool belt walks through next.
17
Jul 2026
Friday
1 shipped
Agents00:06 ISTDesigner (agent #10), the preview wall, and the delivery engines move to Claude Sonnet
Designer (src/lib/ai/agent-designer.ts): codifies the client product's design language from the tech lead's written rules (law where present), the approved use cases, the entities, and the…
Designer (agent #10), the preview wall, and the delivery engines move to Claude Sonnet
Designer (src/lib/ai/agent-designer.ts): codifies the client product's design language from the tech lead's written rules (law where present), the approved use cases, the entities, and the…
- Designer (
src/lib/ai/agent-designer.ts): codifies the client product's design language from the tech lead's written rules (law where present), the approved use cases, the entities, and the assets on file. Two artifacts onprojects: DESIGN.md (identity, palette table with hex + rationale, typography, spacing, voice, component rules, and an honest Sources section saying what was assumed) and scoped shadcn tokens (design_css). The CSS is variables-only by contract AND rebuilt by a sanitizer (only.design-preview/.design-preview.darkselectors survive, only--custom-properties, conservative value charset, no url()/expression/@) because injected<style>is a real surface even on an internal tool. Verdict PASS when written rules existed, ASSUMED_DEFAULTS when Designer had to derive. Audited AGENT_DESIGNED. - The preview wall (
/tech-lead/design/preview): fifteen real shadcn components wearing the client's tokens, light/dark toggle local to the wall (both themes must survive). Works because globals.css maps Tailwind colors through@theme inlinevars, so scoped overrides cascade; Supernova's own clay accents (Button's default variant is bg-clay) are bridged onto the client primary inside the scope so house styling can't leak orange into the client's skin. - The delivery engines now run Claude Sonnet (user: "we need to build claude sonnet agents for the same").
SONNET = 'claude-sonnet-5'beside HAIKU inanthropic.ts; Spec Writer, Data Modeler, and Designer generate on it (their output is what a tech lead defends to a client), JSON repair rounds stay on Haiku. max_tokens raised to 16k on generation calls: Sonnet 5 runs adaptive thinking by default and thinking shares the output budget.llm_pricinggained the claude-sonnet-5 row ($3/$15 sticker) sollm_calls.cost_usdcomputes; the live Designer run logged 11,978 output tokens / $0.18 / 118s, attributed to Naman, verdict PASS. - Markdown tables: the dependency-free
Markdowncomponent now renders pipe tables (header +|---|separator) as real tables - DESIGN.md's palette was pipe soup without it, and every agent-authored doc benefits. - Live-fired as Naman on SalerySe with real written rules (teal #0E7C7B on warm off-white, marigold accent "at most once per screen", tabular numerals as law, never exclamation marks). Sonnet honored every rule and showed its work: per-token contrast ratios, dark mode as "the same role meanings in a teal-charcoal register", voice rules with concrete before/after copy. Verified on the wall in both themes.
16
Jul 2026
Thursday
4 shipped
Agents23:11 ISTDesign language, the approve gate, and Data Modeler (agent #9)
Design language page (/tech-lead/design, user's ask): the product-being-built's brand. Logo uploads render on BOTH light and dark swatches (a logo that survives one background is not a logo…
Design language, the approve gate, and Data Modeler (agent #9)
Design language page (/tech-lead/design, user's ask): the product-being-built's brand. Logo uploads render on BOTH light and dark swatches (a logo that survives one background is not a logo…
- Design language page (
/tech-lead/design, user's ask): the product-being-built's brand. Logo uploads render on BOTH light and dark swatches (a logo that survives one background is not a logo yet), a brand-imagery gallery, and freeform written rules (projects.design_notes) the UX engine will read as law. Assets live inproject_assets(bytea, image-mime whitelist, 8MB cap), served INLINE by the authz-gated/tech-lead/assets/[id](same status+role recheck as documents). Audited PROJECT_ASSET_ADDED. - The approve gate: use cases finally have their sign-off button. APPROVED is what downstream engines feed on; a refine drops the status back so the button honestly reappears. Audited USE_CASE_APPROVED, high-signal in the trail (a client-facing commitment).
- Data Modeler (agent #9,
agent-ontology.ts): derives the product's data model from the use cases, approved first (verdict says UNAPPROVED_BASIS when it had to model from drafts). A regeneration replaces the project's whole set in one transaction: the model is one artifact. The Ontology page went real; the payroll mock (and mock-data.ts) is deleted. - Live-fired the full chain as Naman on SalerySe: approve the refined use case -> derive -> 11 entities, 84 fields, 21 relationships, 4 PII flags, and DesignEngagement's status lifecycle (DISCOVERY -> ... -> COMPLETED) cites BR-5/BR-6 from the use case as its transition gates. Used-by chips trace to the real use case. Logo upload verified through the inline route.
Agents22:18 ISTSpec Writer: the first delivery engine, and the spine goes real
Housekeeping first (user's 1): the 36 seeded projects purged plus the FNBPC test row - the projects table now holds only wizard-created engagements (SalerySe). G. Vedant's test AI cap…
Spec Writer: the first delivery engine, and the spine goes real
Housekeeping first (user's 1): the 36 seeded projects purged plus the FNBPC test row - the projects table now holds only wizard-created engagements (SalerySe). G. Vedant's test AI cap…
- Housekeeping first (user's 1): the 36 seeded projects purged plus the FNBPC test row - the projects table now holds only wizard-created engagements (SalerySe). G. Vedant's test AI cap cleared.
- The spine is real (user's 2):
use_cases,user_stories,test_cases,use_case_refinementsunder projects (flows/rules/acceptance as structured jsonb inside their parent; refinements as rows because they are the spec's audit trail). The mock pages swapped to queries with the depth intact - coverage gaps and blocking questions now compute from real rows. - Spec Writer (user's 3,
src/lib/ai/agent-spec.ts, agent #8): reads the project's own corpus - deal thread, call recaps, notes, promises, uploaded text documents - and drafts the tree with PROVENANCE on every use case. One repair round on malformed JSON (the Email Drafter idiom; the first live run needed it). Runs ride the full spine: withLlm attribution + budget gate, llm_calls, agent_run_summary, audit AGENT_SPECCED (PREVIEW). - The fine-tune loop is live: refine() takes plain language, regenerates the use case, bumps the version, drops status to REFINED, records the note + effect in history, audits USE_CASE_REFINED.
- Live-fired on SalerySe as Naman, and the run was honest twice over: the corpus was mostly the sales thread (the uploaded "scope doc" was filler), so draft v1 specced the sales process it actually saw, with 3 blocking questions saying what was missing. ONE refinement note ("this reads like the sales process, not the product") pivoted the whole spec to "Deliver UI/UX Design Engagement for SalerySe Product": new actors, ₹390,000 budget as a precondition, WCAG 2.1 AA and React mobile-first in the NFRs, delivery-side blocking questions. v2, refined, history kept.
- Queued next (user's 4): ontology generation from approved use cases, story/test regeneration, Roles and Pages, discovery capture, and the Design-language page (logo + brand assets).
Agents15:43 ISTThe cockpits stop greeting and start briefing
User calls, in order: "Good afternoon, Priyadharshini" told the rep nothing and stacked a second display-size headline over the briefing's own (the two even disagreed - live pulses 941…
The cockpits stop greeting and start briefing
User calls, in order: "Good afternoon, Priyadharshini" told the rep nothing and stacked a second display-size headline over the briefing's own (the two even disagreed - live pulses 941…
- User calls, in order: "Good afternoon, Priyadharshini" told the rep nothing and stacked a second display-size headline over the briefing's own (the two even disagreed - live pulses 941 overdue, the 09:40 snapshot 944); then "SDR · My Day · Thursday, 16 Jul 2026 - why do u need this" - the sidebar names the page, the breadcrumb names the role, the briefing's own eyebrow carries the date. Triple-redundant chrome.
- Both cockpits (SDR My Day, AE Desk): greeting h1 AND page eyebrow removed (an sr-only h1 keeps each page named for assistive tech; Sync now stays, aligned with the stats line). The page now opens directly on the numbers: stats sentence -> pulses -> briefing.
- The shared briefing headline (
daily-briefing.tsx) dropped from clamp(1.45-2rem) display size to a text-lg lead sentence - it is the title of the DAY, not a second title of the page. The whole working day fits above the fold now. - NOTE for other pages: eyebrows stay app-wide (they carry real orientation on deep pages); the cockpits are special because nav + briefing already say everything the eyebrow said.
Agents09:15 ISTAI cost per person + per-user budget gate; models on the ledger
Attribution - withLlm() now resolves the acting user itself (explicit meta.userId, else the session cookie; cron/scripts honestly log null), so llm_calls.user_id fills for every in-app…
AI cost per person + per-user budget gate; models on the ledger
Attribution - withLlm() now resolves the acting user itself (explicit meta.userId, else the session cookie; cron/scripts honestly log null), so llm_calls.user_id fills for every in-app…
- Attribution -
withLlm()now resolves the acting user itself (explicitmeta.userId, else the session cookie; cron/scripts honestly log null), sollm_calls.user_idfills for every in-app agent run. Before: 5 of 8 agents logged null and 63 of 116 calls were unattributable. - /admin/ai "By person" - who incurred what: calls, tokens, this-month + all-time ₹ per user, one "(unattributed)" row for cron/scripts/legacy, and an inline monthly budget editor (₹ in the UI, USD in the ledger). Over-budget people surface as a pulse in the header sentence.
- The gate -
users.ai_monthly_budget_usd(targeted ALTER, null = no cap; ₹0 = block all) enforced insrc/lib/ai/budget.tsat the samewithLlmchoke point, reading the same ledger the page shows, so the gate and the display cannot disagree. Blocked runs throw a human-readableAiBudgetErrorand audit asAGENT_BUDGET_BLOCKED(FAILURE, loud); budget edits audit asAI_BUDGET_SETwith before/after. New indexllm_calls_user_time_idx. - Models are visible (user ask) - the By-agent table gains a Models column and Recent calls gains Who + Model, date-suffix stripped (
haiku-4-5, not the SKU). - Found-by-testing: a sub-₹1 budget rounded to $0.00 and read as "no cap" while blocking everything - the action now refuses caps under ₹1 (0 stays the explicit block-all), and the Used bar treats 0 as blocked, not capless.
15
Jul 2026
Wednesday
2 shipped
Agents08:43 ISTThe Rohit walk: the org chart stops decorating, the briefing stops wrapping names
Walking the admin suite as Rohit (Command / Users / Audit / AI usage / Tech leads / Org chart), two real design-language breaches:
The Rohit walk: the org chart stops decorating, the briefing stops wrapping names
Walking the admin suite as Rohit (Command / Users / Audit / AI usage / Tech leads / Org chart), two real design-language breaches:
Walking the admin suite as Rohit (Command / Users / Audit / AI usage / Tech leads / Org chart), two real design-language breaches:
- The org chart was the last page decorating with colour. Its L1-L4 depth ramp was neon indigo / blue / emerald / purple on bordered boxes, the exact thing Daylight bans (hierarchy comes from scale and weight, never a rainbow; the entity hues are warm and muted). Depth is now a ladder of prominence: clay at the root, fading down the ink ramp, elevation carrying the box, borders gone. The legend reads from the same
LEVEL_STYLES, so it re-tuned itself. - The briefing's lead column is a MEASURE column (times "12:30", amounts, ages) and aligns beautifully at
w-16with tabular-nums. The Command Board's rep names are not measures: "Nitish Kumar Rana" wrapped to three lines. Names and company labels now getw-28+ truncate, and drop tabular.
Everything else on the admin suite held up: the team briefing reads with real intelligence ("Himanshu carries the biggest load: Rs 19.9 Cr with 20 slipping deals and 58 overdue tasks"), the audit trail shows agent runs with actor glyphs, the AI ledger costs every call.
Agents00:29 ISTEnrich (agent 7): live web research on any company, including ones not in the CRM yet
The seventh agent, Enrich, answers "who is this company, really?" with the live web instead of the CRM's stale fields. Built on Anthropic's server-side web_search tool (verified on the…
Enrich (agent 7): live web research on any company, including ones not in the CRM yet
The seventh agent, Enrich, answers "who is this company, really?" with the live web instead of the CRM's stale fields. Built on Anthropic's server-side web_search tool (verified on the…
The seventh agent, Enrich, answers "who is this company, really?" with the live web instead of the CRM's stale fields. Built on Anthropic's server-side web_search tool (verified on the user's key; ≤5 searches per run, each metered at $0.01 into llm_calls.web_searches and the cost ledger):
- `src/lib/ai/agent-enrich.ts`: one Haiku call with web search → structured dossier: what they do / size / HQ, recent news (every item must carry its source URL — uncited news is dropped deterministically), key people, an honest ICP-fit read (strong/medium/weak) against Antino's ICP, ≤3 talking angles, and proposed CRM updates (current → proposed, only where the web contradicts or fills a listed field — propose-not-act, they land in Zoho with write-back). Verdict PASS/THIN/NOT_FOUND; run summary + trace-true totals; upsert into new
enrichmentstable (unique per entity_kind + entity_id). - Three entity kinds:
lead(company or corporate-email domain),account(name + website), and `prospect` — research-from-nothing: the company name typed into accounts search, stored lowercase. The/ae/accounts?q=LifeWiseempty state now offers "Research LifeWise on the web"; the dossier renders right there, with a Propose as account button that auditsACCOUNT_ADD_PROPOSED(PREVIEW) with the dossier fields inafter. - Surfaces (
src/components/enrich-panel.tsx): accounts-search empty state, account hub, AE lead hub, SDR lead hub. Toast on start + finish (principle 13), receipt line (verdict · N web searches · ₹). Server actions insrc/lib/enrichment/actions.tsauditAGENT_ENRICHED. - Eval added (5th agent eval):
/api/evals/runcase +run-evals.mjsasserts company identified, all news cited, valid ICP score, totals == ledger, cost < $0.15 (search-metered ceiling). - Verified live: Bajaj Finserv account (PASS, 4 cited news, ICP strong, $0.088); LifeWise prospect (found LifeWise Academy, honestly scored ICP weak — the openers even warn the name is ambiguous across companies); ANSCER Robotics lead (ICP strong, the real $5.4M Series A + US expansion in news, key people matching the actual email thread, 4 proposed field updates: industry, 73 employees, anscer.com, Bengaluru).
14
Jul 2026
Tuesday
10 shipped
Agents20:30 ISTTest infrastructure: route smoke suite + agent evals + CI
The honest audit said it: agents were verified live but had no eval suite, and the early Playwright pass was a one-off, not maintained. Built the first two tiers:
Test infrastructure: route smoke suite + agent evals + CI
The honest audit said it: agents were verified live but had no eval suite, and the early Playwright pass was a one-off, not maintained. Built the first two tiers:
The honest audit said it: agents were verified live but had no eval suite, and the early Playwright pass was a one-off, not maintained. Built the first two tiers:
- Route smoke suite (
tests/e2e/,npm run test:e2e) — Playwright signs in one seeded user per role (global-setup samples live entity ids from the DB), then hits 37 routes across admin/AE/SDR/public — every static page plus real dynamic instances (deal hub, lead hubs, account, contact, recording, meeting event, a peek URL). Each must return <500, render a landmark, show no Next error state (the dev overlay text is checked through the shadow DOM —<nextjs-portal>itself always exists in dev), and log no serious console errors. 37/37 green in ~39s — and the suite caught a real bug on its maiden run:/admin/ai500'd becauseas w(window)used a reserved SQL word. Fixed. - Agent evals (
scripts/evals/run-evals.mjs,npm run test:evals) — a token-guarded eval endpoint (/api/evals/run) runs each live agent against a real fixture entity; the runner asserts hard invariants: valid artifact structure (8 MEDDPICC dims, scores from the enum, every non-unknown score carries evidence; brief sections within bounds; extraction caps), sane verdict, run totals == the llm_calls ledger exactly, and a per-run cost ceiling ($0.10). All 4 evals passed — Meeting Prep even exercised the REPAIRED path live. - CI (
.github/workflows/ci.yml) — typecheck + lint on every push; e2e/evals run locally (need the dev DB + key + server).
Next tiers (planned): golden-fixture grounding snapshots per agent (deterministic, no-API), the 8–10 money-flow tests (peek, link-recording, run-agent with a mocked client, close-on-activity, handoff accept), nightly evals with trend tracking off agent_run_summary.
Agents19:12 ISTThe agent team gets plain-English names
"Witness" and "Qualifier" mean nothing to a salesperson — the name should say what the agent does. Renamed the whole team and propagated everywhere: Daily Brief (Briefer) · Email Drafter…
The agent team gets plain-English names
"Witness" and "Qualifier" mean nothing to a salesperson — the name should say what the agent does. Renamed the whole team and propagated everywhere: Daily Brief (Briefer) · Email Drafter…
"Witness" and "Qualifier" mean nothing to a salesperson — the name should say what the agent does. Renamed the whole team and propagated everywhere: Daily Brief (Briefer) · Email Drafter (Writer) · Call Notes (Witness) · Deal Check (Qualifier) · Meeting Prep (Researcher) · Promise Tracker (Chaser); planned: Meeting Minutes (MoM), What's Next (Sorter). Single source of truth: src/lib/ai/agents.ts. Propagation: every agent writes the new name into agent_run_summary.agent_name + audit actorLabel; all UI copy (panel toasts, buttons, receipts, StubButton hints, "Copilot draft" → "Draft email"); agent system prompts; 115 telemetry rows migrated to the new names. One deliberate exception: historical audit_events rows keep the labels they were written with — the audit trail is append-only and history is never rewritten.
Agents18:33 ISTThe Chaser: promises tracked, kept, chased (agent #6) + the lead thread gets the matcher
People commit to things on threads and calls — "we'll send the proposal by Friday", "Vikrant will share it with the US team" — and those promises evaporate. Two ships close #13 completely:
The Chaser: promises tracked, kept, chased (agent #6) + the lead thread gets the matcher
People commit to things on threads and calls — "we'll send the proposal by Friday", "Vikrant will share it with the US team" — and those promises evaporate. Two ships close #13 completely:
People commit to things on threads and calls — "we'll send the proposal by Friday", "Vikrant will share it with the US team" — and those promises evaporate. Two ships close #13 completely:
The Chaser (src/lib/ai/agent-chaser.ts): reads an entity's deterministic sources — the matched email thread (full bodies) + the recorded calls' action items — and extracts each concrete commitment: crisp text, who made it, which side (WE/THEY), the due date only when one was actually stated (resolved against that source's date, never invented), and the exact source it came from. A fresh-context critic drops anything the sources don't support (extraction filters, it never repairs). Rows land in `commitments` with full provenance (source email/recording + agent run id; a dedupe index makes re-scans idempotent). The Promises panel (promises-panel.tsx) sits on the AE deal hub and both lead hubs: WE/THEY typeset rows, due chips (amber → rose when overdue), source links into the recording page, and in-place kept / dismiss — REAL writes to our table, audited as COMMITMENT_FULFILLED/COMMITMENT_DISMISSED (not previews). Verified live on centaurihs: 7 promises from 3 emails + 2 calls (6 ours, 1 theirs — Vikrant taking Antino to his US counterparts), all due dates correctly resolved to the Jul-14 proposal deadline, critic verdict FILTERED (it dropped an unsupported one), ₹0.79. 6 of 8 agents live.
The matcher reaches the lead thread (#13's leftover): getLeadTimeline now uses the same 4-branch corporate-domain matcher as the deal thread — exact + company-domain mail in ONE indexed pass, with a per-row exact flag driving "with this lead" vs "team thread @domain" (the old code only fell back to domain when exact matched nothing, hiding team threads entirely — SwiffyLabs' finance-team invoices now show on Ashish's lead). And a hazard found before it shipped: a lead at microsoft.com would have dragged 400+ dmarcreport@/no-reply@ notifications into their timeline — new NOISE_LOCALPARTS filter on the from-domain branch (431→15 real rows), exact-address matches untouched, deal side inherits the fix.
Agents18:17 ISTThe Researcher: the pre-call brief (agent #5)
Walk into every meeting prepared. On the meeting-event page, "Brief me" → the Researcher gathers deterministic grounding (who the external attendees are in the CRM — lead/contact → their…
The Researcher: the pre-call brief (agent #5)
Walk into every meeting prepared. On the meeting-event page, "Brief me" → the Researcher gathers deterministic grounding (who the external attendees are in the CRM — lead/contact → their…
Walk into every meeting prepared. On the meeting-event page, "Brief me" → the Researcher gathers deterministic grounding (who the external attendees are in the CRM — lead/contact → their deal + account; the recent email thread both directions; the last recorded calls' recap + action items/risks) and writes the brief: headline goal · who's in the room · where it stands · still open · lead with · ask them. Same guarantees as the rest of the team: a fresh-context critic flags any fact not in the grounding (one repair round), meeting_briefs stores one row per (mailbox, meeting), agent_run_summary + AGENT_RESEARCHED audit event, receipt on the panel.
Verified live on the Right Horizons negotiation meeting — the brief nailed the deal's actual shape from 5 recorded calls + the thread: "Bridge the 150% budget gap… reworked two-phase proposal that fits their 25-30L constraint"; who: "Sunil Kawariya — decision-maker; firm on 25-30L, rejected three-phase model"; still open: Anil's internal greenlight; ask: "Has Anil signed off on the Phase 1 scope?". Files: src/lib/ai/agent-researcher.ts, scripts/agents/create-researcher-table.mjs, event-page actions.ts + researcher-panel.tsx. 5 of 8 agents live.
Agents18:06 ISTThe Qualifier: MEDDPICC scored from real evidence (agent #4)
The deal hub's empty MEDDPICC scorecard is now filled by a real agent. Same harness as the agentic Writer (tool loop → fresh-context critic → repair), new job: score the 8 MEDDPICC…
The Qualifier: MEDDPICC scored from real evidence (agent #4)
The deal hub's empty MEDDPICC scorecard is now filled by a real agent. Same harness as the agentic Writer (tool loop → fresh-context critic → repair), new job: score the 8 MEDDPICC…
The deal hub's empty MEDDPICC scorecard is now filled by a real agent. Same harness as the agentic Writer (tool loop → fresh-context critic → repair), new job: score the 8 MEDDPICC dimensions with the evidence that earned each score.
- `src/lib/ai/agent-qualifier.ts` — INVESTIGATE via the deal tool belt, then emit strict JSON (per-dimension score
unknown|weak|partial|strong+ evidence, headline, gap, suggestedFocus). Guardrails: a non-unknown score with empty evidence demotes to unknown; JSON extracted substring-wise; a re-emit round recovers truncated output (the first live run failed exactly here — too-small max_tokens + a strict parse; fixed). The critic audits that scores are EARNED ("strong needs explicit confirmation, not a passing mention"), one repair round. - Stored in
deal_qualifications(one row per deal, latest wins;scripts/agents/create-qualifier-table.mjs) +agent_run_summary+ anAGENT_QUALIFIEDaudit event; RACI-gatedrunQualifieraction. - `qualifier-panel.tsx` — headline read, Biggest gap, 8 score rows (dot-colored, evidence expands per row), a clay Next move, Run/Re-qualify, and the auditable-AI receipt (verdict · tokens · ₹).
Verified live on the centaurihs deal (the very deal linked via the new in-place connect — full circle): 14 tool calls (bundle×1, email×3, call-intel×2, transcript-search×8), verdict PASS, ₹1.47. The read is blunt and grounded: "Early-stage prospect with implicit need but no urgency, incumbent entanglement, and US-based economic buyers — poor fit for short-cycle deal"; Competition scored strong quoting the Jul-1 call (two incumbent EOR vendors, 3 offers extended); next move: "Before sending the proposal due Jul-14, confirm who at Centauri HQ will evaluate it and secure a warm introduction — otherwise proposal dies in inbox."
Agents15:53 IST#10: the Writer is AGENTIC — tool loop + critic, live on the AE inbox
The Copilot draft is no longer a pre-stuffed prompt: it's a real agent. Built on the official SDK's Tool Runner (client.beta.messages.toolRunner, betaTool) — deliberately NOT the "Claude…
#10: the Writer is AGENTIC — tool loop + critic, live on the AE inbox
The Copilot draft is no longer a pre-stuffed prompt: it's a real agent. Built on the official SDK's Tool Runner (client.beta.messages.toolRunner, betaTool) — deliberately NOT the "Claude…
The Copilot draft is no longer a pre-stuffed prompt: it's a real agent. Built on the official SDK's Tool Runner (client.beta.messages.toolRunner, betaTool) — deliberately NOT the "Claude Agent SDK" package, which is Claude-Code-as-a-library (subprocess + filesystem tools; wrong fit inside a Next.js server action). The Tool Runner is the SDK's model-driven tool loop over tools we define — genuinely agentic, in-process.
- `src/lib/ai/tools.ts` — the Writer's tool belt, READ-ONLY and deal-scoped by construction (ids resolved at build time;
get_emailrefuses ids off this thread):get_deal_bundle(facts + thread INDEX),get_email(full body + attachments),get_call_intelligence(recap/action-items/risks),search_transcripts(FTS over the deal's linked call transcripts — the GIN index earning its keep). Every call is logged for the receipt. - `src/lib/ai/agent-writer.ts` — INVESTIGATE→DRAFT (toolRunner, Haiku per the model dial, max 8 iterations) → CRITIC (fresh context, judges the draft against the evidence the tools actually returned: no invented facts/numbers/commitments, exactly one ask) → one repair round on failure. Telemetry row per run in
agent_run_summary(model/tokens/cost/verdict). - Receipt in the UI — the toast now says what the agent did: "Writer investigated (deal bundle×1 · email×3) · critic flagged & repaired".
- Verified live on the GasTech deal: agent chose to open 3 emails, critic flagged, repair fixed it; grounded draft with one clear ask. ~9.9k tokens in / 344 out ≈ ₹1 per draft on Haiku.
Also this stretch: grounding.ts (deep thread grounding — full email bodies, meetings, call intel, attachments + receipt) now powers the SDR lead Writer; the deal Writer's fetchers became the agent's tools, as planned.
Agents15:21 ISTMarkdown renderer for user/AI-authored text (+ transcript backfill complete)
"Markdown to html is not working" — traced to two real homes: (1) lead descriptions carry AI-enriched markdown ( bullets, bold — e.g. the Target/Bandhan leads), and (2) one transcript turn…
Markdown renderer for user/AI-authored text (+ transcript backfill complete)
"Markdown to html is not working" — traced to two real homes: (1) lead descriptions carry AI-enriched markdown ( bullets, bold — e.g. the Target/Bandhan leads), and (2) one transcript turn…
"Markdown to html is not working" — traced to two real homes: (1) lead descriptions carry AI-enriched markdown (* bullets, **bold** — e.g. the Target/Bandhan leads), and (2) one transcript turn where the prospect pasted formatted requirements into the meeting chat and Proshort merged it into the transcript. (The Proshort long fields/insights have zero literal markdown — checked all 548.) Built src/components/markdown.tsx: a tiny dependency-free renderer (bold/italic/code/links/headings/bullet+numbered lists) that outputs React nodes, never injected HTML (XSS-safe by construction), styled on the DESIGN text scale (clay-dot bullets, mono numerals, semibold ink emphasis). Wired into: transcript turns (recording page), AE + SDR lead descriptions, and the deal "What they want" panel. Verified on the Target lead + the InfraKeys transcript.
Also: the transcript backfill finished — 498/548 recordings now have speaker/timestamped transcripts (50 genuinely empty, 0 failed).
Agents14:34 ISTThe Witness: notes write themselves from the call recording
"Add note" was a hollow placeholder string. Replaced it with the Witness — on a deal, ✨ Draft from last call composes a real minute-of-meeting from the deal's most recent Proshort…
The Witness: notes write themselves from the call recording
"Add note" was a hollow placeholder string. Replaced it with the Witness — on a deal, ✨ Draft from last call composes a real minute-of-meeting from the deal's most recent Proshort…
"Add note" was a hollow placeholder string. Replaced it with the Witness — on a deal, ✨ Draft from last call composes a real minute-of-meeting from the deal's most recent Proshort recording: recap + action items + risks + pain points + competition. Deterministic (src/lib/notes/recording-note.ts composes from the already-extracted proshort_recording_insights — no LLM, so it never invents; every line traces to the transcript). Matched to the deal via its buying-committee contacts or the account name. draftDealNote action + a proper note-compose panel in the deal rail (editable textarea, toast acknowledgement, Save). Preview until Zoho write-back. Verified live on centaurihs (Jul 10 call → recap + 4 action items + 3 risks + competitor). Next: same for leads + auto-fill on "Log call".
Agents13:25 ISTFollow-up cleanup: clear what's already done (+ briefing below the greeting)
The overdue-task mountain (19.5k team-wide) is mostly uncleared, not undone — reps did the work and never ticked the box. Built an evidence-based cleanup that recognises done/moot…
Follow-up cleanup: clear what's already done (+ briefing below the greeting)
The overdue-task mountain (19.5k team-wide) is mostly uncleared, not undone — reps did the work and never ticked the box. Built an evidence-based cleanup that recognises done/moot…
The overdue-task mountain (19.5k team-wide) is mostly uncleared, not undone — reps did the work and never ticked the box. Built an evidence-based cleanup that recognises done/moot follow-ups and clears them for the rep, reversibly.
- `task_completions` overlay — Zoho write scope isn't granted, so we don't fake the mirror; a completion lives app-side and every open-task query LEFT-excludes it (cleared tasks vanish immediately). Becomes the write-back outbox when scope lands.
- `getTaskCleanup(zid)` (deterministic — no LLM decides a close): matches each overdue task to its lead (subject-based, like getTasks) AND deal (what_id), then buckets: Moot (deal closed / lead lost/converted), Looks already done (lead or deal moved after the task was due, incl. an email off the CRM — the exact "did it, never logged it" case), Stale (>90d — suggest, don't auto). Each row carries its evidence.
- `completeTasks` action — bulk clear → overlay + ONE audit event (reversible, PREVIEW).
- `CleanupPanel` on
/sdr/tasks: three bucket cards with evidence + "Clear N". Honest by design — it clears only what it can prove (~4-5% here; the CRM lacks evidence for the rest, which correctly stay). The durable fix remains close-on-activity (prevention), noted for next. - Layout: the daily briefing now sits below each cockpit's greeting/KPI header (AE Desk, SDR My Day, Command) — greeting first, then the brief.
Agents12:24 ISTThe Briefer: the daily briefing agent (first of the agent team) is LIVE
The API key landed (valid — verified), so the agent program is unblocked. Built the Briefer end-to-end and put it as the first thing on the Desk / My Day / Command board.
The Briefer: the daily briefing agent (first of the agent team) is LIVE
The API key landed (valid — verified), so the agent program is unblocked. Built the Briefer end-to-end and put it as the first thing on the Desk / My Day / Command board.
The API key landed (valid — verified), so the agent program is unblocked. Built the Briefer end-to-end and put it as the first thing on the Desk / My Day / Command board.
- Storage — two app-owned tables (
src/db/schema.ts+scripts/briefings/create-tables.mjs):agent_run_summary(per-run telemetry every agent writes — model/tokens/cost/verdict; the auditagent_run_idpoints here) andbriefings(one row per user per day;sectionsJSONB;unique(user_id, briefing_date)). - Section ownership done right —
src/lib/briefing/facts.tscomputes every NUMBER in SQL, role-scoped (AE/SDR = their book; HEAD_OF_SALES/ADMIN = team roll-up).briefer.ts(Haiku) writes prose only; the schedule (today's meetings, exact IST times), at-risk deals, and rep lists are rendered deterministically from facts — the model never touches a time or amount. This fixed the first-cut bug where the model narrated meetings (wrong/empty bullets). - Surface —
DailyBriefingcomponent: headline + aligned mono lists (time/amount lead) + prose sections + a highlighted "Your one move" + an auditable-AI receipt (agent · model · "numbers computed from your live CRM"). Lazy-generates on first view;getAnthropic()now prefersprocess.env.ANTHROPIC_API_KEY. - Batch —
POST /api/briefings/generate(token-guarded, the cron target). Generated 17/17 live briefings: Rohit (team: "₹42.3 Cr slipping… urgency starts with Himanshu and Harshad") + every mapped SDR/AE. Reads like a real chief-of-staff.
Format: bullets everywhere. Every section is now short bullet points (the model writes 2-4 one-sentence bullets per section, never paragraphs); data lists lead with a mono time/amount, narrative bullets with a clay dot. Regenerated all 17.
Enrichment + clickable links. Three data sources added to the facts layer (all numbers/names still code-computed): (1) meeting prep — each today's meeting is matched to a deal via an external attendee → shows "— FedX · Qualification · no next step"; (2) "Waiting on your reply" — the actual unanswered threads (lead + subject + days-waiting), not just a count; (3) "Follow up from your recent calls" — Proshort call intelligence (open action items + ⚠ risks from the last 10 days of recorded calls the user was on). Every list item now carries a ref and renders as a link (role-aware): meetings → the event page, deals → the deal hub, threads → the lead, calls → the recording. Verified: clicking a meeting opens its detail (correct 16:00 IST, real subject).
Still to do: the pre-dawn cron trigger + the rest of the agent team (MoM, Sorter, Qualifier, Researcher, Chaser) + the critic pass. #11 is now in progress (Briefer shipped).
12
Jul 2026
Sunday
2 shipped
Agents18:46 ISTAudit + notify spine v2: actor-kind (agents!), outcome, severity, the wrapper
Diagnosed the "fucked up" audit/notify: the code was well-built but the spine couldn't log agents (it required a User actor; null just meant "system"), had no outcome/severity/summary, and…
Audit + notify spine v2: actor-kind (agents!), outcome, severity, the wrapper
Diagnosed the "fucked up" audit/notify: the code was well-built but the spine couldn't log agents (it required a User actor; null just meant "system"), had no outcome/severity/summary, and…
Diagnosed the "fucked up" audit/notify: the code was well-built but the spine couldn't log agents (it required a User actor; null just meant "system"), had no outcome/severity/summary, and covered only the identity/admin half — every sales + agent action wrote nothing. Rebuilt the foundation:
- Schema (
audit_events+notifications, viaALTER TABLE, neverdb:push):actor_kind(USER|AGENT|SYSTEM),actor_label(person or agent name),outcome(SUCCESS|FAILURE|PARTIAL|PREVIEW — for propose-not-act),summary(a rendered human sentence — what the trail shows),agent_run_id; notifications gainseverity(info|warning|critical) +cta_label/cta_href. - `recordAudit` now takes actorKind/label/outcome/summary/agentRunId and is fire-and-forget (swallows errors, returns
number | null) so a logging failure never breaks the action.notifygains severity + CTA and is fire-and-forget too. - `recordAuditAndNotify()` — THE spine wrapper (antino3's SyncAudit pattern): one call = one audit row + notifications with a shared vocabulary (outcome→severity). `recordAgentRun()` — logs an AGENT action attributed to the user it ran for.
- Wired the Writer agent: every draft (success/empty/error) now writes an
AGENT_DRAFTED_REPLYaudit row (actorKind AGENT, label "Writer", outcome, char-count). Registry (audit-actions.ts) gained the sales + agent action codes + ActorKind/Outcome/Severity enums. Verified end-to-end (agent event + severity/CTA notification insert). Then made it visible + gated:/admin/auditnow surfaces the new detail — an actor-kind glyph (agent = violet sparkle, system = cpu, user = person), a colored outcome chip (shown when not SUCCESS), and the summary sentence on its own line. Built the RACI access helper (src/lib/auth/access.ts):canActOnOwner/requireActOnOwnergate write-actions by admin-tier · ownership (Zoho mapping) · manager hierarchy (recursive report tree) — unauthorized →notFound(); reading stays open. Then wired the deal action rail to log every user action:src/app/ae/deals/[id]/actions.ts(logStageAdvance/logDealNote/logCall) — eachrequireRole('AE')→ looks up the deal owner →requireActOnOwner(404 if not owner/manager/admin) →recordAuditAndNotify({ outcome: 'PREVIEW', … })(audit row + a manager notification with a CTA). The deal-hub Stage-gate Advance button + the Advance / Log call / Add note rail buttons now call these server actions (client toast reflects the logged result) instead of a bare client toast. Typecheck-verified; the spine itself was DB-smoke-tested. Live click-through pending a fresh sign-in (I can't authenticate). Then wired the SDR lead action rail the same way (src/app/sdr/leads/[id]/actions.ts): Send email / Log call / Book meeting / Qualify / Add note now call RACI-gated server actions logging PREVIEW events + notifying the SDR's manager. Qualifying fires the SAL handoff (SAL_HANDOFF_SENT) — the seam antino3 calls the most important — and Unqualified logsLEAD_DISQUALIFIEDwith the reason code. #9 complete: every user action (deal + lead) and every agent run is now logged in detail, RACI-gated, with severity/CTA notifications. (SAL accept/reject on the AE side + the SLA engine belong to #8.)
Agents09:09 ISTFirst AI: the "Writer" agent + task accordion + hover cards
First real Claude call in the app. @anthropic-ai/sdk installed; src/lib/ai/anthropic.ts builds the client from the encrypted ANTHROPIC_API_KEY setting (cached until the key changes);…
First AI: the "Writer" agent + task accordion + hover cards
First real Claude call in the app. @anthropic-ai/sdk installed; src/lib/ai/anthropic.ts builds the client from the encrypted ANTHROPIC_API_KEY setting (cached until the key changes);…
- First real Claude call in the app.
@anthropic-ai/sdkinstalled;src/lib/ai/anthropic.tsbuilds the client from the encryptedANTHROPIC_API_KEYsetting (cached until the key changes);src/lib/ai/draft.ts= Writer, which drafts a reply grounded in the lead's real email thread + Zoho record (Haiku 4.5, read-only). Server actiondraftReply(leadId)insrc/app/sdr/ai-actions.ts; the Inbox "Copilot draft" button now calls it (loading state, toast, graceful error) instead of a template. - Verified the path is real: clicking it fires a genuine API call and returns a proper Anthropic response. The stored key is currently an invalid 24-char placeholder (
sk-ant-tes…, updated 2026-07-07) so it 401s — a valid key in Admin → Settings unblocks it, no code change. - Agent framing: the SDR gets a team of simple-named agents — Writer (live), Briefer / Sorter / Qualifier / Researcher / Chaser (planned) — all Haiku, all propose-not-act, user in the center. Write-back (send via Graph, CRM update via Zoho) is a separate, deliberate switch.
- Follow-ups → accordion.
getTasksnow resolves each task to its lead via a lateral join (subject parse, since who_id is null), so tasks carry a hoverable/linkable lead. Newsrc/lib/sdr/task-types.tsclassifies each task (follow-up / status / future / notes / data / linkedin — grounded in a scan of the real subjects, ~8k are "Priority Action" bot tasks); the row expands to the right "how to finish it" actions per type. All preview + toast. - Universal `LeadHoverCard` (
src/components/sdr/lead-hover-card.tsx) — hover any lead name to preview contact/status in the lead entity hue + jump to the record; the pattern extends to deal/contact on the AE surface.