60 · Gateway · PRD

PRD — The Gateway Push: closing the /60 delivery gaps

Repo: SixtySecondsApp/use60-mcp-gateway (Railway, mcp.use60.com; chat brain in lab/ — server.mjs, brainAnswer.mjs, chatStream.mjs; type registry src/mcpToolResponseTypes.mjs). Staging: use60-mcp-gateway-staging.up.railway.app, branch staging.

Why now. The /60 V3 front-end is done: a 3-track audit (contract coverage, adversarial code review, live behavioral harness) plus two fix waves mean the app now renders every response type the gateway can emit, restores conversations, and never hangs. The remaining user-visible failures are all gateway-side. Evidence base: the 43-query harness (scratchpad/fe-harness.js, re-runnable) scored 17/43 genuinely delivered — 25 of the 26 gaps are gateway routing/data. Andrew's two manual QA reports agree.

Prime directive (inherited from PR #88): grounded answers, degrade-open, confabulation = 0. Every epic's acceptance runs the eval battery (v3app/eval/run-eval.mjs) — no dimension may regress — plus the fe-harness for delivery.

Filed issues covered: #2442 currency, #2443 stale-deals routing, #2449 write execution, #2450 time-range rollups, #2451 empty records, #2458 anaphora, #2459 upcoming-vs-past. App-side contract already handles: ledgers (rows/items/records/results/deals/activities/commitments/contacts), single-object detail rows, confirmation cards for action_summary/*_creation, approval envelopes, drafts, charts. The gateway only has to return the right type + shape; rendering is solved.


Epic G1 — Record reads: real rows for deals/contacts/companies (#2451, #2443) — BIGGEST WIN

Problem. sixty_search_records delegates to get_deal/get_contact/get_company which return {deals:[],total:0} on prod despite 404 open deals. Every deal/record query then falls back to pipeline_summary stage rollups → the user sees bar charts where they asked for rows ("show me a table of my deals", "deals over 50k", "what's in Verbal", "stale deals", "recent contacts", "my customers"). Design. Fix the delegated lookups (likely org-scoping/filter bug — sixty_get_pipeline_summary sees the same org's 404 deals, so the data is there). Then add proper list routes in the chat brain: deal-list intents (filters: stage, value, staleness, close-date) → responseType:'crm_search_results' with {results:[{name,value,stage,daysStale,owner,closeDate,id}...]}; same for contacts/companies. "Tell me about " → deal_detail single object. "Stale deals"/"gone quiet" = deal-list with a staleness filter (NOT pipeline_summary). Acceptance. fe-harness: the 9 WRONG_ARCHETYPE queries flip to ledger/detail with real rows; sixty_search_records(deal) returns ≥1 row on the Sixty org; eval no-regress. Fixes #2451+#2443 and, with G4, most of Andrew's report.

Epic G2 — Anaphora + conversational context (#2458)

Problem. The client sends the last 12 turns; the classifier ignores them. "Tell me more about that deal" / "draft an email to its owner" → connect-workspace fallback. This is the #1 conversational-quality bug. Design. Pre-classification anaphora resolution: when the message has pronouns/deictics (that/it/them/this one/the second) and history exists, resolve against entities in the last N answers (the gateway already returns entities per turn — persist them per conversation or re-extract from history). Rewrite the message internally ("tell me more about Adom Tech") then route normally. Confab guard: if resolution confidence is low, ask a clarify (responseType:'clarify' — the app renders choice chips) rather than guessing. Acceptance. New eval RECALL sub-battery (5 anaphoric follow-ups seeded from real entities) scores ≥2 each; the harness multi-turn flow (turn1 risk-deal → turn2 "more about that deal" → turn3 "draft to its owner") delivers on all 3 turns; confabs stay 0.

Epic G3 — Writes that execute (#2449)

Problem. Voice/chat can read everything but writes don't land: calendar add didn't execute; "create a task" returns fallback; "book a follow-up" falls back. The write_workflow propose path works (approval envelope verified) — the gaps are (a) routing write intents INTO the workflow and (b) executing on approval. Design. Route create-task / add-event / book-meeting / draft-email intents → sixty_run_write_workflow propose → approval envelope (app renders the card, approve round-trips already). On approval: execute against the real domain (tasks table, calendar via google-direct, Gmail draft per the write-approval model — email NEVER sends, drafts only). Voice: same path; spoken confirmation of the proposal, execution still requires the card approval (per the approval model — no voice-only writes). Acceptance. Live round-trip on prod (Sixty org): "create a task to follow up with Adom Tech" → approval card → approve → task exists via sixty_list_tasks; calendar add → event visible in sixty_get_upcoming_events; draft-email → Gmail draft exists, nothing sent. fe-harness write checks flip to approval/confirmation.

Epic G4 — Time-range aggregation (#2450, #2459)

Problem. "Summarise the last 7 days" returns ONE meeting ("1 analysed"); "what meetings are coming up" returns past recorded calls; "what changed today / this week" → fallback. Design. A time-window resolver + rollup route: parse ranges (today/yesterday/this week/last 7 days), fetch ALL meetings in-window, aggregate (count, key decisions, commitments, notable calls) → meeting_list rack of the window's meetings + a rollup headline. "Coming up" phrasing → sixty_get_upcoming_events (calendar), never recent recordings. "What changed" → a digest route over the window (meetings analysed, deals moved, tasks closed — reuse the brief's building blocks). Acceptance. "Summarise the last 7 days" returns N≥all analysed meetings in the window as a rack; "coming up" returns only future events; "what changed today" produces a digest (no fallback); eval FRESHNESS ≥ current.

Epic G5 — Currency + polish (#2442 + receipt-quality)

Problem. Headlines say "$1.1m" while the org is GBP (chips correctly show £). Plus assorted summary-quality nits (stringified-JSON summaries reaching summary fields — the app now defends, but the gateway shouldn't emit them). Design. Org currency threaded into the writer/formatter (org settings has currency; format all money in reply/spokenSummary with it). Stop emitting raw JSON strings in summary/card-body fields — always prose. Acceptance. fe-harness: zero "$" in answers for the GBP org; zero RAW_JSON flags; eval no-regress.

Epic G6 — Honesty hardening (fictional-deal confab)

Problem. "What did the Nonexistent Fictional Corp deal close at" → a pipeline chart (implies the deal exists). The meeting-side honesty gate works; the deal/records side doesn't. Design. Extend the confabulation gate to CRM entities: unknown deal/company/contact names → honest absence ("no deal by that name — closest I have is X, want that?"), never a generic chart. Acceptance. Eval HONESTY stays ≥ current with 2 new CRM traps at 3/3; harness honesty checks: both probes decline.

Epic G7 — Prospecting + analytics routes (fallback family)

Problem. "Find leads like my best customers", "find 20 prospects in my ICP", "what's my win rate", "am I ready for my next meeting" → connect-your-data fallbacks even where data exists. Design. (a) Win-rate/analytics: compute from the deals table (won/lost by period) → chart. (b) Meeting-readiness: join next event + prep/transcript/deal state → judged answer (the Brain-Era E5 pattern). (c) Prospecting: if external sources aren't connected, return an honest capability answer WITH a next step (approval card to start from existing pipeline) instead of a bare fallback. Full prospecting (Exa/Apollo) is out of scope here — tracked in the Brain-Era PRD. Acceptance. Win-rate returns a real chart; readiness returns a judged answer; prospecting returns the guided answer (no bare fallback). fe-harness FALLBACK count for these ≤1.


Sequencing & effort

Order Epic Size Unblocks
1 G1 record reads M 9 harness queries + half of Andrew's report
2 G4 time-range rollups M meetings family
3 G2 anaphora M conversational feel
4 G3 writes execute L the "Do" half of the product
5 G5 currency/polish S trust in numbers
6 G6 honesty CRM S confab=0 everywhere
7 G7 analytics/prospecting M remaining fallbacks

Rules for the implementing agent(s): work in the gateway repo on a feature branch off staging→ verify with the local-gateway eval loop (PR #88 methodology) + the app's fe-harness pointed at the staging gateway; attach before/after eval + harness tables to every PR; degrade-open everywhere; no battery-entity hardcoding; deploys are a human decision. The app-side contract is FROZEN — return the types/shapes in mcpToolResponseTypes.mjs and the app renders them.

North star: fe-harness ≥ 40/43 delivered · eval Memory ≥95 / Brain ≥95 · confabs 0 · a real write round-trip on voice and chat.

Prepared 8 Jul 2026 · Fable · companion docs: /60-v2 wiring audit · Brain-Era PRD · coverage matrix (audit-coverage, this session).