Docs

Changelog

RSS

All notable changes to the Amdahl Platform API are recorded here. The format follows Keep a Changelog: each entry groups its changes under the sections that apply (Breaking, Added, Changed, Deprecated, Removed, Fixed, Security) and omits the ones it does not use. Breaking comes first and flags anything that needs action on your side. Dates are the day the change landed on the stable path.

Last updated 2026-07-23

Filter
Breaking
  • Saving CRM Mappings with a deal_amount_tiers, segment_rules, default_segment or use_crm_segment key is now rejected. Existing saved settings still load — the retired keys are ignored — but a caller that builds its own payload must drop them.
Added
  • Deal amount unit. No CRM records what period its amount field is in — Salesforce Amount, HubSpot amount and Pipedrive value are bare numbers — so a workspace that tracks deals monthly had its deal values read as though they were annual. You can now declare what your amount means, per CRM source, under Settings → CRM Mappings (monthly, quarterly or annual), along with its currency. Deal and interaction queries carry that declaration beside the value as deal_amount_unit / deal_amount_currency, and the assistant reads it — so an answer says "$3,750 monthly" rather than a bare "$3,750". Your numbers are never touched: deal_amount still shows exactly what your CRM shows. We surface what it means instead of converting it, because there is no honest conversion — a one-time implementation fee isn't a rate, and only you know whether your amount is a subscription or a whole contract. Until you declare a unit it reads unspecified, so a figure is reported as unknown-basis rather than assumed annual. Onboarding now offers this as a quick (skippable) step, and your home-view setup checklist keeps a reminder open until every connected CRM has a unit — so it is easy to set later if you skip it. We never guess the value for you: it stays "Not declared" until you choose one.
  • `firmographic_segment` on interaction and deal queries: SMB / mid-market / enterprise from company headcount and revenue. It abstains rather than guess when a company's size straddles a band boundary.
  • New knowledge_base.get_node_widget operation (GET /knowledge-base/:id/node/:node_key/widget) returns one living-doc node's figure as a standalone single-visualization PageSpec, so a pinned chart or stat can render as an embeddable widget card — regenerated on read to stay in sync with the doc.
  • Pre-sync call filters (compliance control). A compliance-sensitive workspace can now ensure that non-qualifying call transcripts are never fetched into Amdahl in the first place — not filtered out after the fact, but never pulled over the wire. On a supported calls connector (Fathom, Fireflies, Gong, Grain), open the connection's Call filters section and choose the rules a call must match to be ingested: *external only* (the call has at least one participant outside your own email domains) and/or *includes a rep* (one of the people you list attended or hosted it). A call is ingested only if it matches every rule you set; a call that matches none is never fetched. Your own domains are added automatically, so you never type them in. A calls source stays paused until you save a filter — it will not sync a single call until the rule is in place — so ingestion can only ever begin with the control applied. The filter is inclusion-based and fails safe: if a call's roster can't be determined, it is excluded. (Granola support is coming in a follow-up; it is not offered as filterable yet.)
  • Personalized first wins in developer onboarding. New developers now start from a short list of first-win cards computed from the workspace's own data — the top account by conversation volume, the loudest conversation theme, and a named competitor from the company profile — instead of a generic template. Picking a card opens Search or Chat with the ask prefilled, so the guided tour begins on a real question about your own pipeline. Workspaces whose data has not landed yet get safe generic cards, so onboarding never blocks on thin data.
  • `external_only` on `context.query_substrate`. Restricts returned evidence to what the other side of the conversation said — customers, prospects, partners — and drops your own reps' messages and internal email. Set it for voice-of-customer, ICP, persona and positioning questions, where an internal message is evidence of what you say, not of what a buyer thinks. Defaults to false, so existing calls are unchanged.
  • Call filters can now EXCLUDE calls, not just include them. The pre-sync call filter gained an exclusion carve-out: exclude_participants (emails) and exclude_domains (whole domains, suffix-aware) on connections.set_call_filters and the atomic connections.connect call_filters. Any call where a blocked email or domain is an attendee/host is never ingested — its transcript is never fetched — and exclusion is evaluated before the include rules, so a blocked party always wins over an otherwise-qualifying call. Exclusion is a valid standalone choice ("ingest every call except anything involving @sensitive-client.com") and composes with the existing external-only / rep rules. It keeps the same fail-closed guarantee: an unresolvable roster still drops the call (we can't prove the blocked party is absent). Existing filters are unchanged.
  • Connecting a source now checks the credential against the provider — and shows you what it found. Until now, connecting with an API key stored the key and marked the source connected without ever calling the provider, so a wrong or expired credential stayed silent until a sync failed. Connecting Salesforce, HubSpot, Gong, Salesloft, Aircall, Fireflies, Fathom or Grain now makes one read-only call to the provider and records what it saw. The connection's summary (GET /connections/:id/summary) shows that evidence right away — "12,400 deals, latest yesterday" — instead of an empty card while the first sync runs, and the account name the credential opened, so a valid key pointed at the wrong account is obvious. The numbers are exactly what the provider reported, marked as such: a provider that will not give a total shows its date range rather than a made-up number, an approximate or capped figure is labelled approximate, and a real zero is shown as zero. Once your data lands, the summary switches back to your own synced data. Nothing is read that was not already read by the sync, no message or meeting content is stored, and a failed check never blocks connecting.
  • Fast Search is faster and more reliable on the common GTM asks. Recurring questions — competitor mentions by account, the rival landscape and win rate, objections, pain points, feature requests, champions, economic buyers — now run over the signal your pipeline already computed (so "which accounts mentioned a competitor in the last 60 days" is a sub-second lookup, not a slow text scan that timed out). Anything outside that set still writes SQL for you, but that call is now bounded and tolerant, so a stray bit of model output no longer aborts the search.
  • Every Search result now carries a `coverage` objectlatest_event_at, latest_ingest_at, total_rows, days_behind, is_stale — so you can show how current your workspace data is. When your data is materially behind "now" and a recent-window ask comes back empty, the result message says so in plain language, so an empty answer reads as a data-freshness gap rather than a mysterious failure.
  • Map your CRM stages while the first sync is still running. Connecting a CRM now fetches its reference objects — pipelines, stages, owners, field definitions — within seconds, so the stage-mapping review is ready immediately instead of waiting for the full sync to land. A new GET /console/crm-metadata returns the catalog, and mappings are seeded from it as candidates you review rather than facts we assume. Seeded entries never count as confirmed until a human has actually looked at them, and a follow-up pass picks up any stage that appears on old deals but no longer exists in the CRM's current catalog.
  • OAuth discovery now teaches agents how to onboard themselves. The authorization server metadata at /.well-known/oauth-authorization-server now includes service_documentation and an agent_auth block (per the emerging auth.md convention): where to register a client (open dynamic registration), the identity and credential types to expect (anonymous registration, bearer access/refresh tokens), where to revoke, and pointers to the step-by-step recipe at https://amdahl.co/auth.md and the agent-skills index at https://amdahl.co/.well-known/agent-skills/index.json. An AI agent pointed at either domain can now discover the full register-authorize-connect flow without human help. Purely additive — existing OAuth clients and tokens are unaffected.
  • New per-user pinned-figure operations — pins.list (GET /pins), pins.pin (POST /pins), pins.unpin (DELETE /pins/:document_group_id/:node_key) — let a user pin individual living-doc figures (by document_group_id + node_key) to their Home, each rendered standalone via the node-widget read. Personal to the caller (gated by the new pins:read / pins:write / pins:delete scopes); console + REST + Anthropic only.
  • Structured search modes on the API: POST /search/query routes each ask onto typed filters, plain-language search, or meaning-based (semantic) matching over your conversation data — force a lane with mode, or let Amdahl pick. Discover the filterable fields at GET /search/fields.
  • Enrichment endpoints: POST /enrich/company, POST /enrich/person, and POST /enrich/topic return a cached brief instantly when one is fresh, your own first-party evidence while a full refresh runs in the background, or the complete fused brief inline with mode=full.
  • Lookalikes: POST /lookalike finds the companies or deals most similar to a seed account, and POST /lookalike/themes finds the customer-conversation themes closest to any question — both ranked by similarity over your own corpus.
  • Revenue Review living document. A new grounded, always-refreshed report on new-business revenue: closed-won revenue and quarter-over-quarter growth per quarter, plus win rate by deal-size band. Every figure is a computed value the assistant reads back verbatim — the growth rate is calculated in the query itself rather than derived in prose, and each dollar amount carries its CRM period (annual / monthly / …) so a total is never silently re-scaled. It abstains where a band's sample is too small to give a stable win rate instead of reporting a swingy percentage.
  • CRM recency window. When you connect a CRM (Salesforce, HubSpot, or Pipedrive) you can now choose how much activity history to import — a month window (e.g. the last 12 or 24 months) or all history — so you decide up front how far back emails, meetings, calls, notes, and tasks are pulled. Your account, contact, and deal records always land in full; the window only bounds the activity/communications data. It defaults to the last 12 months, and you can change it any time on the connection — widening re-pulls the older activity, narrowing drops what falls outside the window, and reference records are never touched.
  • CRM comms filter (external / rep-involved). For HubSpot connections you can now filter which email activity is imported to only communications that involve an external party (a customer/prospect) or a specific rep — internal-only or off-topic emails are dropped before they ever land in Amdahl. Your account, contact, and deal records are never affected. It's a soft filter (when a message can't be classified it is kept), it's off until you configure it, and changing it re-pulls just the affected email activity to apply the new rule.
  • The assistant's learned data-access rules now reach the fast search-to-SQL writer and connected MCP clients, not just the living-doc author. When Amdahl learns a workspace-specific access rule (e.g. a field substitution or a grain caveat that keeps a query correct), that rule is now projected into every place SQL is written, so answers stay consistent across the console's fast search, the MCP surface, and living documents. Inert until a rule is learned; no change to existing behavior.
Changed
  • Connect a call recorder and set its filter in one step. Connecting a call recorder (Fathom, Fireflies, Gong, Grain) now lets you choose what it ingests in the SAME action as connecting it — one request instead of connect-then-configure. On the API, POST /connections (connections.connect) accepts an optional call_filters for an api-key recorder: pass an enabled filter to keep only matching calls, or { "enabled": false } for the "ingest all calls" opt-out. The filter is fully validated before anything connects, so an invalid choice never leaves a half-configured source, and the recorder starts syncing with your decision already in place. Omit call_filters and nothing changes — the recorder stays paused until you set a filter later, exactly as before. Setting a filter still requires workspace admin.
  • Call recorders: choose what you pull, right when you connect. Connecting a call recorder (Fathom, Fireflies, Gong, Grain) now asks you to decide up front what it ingests: set a pre-sync call filter to keep only calls that match (e.g. calls with an external participant and/or a named rep — everything else is never fetched), or pick Ingest all calls to bring in everything. The source stays paused until you choose, so a workspace that must filter for compliance can't accidentally start pulling calls before it's configured, and a workspace that doesn't want filtering is one click away. Already-connected sources are unchanged, and their manual Sync now works as before.
  • Customer-voice living documents (persona voice deep-dive, voice of customer, cross-persona divergence, proof & ROI) now surface genuine customer and prospect voice. Utterances from non-sales relationships that share a prospect's email domain — investors, advisors, press/media — are filtered out, while a real buyer on such an account is kept via their attached deal. Each voice panel now spreads across many speakers and accounts instead of over-representing whoever happens to have the most high-scoring quotes. data.cluster_search gains an optional facet_filter so themes can be sliced by account relationship (account_relationship) or deal context (deal_status / deal_stage / deal_presence).
  • Deal amounts now carry their period in your reports. The living GTM reports that total or average deal value — Win/Loss, Pipeline Health, ICP Signal, the GTM Health Report and the Weekly GTM Digest — now state the native period of every deal-value figure (e.g. "$1.2M open pipeline (monthly)") and never annualize it, reading the deal_amount_unit you declare per CRM source under Settings → CRM Mappings. A workspace that tracks deals monthly no longer has its report figures read as though they were annual. Where no unit is declared the report calls the basis unknown rather than assuming annual, and where you connect more than one CRM with different units it states both rather than summing across them. Your numbers are untouched — deal_amount still shows exactly what your CRM shows; the report surfaces what it means instead of converting it.
  • Ask about pipeline value in chat and the answer now states the period too. When you ask an Amdahl agent — or your own MCP client — for pipeline or per-stage value from the ready-made funnel rollup (not just the living GTM reports), it now reads the deal_amount_unit you declared per CRM source under Settings → CRM Mappings and states the native period next to the figure (e.g. "$3.3M in the POC stage (monthly)") instead of presenting a monthly figure as though it were annual. A workspace that tracks deals monthly now gets an honest basis on every ad-hoc pipeline answer, matching what Win/Loss, Pipeline Health and the rest of the living reports already do. Your numbers are untouched — the figure is exactly what your CRM shows; the answer surfaces what it means.
  • Workspace domains are active the moment you add them. Adding a company email domain under Settings → Team → Domains used to leave it "Pending verification" with no way to complete the verification — so turning on domain self-join did nothing and eligible teammates never saw your workspace in the join picker. The verification step is gone: every domain you add (and every domain already sitting at pending) is active immediately, and with Domain self-join turned on your teammates can find and join the workspace from signup or the workspace switcher right away. The guardrails are unchanged — public mailbox domains (gmail.com and the like) still can't be claimed, self-join stays off until you opt in, and the workspace owner is emailed on every join.
  • Fast Search (POST /search, the search MCP tool) now handles multi-intent asks. A question that carries more than one independent data intent — "the objections we hit from 11x, and how are deals with Acme going?" — is planned into separate sub-questions, each queried and returned as its own entry in a new groups[] field (the flat internal field is retained as the primary group for existing callers). When part of an ask is not a warehouse question (advice, "what should I say"), the response now carries an escalate_reason pointing to Chat while still returning the data groups it could answer.
  • The public API and MCP surface is now Search + Agents. External callers see two surfaces: Search (search.run) — a synchronous query over your customer conversations, optionally blended with a quick web + news pass — and Agents — the multi-turn Chat ask-door, a reusable agent library, and cron-driven Routines. The MCP server exposes exactly the search and agents coarse tools, and an external credential — a platform API key OR an MCP/SDK OAuth token — can call only the operations behind them over REST; other endpoints now return 403 not_on_public_api. Nothing changes inside the Amdahl console — every workspace surface stays available there.
  • Meaning-based (semantic) search now ranks individual customer utterances, not whole calls. A "sounds like…" / "about X" query on search (query) previously matched at the conversation level and returned one representative snippet per call; it now retrieves the specific buyer-voice utterances closest in meaning to your query, each carrying its own verbatim text and naming the parent call it came from (interaction_id on every match). Matching is scoped to the customer/prospect side of the conversation, so a voice-of-customer search surfaces what buyers said rather than your own reps. Results are drawn from a fresh rebuild of the semantic index at the finer grain — brand-new coverage fills in over the first sync after release.
  • Enrichment briefs (POST /enrich/{company,person,topic}) now stay fresh. Fast mode still returns a cached brief instantly, but it also kicks off a background refresh on every hit, and cached briefs now expire after 24 hours (previously 7–14 days). You get the same fast answer while the underlying brief is continuously kept current.
  • Revenue now reads as true ARR where your CRM records it. The Revenue Review living document and the assistant's revenue math now prefer native annual recurring revenue (from HubSpot's ARR field) and fall back to the deal amount only where ARR is not set — instead of summing the CRM deal amount, which overstates revenue for multi-year contracts (their whole-contract value is not annual). Every figure states its basis (ARR vs deal amount / total-contract value), and the assistant is steered to the ARR-preferring measure when it writes revenue queries. Where ARR is not yet populated the totals are unchanged, and they sharpen automatically as the CRM ARR passthrough fills in.
Removed
  • Deal amount tiers. The hand-authored amount bands under Settings → CRM Mappings (and the deal_amount_category / company_segment_normalized values they produced) are gone. They bucketed a deal amount whose unit was never declared, so they were wrong for any workspace tracking deals monthly. Use firmographic_segment for company size, and read deal_amount with its new deal_amount_unit for deal value.
  • `deal_size_band` is no longer returned by deal and interaction queries.
Fixed
  • The `data` tool no longer mistakes your own company for an account. The schema guidance now states that company_name is the EXTERNAL party on each interaction (the prospect / customer you're talking with), never your own business — every query is already scoped to your workspace. This stops the SQL-writing agent from adding a company_name LIKE '%<your company>%' filter to "find our calls," which silently returned zero rows even when the conversations were there.
  • `context.query_substrate` now honours your `query` on the broad intents. On research_overview and summarize_research the query text reached theme ranking but not the evidence ranking, so those two intents returned a spread across every account in the session rather than the ones matching what you asked — the wider your workspace's data, the more off-topic the result looked. Both now rank evidence by relevance to your query while still favouring a spread of accounts over many quotes from one. The other intents are unchanged, as is a call with no query, which is still a deliberate broad scan.
  • An abandoned connect no longer sits in "Connecting" forever, and no longer reports itself as healthy. When an OAuth handshake did not finish — you closed the provider's consent screen, the provider rejected the request, or the authorization link expired — the connection was left in a state only the completed handshake could clear: nothing retried it, no sync could reach it, and connections.get / list reported its health as healthy even though no credential existed and no data was flowing. Two changes: a connection with an unfinished authorization now reports health: "needs_reauth" (and, where connection health alerts are switched on, can raise one), and an authorization left unfinished for over an hour — past the point its authorization link can still be used — is now released to status: "disconnected", which is the state connections.reconnect restores from in place. No connection that holds a credential is affected, and an in-flight connect is never interrupted.
  • `search` now answers the question you asked. A negative-sentiment ask ("pain points", "complaints", "concerns") was being rewritten to "objections" before it ran, and a company or account name in the ask ("objections from Acme") was silently dropped — so the answer came back mislabeled or scoped to everyone. The planner now keeps the exact sentiment word you used (and invents none you did not), preserves every entity name verbatim, and the curated fast-search templates route on your original wording rather than a paraphrase. An entity-scoped ask now scopes to that entity, and an aggregation or "themes" ask falls through to the query writer that groups correctly instead of dumping raw rows.
  • `search` meaning-based (semantic) matching now spans your whole corpus. Results were collapsing to a single conversation with identical similarity scores because the semantic index only ever populated a thin sliver of it. The index now builds fully, so a "sounds like…" search ranks across every relevant conversation. An entity-scoped, filter-shaped ask is also routed to the exact-filter path rather than semantic matching, where a name match is what you want.
  • `search` accepts a pure distribution query. A group_by + metric aggregation with no filter ("distribution of sentiment across everything") is now accepted instead of rejected as an empty request.
  • `search fields` lists the allowed values for enum fields. Low-cardinality fields (sentiment_primary, record_type, outcome_band, and similar) now carry their sample_values, so you can discover the vocabulary from the field catalog alone.
  • `enrich` returns your first-party evidence for real customers. A cold lookup on a company, person, or topic was timing out its evidence read and reporting zero matches even when the account had thousands of conversations; the read now has room to complete. enrich on a company also accepts a name when you have no domain, resolving it to the account in your workspace instead of erroring.
  • `lookalike find` works by domain. Passing a company domain now resolves it to the seed account before the similarity lookup — the by-domain path previously reported that centroids were not built. The "not found" reason is also split into distinct domain_unresolved / entity_not_found / centroids_not_materialized / error cases so you can tell what actually happened. `lookalike themes` now honours limit, returns the real theme id and match score, and de-duplicates repeated themes.
  • Meaning-based (semantic) search now respects per-member data-scope. When a workspace restricts a member to specific accounts (a data-access rule), a "sounds like…" / "about X" query on search (query) now returns only customer utterances from the accounts that member is allowed to see — the fast semantic index applies the same company scope as structured search and the rest of the data surface. A member with no restriction, and an admin/owner, are unaffected.
  • Quarter and date-range totals are now correct at the day boundary. When a question or report filtered a date column with an inclusive "through <date>" bound (e.g. deals closed *through the last day of the quarter*), any record whose timestamp fell later that same day was silently dropped — so a quarter total could omit a deal that closed at 5pm on the final day and overstate the quarter-over-quarter change. The assistant now compiles every such bound to the correct end-of-period boundary automatically, on every data surface, so a "through June 30" total includes all of June 30. Your queries need no change; explicit end-of-day timestamps are untouched.
  • Breakdowns abstain instead of inventing a chart over sparse data. A rate or breakdown (e.g. win rate by industry) is only shown when the grouping field is populated on enough of the population, and a per-group rate is only reported once its sample is large enough to be stable — so a chart is never drawn over a field that is a fraction of a percent populated, and a "win rate" is never computed from a handful of deals.
  • Restored the pre-sync call-filter safeguard for meeting/call connectors (Fathom, Grain, etc.). An internal query referenced a column that no longer exists, so the gate that pauses an unconfigured call connector until you pick a transcript filter was erroring on every check and falling open. The gate is enforced again — a new call connector stays paused until you choose a filter or explicitly opt into ingest-all.
  • Semantic search (POST /search/query with mode: "semantic") no longer returns unlinkable duplicate rows. A small set of customer utterances in the source data carried no parent conversation id and shared a placeholder embedding; a query that happened to match them returned a cluster of near-identical results with a null interaction_id and no way to open the underlying call. Those rows are now excluded end to end, so every semantic result names the conversation it came from.
Added
  • version_provenance on knowledge_base.upload. When you append a version of a living document, you can now record which of the run's data queries each section was written from, as [{ heading_anchor, query_keys }]. Those sections become live: the console underlines them and shows how the underlying query has moved since the last version. Optional — a section you declare nothing for is simply left unbound, and a key naming a query the run did not execute is discarded.
Breaking
  • Chat and Routine runs now default to `deep` investigation depth. A run that does not set depth explicitly previously used standard; it now uses deep — which runs on a more capable model, allows more turns, and turns on external web search and the divergence view by default. Deep runs are more thorough but slower and cost more. To keep the previous behavior, pass depth: "standard" (or "quick") explicitly on the chat/routine config.
Added
  • Data Filters can now exclude specific CRM deals, not just calls and emails. An excluded deal drops from the deals data surface, win-rate, and pipeline value, and its linked interactions are removed from the corpus — reversible by removing the rule. The filters preview also shows a per-object breakdown (deals, companies, contacts).
  • Your workspace agents can now build and manage other agents and routines from inside a Chat. The Master (and any sub-agent it dispatches) can create, edit, and retire workspace agents (agents.create_agent / update_agent / delete_agent) and scheduled routines (routines.create / update / delete / run_now) directly, instead of only drafting them for you to save. A created agent runs under the same permissions as the agent that made it, and the assistant asks for confirmation before deleting an agent or a routine.
Fixed
  • "Apply" on CRM Mappings now reliably re-normalizes your data, even when the corpus has silently drifted. The pipeline only re-normalizes when the saved mapping version differs from the applied version, so a workspace whose data drifted from a stale "applied" stamp (e.g. an old backfill that marked mappings applied without fully normalizing) could show thousands of pending rows in the impact preview yet have "Apply" do nothing. Apply now reopens that gate before triggering the pipeline, so the re-normalization actually runs.
Added
  • CRM stage mappings are now auto-suggested for a workspace that has none. Opening CRM Mappings on a workspace that has never configured them now generates a first-pass set from your own pipeline data — each of your CRM's deal stages classified into a standard funnel stage — so the page starts with editable suggestions instead of blank. Suggestions are marked as auto-generated and are never applied to your normalized data until you review and save them.
  • Answers now bind claims to their evidence inline. During synthesis the agent hyperlinks a specific factual or qualitative claim to the citation / table / chart / metric block that backs it, right in the prose, using the closed amdahl:cite link grammar — a markdown link whose destination is amdahl:cite?block=<id> (block required — the id of a presented evidence block; fu optional — a follow-up question). Rich clients render the bound phrase as an evidence chip; every surface (Chat READ, terminal answer frame, live SSE) carries it. A cite that points at a block the answer never presented, or that fails the closed grammar, is unwrapped to plain text, and the flattened answer_text for MCP consumers / logs strips the scheme entirely so prose reads clean. Orthogonal to the existing amdahl:q figure-exploration links — the two never clobber each other.
  • A historic living-doc version can now show its provenance. knowledge_base://<id>/suggestions?resolved=1 (REST GET /knowledge-base/:id/suggestions?resolved=1) returns the suggestions APPROVED into that version or DENIED against it — accepted suggestions matched by the version they produced, dismissed ones by the version they were proposed against — each carrying its status, resolved_at, resolved_by_user_id, and resolution_note. The default (proposed) read is unchanged.
  • Living docs now have a per-doc "Review before publish" toggle. A living-doc workflow tagged for auto-update publishes each refresh straight to current; flip a doc to "review before publish" and each new refresh instead lands as a proposed version for a human to promote, keeping the approve/decline review meaningful on docs that otherwise auto-supersede pending suggestions. Read the current mode with GET /knowledge-base/:id/review-mode (resource knowledge_base://<id>/review-mode) → { document_group_id, review_before_publish }; set it with POST /knowledge-base/:id/review-mode body { review_before_publish: boolean }. Default is unchanged (auto-update) for every existing doc. The setter is console/REST + Copilot only (not on the MCP coarse tool).
  • A successful SQL-shaped tool call now carries a bounded preview of the rows it returned. The tool_complete agent event (live SSE, persisted step_data.events, and the Chat run READ ?include=events replay) gains an optional resultSamplecolumns, up to 25 rows keyed by column, and a truncated flag when the real result had more rows or columns than shown. Present only for tabular successes (data.query, data.ask, search.run); absent for non-tabular, empty, or failed calls. Lets a client render the actual returned data (table or chart) instead of just a row count.
  • The CRM Mappings settings tab now opens with a deal-grain summary of your pipeline — total deals, open pipeline, and won / lost value — plus a per-stage deal count on every mapping, so you can map your CRM stages against the numbers you already know.
Changed
  • Cluster-driven figures in Chat answers are now citable. A count that comes from a customer-conversation theme — "349 SMB deals were lost", "541 executive conversations" — comes from a data.cluster_search / data.cluster_detail read, not a warehouse row, so it previously had no openable evidence to link to. Answers can now present a theme as a cluster_finding block (its title, insight, narrative hook, and member count, plus representative rows) and bind the figure to it with amdahl:cite, so clicking the number opens the actual theme behind it.
  • Every backable figure links reliably, with no fixed per-answer cap. The linked-data-phrase guidance is now coverage-based — link every figure a presented block backs, not a favored two or three — and a deterministic pass at the answer boundary wraps any bare figure that exactly and unambiguously matches a presented block's headline value (a metric value, a theme's member count) in a cite to that block. A figure the answer wrote as plain prose still carries its evidence link; it can never bind to the wrong block.
  • "Continue" after a stopped answer keeps its context. Stopping an answer mid-stream and then sending "continue" no longer cold-starts as a fresh session — the stopped turn's question and partial answer stay in the conversation's context, so the agent picks up where it left off instead of asking you to restate what you were doing.
  • Chat asks a sharp clarifying question when — and only when — it matters. When a request is ambiguous in a way that would genuinely change the answer (Enterprise vs SMB, win-rate vs velocity, "recent" as 30 vs 90 days) and guessing wrong is costly, the agent now pauses with one question, preferring a quick multiple-choice pick when the options are enumerable. Otherwise it answers and states the assumption it took as a one-click steerable fork — never a wall of clarifications, never a forced gate.
  • Verbatim customer quotes in Chat answers now render as citation evidence, not quoted prose. When an answer leans on a customer's exact words, the agent presents the quote as a citation block (kind utterance — an attributed, openable evidence card) and binds the claim to it inline with amdahl:cite, instead of stacking literal double-quoted lines in the prose. Answers also now bind every specific and qualitative claim to a REAL presented block — a table / chart_spec carrying the underlying query + rows, or the cited quote — so a reader can open the actual evidence behind each statement rather than take it on faith.
  • Theme-search tool cards now preview their results. An expanded data.cluster_search step in the Chat transparency panel shows a sample of the themes it returned (label, score, representative quote), matching the row preview already shown for data.query / data.ask / search.run. A degraded or empty theme read shows no preview.
Fixed
  • Living-doc suggestions now match the version you're viewing. The suggestion rail + inline redlines on a living document are pinned to the version being rendered, so proposals authored against a superseded version no longer appear mis-anchored on the current document. knowledge_base://<id>/suggestions filters to the requested version by construction.
  • CRM Mappings settings no longer fail to load for workspaces whose saved mappings predate deal-amount tiers. A mapping record created by an earlier import could be missing its amount-tier section, which surfaced as a server error on the settings page's change-impact preview and left the page unable to load. Older records are now always read as a complete, well-formed mapping, so the page loads reliably regardless of how the mappings were first created.
  • Living-doc suggestion rationales no longer overcount related fields. A section-level suggestion's "why" now counts the DISTINCT fields that moved, so it reads "…(+9 related fields)" instead of the absurd "…(+240 related fields)" that appeared when the same handful of fields rolled up once per subsection. The suggestion's stored evidence is likewise collapsed to one row per field.
  • Living-doc suggestion rationales are readable now. A refresh suggestion's "why" is a short human phrase describing the primary change (e.g. "Days Since Last Touch (mean) rose from 65.05 to 71.39") instead of a raw dump of dotted field paths and long decimals, and immaterial sub-5% field wobbles are dropped from the evidence at the source so they never crowd out the change that actually moved.
  • The data assistant no longer wrongly claims Salesforce workspaces have no email. The record_type / interaction_type schema guidance the NL→SQL assistant reads (via data://schema) stated that email interactions exist only for HubSpot workspaces and that a Salesforce-CRM workspace has zero record_type='email' rows. That was incorrect — Salesforce logs emails as Task/Event activities that the pipeline normalizes to record_type='email', so they are in the corpus and queryable. The assistant now answers email questions on Salesforce workspaces instead of refusing; coverage is still source-dependent (email comes from HubSpot, Salesforce, Salesloft, and Pylon, while Gong is calls-only), so it still checks the live sample_values before filtering.
  • The CRM Mappings change-impact preview no longer shows a misleading "0 rows would re-normalize" right after mappings are first generated. On the very first load of a workspace's CRM Mappings settings, the impact preview could race the initial auto-generation and compute over an empty mapping set — reporting 0 even though applying the mappings would re-normalize a large number of rows. The preview now waits for the generated mappings (across app instances) so the number always reflects what an apply would actually change.
  • Auto-generated CRM stage mappings classify contracting and closing stages more accurately. A late-stage "Contract" (or legal / signature / procurement) stage is now mapped to Negotiation rather than Proof of Concept, which is reserved for actual technical trials/POCs. This improves how deals in those stages roll up into your funnel view.
Added
  • `data.query` time windows without hand-written SQL. A new optional last_n_days parameter (operation + MCP data tool) injects the correct event-time window for you — pass last_n_days: 90 instead of authoring TIMESTAMP_SUB / interval syntax. The data://schema resource now carries a sql_dialect block documenting the warehouse's SQL rules, and failed queries return targeted rewrite hints (e.g. Postgres-style INTERVAL '90 days'INTERVAL 90 DAY) instead of raw parser errors.
  • Chat answers now carry suggested follow-up questions. The answer envelope on every surface (run READ, terminal SSE frame, MCP chat_status) includes answer.follow_ups — up to 4 complete, runnable next questions the agent attached to its final answer block. Render them as one-click chips or feed one straight back into chat.start / respond; the field is always present and empty when the agent supplied none.
  • Chat now shows how it understood your question. Before answering, a fast enrichment phase interprets your plain question against your business context and standing reports, and the run READ carries an optional intent_brief (original_query, interpreted_intent, expanded_question, hints[]) so clients can render a collapsed "How we understood your question" card. Advisory only — your original question is always answered verbatim, and a skipped or failed enrichment changes nothing.
  • Suggested queries come from your Living GTM Docs. GET /console/suggested-queries serves deep, specific example questions derived from the living-doc catalog, tagged per persona (executive / developer) and surface (search / chat) with the source doc attached.
  • Charts in answers: four new shapes. chart_spec blocks accept funnel, radar, treemap, and gauge alongside bar / line / area / pie / scatter — same { x, y, series? } encoding; gauge renders the first row (label, value, optional max).
Changed
  • Master Chat commits living docs only. agents.delegate now requires the new agents:delegate scope (split from workflows:delegate, which still gates agents.start / agents.fork_blueprint). Master no longer resolves the specialist-start tools; lasting saves go through outputs.write_doc. Discover valid living-doc slugs via GET /outputs / output://list.
  • Blended fast search now enriches the web query with your workspace context. In POST /search with mode: "blended", the web + news citation pass no longer runs your raw ask verbatim: a cached, tenant-aware rewrite folds in your company and industry context first, so ambiguous asks resolve to *your* market instead of the public internet's dominant sense of the words. When an ask reads as purely a question about your own workspace data, the external pass is skipped with the new external_omitted: "not_relevant" instead of returning off-topic links (this never blocks a healthy fan-out — uncertainty runs the search). The result's external.external_query_used shows the query the web pass actually ran. Also in this release: agents.start with async: false no longer reports a slow specialist as an error — if the run is still working after the ~60s bounded wait, the call returns success with status: "running" and still_running: true (the async contract), and a run that genuinely failed mid-wait surfaces the honest run_failed code; and data.query results now return BigQuery date/timestamp cells as plain ISO strings instead of {"value": ...} wrapper objects, on every protocol.
  • Product feedback Slack pings always attempt delivery (no production-only env gate); still require INTERNAL_SLACK_WEBHOOK_URL.
  • Cost accounting is server-internal; runs no longer loop on a finished answer. The Chat run read (GET /chats/:id/runs/:run_id, chat://<id>/runs/<run_id>, the agents tool's chat_status) now reports usage as tokens + turns_used only — estimated_cost_cents is gone, as is cost_cap_cents on chat summaries and the conversation:// / agent_run:// reads. The max_cost_cents config knob is removed from chat.start, chat preferences, and routine configs (sending it now rejects as an unknown field); every new chat gets the platform-managed limit automatically. The cost_cap_exceeded pause is renamed usage_limit_exceeded with a unitless payload: reply { action: "continue" | "stop" } — on continue the platform extends the limit itself. Separately, imperative first messages no longer force tool use past the first model request, fixing runs that re-presented the same answer repeatedly before settling.
Fixed
  • Champion & EB Voice Digest no longer reports a phantom "coverage decline." The first time a workspace runs the digest after the champion-scoring fix (which excludes the neutral-default 0.75 stakeholders), champion/EB counts step down versus the older, un-gated version. The digest now labels this as a one-time scoring correction and treats the new counts as the baseline — instead of inventing a real-world decline (seasonality, pipeline gaps, "cleaned test data") or recommending an investigation.
  • Pipeline Health Report now refreshes for workspaces with many pipeline stages. The report's stage-by-stage funnel (and the open-pipeline stage breakdown) were capped at 30 and 25 rows — a complete per-stage aggregate that must not be truncated, so a workspace whose Salesforce pipeline has more stages than the cap (including legacy or #REF! stage labels) failed the whole run and the living document never updated. The caps are raised to the query engine's row limit, so every stage is included.
  • Living GTM Docs no longer stall on large workspaces. The ICP Signal Report, Win/Loss Report, Deal Qualification Report, and Pipeline Health Report each ran a few "cover the whole population" queries (e.g. per-industry, per-segment, per-competitor breakdowns) under a hand-picked row cap. On a workspace whose true breakdown exceeded that cap, the scheduled refresh aborted and the document froze at its last version. Those completeness queries now read the full set, so the docs keep refreshing no matter how broad the workspace's customer base, pipeline, or competitor field grows.
Added
  • Agents are now a first-class primitive: list the platform agent roster and author your own workspace agents (a named prompt with a stable slug) via the new GET/POST /agents API family. Amdahl-shipped agents (starting with researcher) appear alongside your own; platform agents are read-only.
  • Agents can now take outbound actions — with your permission: the new POST /actions/invoke API (and the matching agent tool) fires one of two outbound actions: email_member (email current workspace members — external addresses are always rejected, sends are capped and idempotent) or notion_sync (push a knowledge-base document into your connected Notion now). Actions are opt-in per run via an actions_allowed list that defaults to empty, so an agent that was not explicitly granted an action cannot send anything — it proposes instead. GET /actions (the action://list resource) shows each action's live availability and remaining send budget, and a failed action is never retried automatically.
  • Agents can now keep long-term memory honestly: the new POST /memory/write API (and the matching agent tool) commits durable facts to the workspace context store. Every agent-written entry is automatically tagged auto_detected, and entries a human wrote (manual) are protected — an agent that disagrees with one must add a new flagging entry rather than edit yours.
  • Agents can now ask a human: the new ask_a_human agent tool pauses a run to put one structured question to a person — multiple_choice (2-6 options, with a guaranteed "Other" write-in the platform injects) or free_form. Only the asking run waits: a sub-agent's question is escalated to its caller's event stream as a child_needs_human frame while sibling work keeps running, and the answer resumes the waiting run directly through the existing resume API as a { answer, option_id? } tool result. Runs started with on_question: "auto" skip the ask and proceed on stated assumptions; headless / scheduled runs (on_question: "none") get an immediate no_human_in_loop error instead of a hang.
  • See exactly which tools a workspace agent can block: the operation catalog read takes a new blockable_for=master filter (operation://list?blockable_for=master, REST GET /operations?blockable_for=master) that returns just the tools an agent actually runs with — its granted kit, minus reads and the protected loop tools a blocklist can never remove — instead of the full operation catalog, so a tool-blocklist picker only offers tools it can meaningfully block.
  • Chat reads now tell you what triggered a run: every chat + run read (GET /chats, GET /chats/:id, GET /chats/:id/runs/:run_id, and the agents MCP tool's chat reads) now carries a trigger (chat · agent · routine), the routine ({ id, name }) behind a scheduled fire, and the agent_ref a run is pinned to — so a routine fire and an agent run are distinguishable from a plain chat without inferring it from the session title.
  • Agents can now delegate: the new POST /agents/delegate API (and the matching agent tool) dispatches a sub-agent — a roster agent like researcher, one of your own workspace agents, or a one-off inline prompt — to investigate a single goal and report back with an answer, evidence, and suggestions. Pass task, plus optional desired_output and context, to shape what comes back. Delegation is async (the call returns the child run id immediately; progress streams into the caller's session), sub-agents run with investigation-only permissions (they never write documents, memory, or send anything), and delegation depth is capped at one level.
  • Multi-agent Routines: config.agents picks who a routine employs — "all" (plain Master, unrestricted delegation), one ref (the fired turn runs AS that agent, the pin), or several refs (the Master may delegate ONLY to that roster; other agents and inline prompts are refused by agents.delegate with agent_not_allowed). Every roster member re-resolves at fire time. The legacy single agent pin still works; passing both rejects. The output_slug / full_regen doc-target knobs left the writable config — the agent picks its own document slug via outputs.write_doc.
  • Workspace agent controls: slug is now optional on POST /agents (derived from the name and uniquified when omitted), and every workspace agent accepts a tool_blocklist — operation ids subtracted from the agent's kit at run time (all kit tools are on by default; runner-inline tools like finish cannot be blocked).
  • Fast search — data back in one call. POST /search is the synchronous fast lane beside Chat: it turns your ask into SQL over your interaction warehouse and runs it, blocking for a single answer (a ~15s hard ceiling) with no agent run. Pass mode: "blended" to also fan out a quick web + news citation pass (internal is the default, warehouse only), limit / external_limit to size the result, and synthesize: true for a one-paragraph headline. It returns the rows, the SQL that ran, and any citations. Reach for it when you want data now (a dashboard cell, a slash command); reach for Chat when the ask needs a multi-step investigation. Every failure mode is a typed field on the result — an unsupported ask, zero rows, a timed-out source — never a raw error, and a partial answer always beats a 500. Also on MCP as the search tool (one action, run). Needs only data:read; the blended leg uses external_search:execute and degrades to internal-only without it. See the new Search guide.
  • The search MCP tool. Connected MCP agents (Claude, Cursor, ChatGPT) can now ask Amdahl anything end-to-end: start opens or continues a named Session and runs one Master agent turn server-side, returning handles immediately; status polls the run (with an optional wait_ms long-poll up to 30s); respond answers a paused human question; cancel stops a run. A deep run is never crammed into one tool call — start, check in, respond. Session reads are also MCP resources (session://list, session://<id>, session://<id>/runs/<run_id>).
  • The agents MCP tool: the workspace agent library (list / get / create / update / delete named, reusable prompts — Amdahl library agents are locked) plus Routines in the same tool (create_routine with a prompt + cron for a standing scheduled ask, update_routine, delete_routine, and run_routine_now, which fires immediately and returns the same handles a Search start does). Roster and routine reads are also MCP resources (agent://, routine://).
  • Existing MCP API keys were backfilled with the new scopes automatically — no rotation needed. Read scopes (conversations:read, agents:read, routines:read) landed on every bundle; write scopes (conversations:write, workflows:write, agents:write, routines:write) on customer-agent keys and up.
  • Routines are live: a Routine is a cron that fires a Search — each occurrence opens a fresh Session named "{name} — {date}" and runs one Master agent turn, headless (ask_a_human fails fast with no_human_in_loop; a routine never parks on a person). Full CRUD plus an off-cadence trigger: POST /routines, GET /routines[/:id], PATCH /routines/:id, DELETE /routines/:id, and POST /routines/:id/run-now (returns the same watch handles a Search START does). See the new Routines guide.
  • Verified document commits: the Master agent now refreshes living documents through outputs.write_doc — never a raw knowledge-base upload. Run outputs.run_bound_queries first: it executes the vetted query pack behind an analytical document and records the cells; every digit the commit states is verified against exactly those cells. Auto-promotion to the current version is refused without a verified pack (the version lands proposed for human review), and a failed theme read during the run blocks the commit entirely so a degraded read can never overwrite the last good version.
  • Answers are content blocks: a Chat answer carries answer_text plus an ordered content_blocks[] list (text, callout, citation, table, chart_spec, metric). Data-backed blocks include both the declared query and the snapshot data on every surface, so clients can render the snapshot instantly, re-run the query live, or diff the two. Agents emit blocks mid-run via the new answer.present tool; blocks stream as content_block SSE events and reconcile into the terminal answer in order.
  • Living-doc suggestion threads + a runnable data-query tree. Two review surfaces for living documents. (1) Suggestion discussion threads — every data-driven suggestion now carries its own comment thread: read it at GET /knowledge-base/:id/suggestions/:suggestion_id/comments (also the knowledge_base://<id>/suggestions/<suggestion_id>/comments resource), reply with POST …/comments (body, optional thread_root_id), and resolve/reopen with POST …/comments/:comment_id/resolve. Threads are keyed on the suggestion (not a version block), grouped root + replies, newest-first. (2) Per-node data-query tree — open any node of a living doc and see its numbers live: POST /knowledge-base/:id/nodes/:node_key/run executes that node's bound queries through the same interactions engine (tenant- and access-scoped to you) and returns fresh rows per query; POST …/nodes/:node_key/ask answers a question grounded only in that node's bound queries and their latest captured signatures. Reads need knowledge_base:read; the thread writes need versioning:write (editor). Like the existing suggestion accept/dismiss/ask + review-thread writes, the thread writes and the node compute are REST + in-app copilot only (off the MCP coarse tool); the thread read rides the knowledge_base:// resource scheme, and an MCP client runs a node's query by reading its SQL from knowledge_base://<id>/tree and calling the data tool directly.
Changed
  • The agent lane is now Chat (renamed from Search): the multi-turn agentic surface that backs the console's sidebar chat is called Chat everywhere, and the name Search now belongs to the synchronous fast-search endpoint (POST /search, shipped in this release — see its own changelog entry). What moved: POST /searchPOST /chat; GET /search/sessions[...]GET /chats[...] (rename via PATCH /chats/:id); the start body's session_id / session_namechat_id / name, and responses return chat_id (list responses return chats, detail responses chat); op ids search.*chat.* (chat.start, chat.list, chat.get, chat.get_run, chat.rename); the session:// MCP resource scheme → chat://. Over MCP the standalone search tool is retired — its four lifecycle actions live on the agents tool as start_chat / chat_status / respond / cancel_chat with the same check-in contract (START always returns handles; poll chat_status or read chat://<id>/runs/<run_id>; wait_ms long-polls up to 30s). Routines fire Chats; run-now responses return chat_id and routine reads carry last_chat_id. See the Chat guide (formerly the Search guide).
  • Chat `depth` is now a real investigation tier. quick, standard, and deep no longer differ in name only — each sets the model, the turn budget, and the toolset for you. quick runs a lean, fast toolset for a focused lookup; standard is the everyday full toolset; deep uses the most capable model with a larger turn budget, forces market search + the divergence map on, and instructs the run to decompose the ask and self-verify every figure before answering. You never pass a model — the tier picks it.
  • Two Chat config knobs were removed. evidence is gone — citations and the query behind every data-backed answer block are now attached unconditionally, so there was nothing to toggle. as_of is gone from the Chat / routine run config — an interactive Chat always reads your current data; historical "as we understood it on date X" reads belong to workflow backtests (the data.query time-travel primitive is unchanged). Sending either field now returns a clear invalid_argument error rather than being silently ignored.
  • Tool activity now reads in plain language. Each tool a run calls streams a short, human-readable label ("Searching your customer conversations") and a plain-language result summary ("30 results") on its lifecycle events, so the console's tool cards are legible instead of raw operation ids and JSON.
  • data.query now refuses a silently-truncated result: when a query's population exceeds the effective row limit, the call returns a structured result_truncated error (with guidance to aggregate in SQL or bridge on an id list) instead of an arbitrary slice. Pass allow_truncation: true for a deliberate bounded top-N (ORDER BY ... LIMIT n).
Removed
  • The blueprints MCP tool. Connected MCP agents no longer author, run, schedule, or fork workflow (blueprint) recipes over MCP, and the workflow resource reads (agent_blueprint://, blueprint_run://, blueprint_schedule://, blueprint_backtest://, blueprint_output://, step_kind://, trigger_kind://, prompt://) are gone with it. Workflows themselves are unaffected: the console Workflows surface and the full REST lifecycle (authoring, validation, one-shot runs, schedules, backtests, output promotion) keep working unchanged, and scheduled runs keep firing. From MCP, use the new agents tool's Routines for recurring work and the new search tool for one-shot deep work. MCP API keys need no rotation; the blueprints:execute scope on existing keys is simply inert on the MCP surface.
  • The pages MCP tool. Connected MCP agents (Claude, Cursor, ChatGPT) no longer author, list, or delete workspace Pages, and the page:// + page_template:// MCP resources are gone with it. Pages themselves are unaffected: the console Pages surface and the full REST lifecycle (/api/platform/v1/pages — create, validate, render, update, archive, delete, templates, embed links) keep working unchanged. MCP API keys need no rotation; any pages:* scopes on an existing key are simply inert on the MCP surface. See the updated "Pages over MCP" guide for the migration path.
Fixed
  • Account Tier Intelligence living document: corrected two accuracy issues — the triage-tier account count now matches the ranked account list, and accounts with no identified champion no longer display a default champion score.
  • API-key connections no longer land Disconnected the moment you connect: connecting any API-key connector (Fireflies, Fathom, Granola, Grain, Aircall, Salesloft, Pipedrive, Pylon) created the connection row before its key was stored, and a legacy database trigger that derived status from credential existence marked the row disconnected at birth — so the console showed Disconnected instantly and the sync scheduler (which only picks up connected sources) never ingested anything. The trigger is retired, and POST /connections (api_key) plus POST /connections/:id/reconnect now order the flow as pending → store the key → connected, so a connection only ever reads connected once its credential actually exists. Previously affected connections can be restored in place with a normal Reconnect.
  • Living GTM documents: the Persona Voice Deep-Dive and Champion & EB Voice Digest no longer count or quote accounts that carry only the pipeline's neutral default champion score as champions (matching the fix already shipped for Account Tier Intelligence), so champion totals are no longer over-stated on data-rich workspaces. Separately, the Voice-of-Customer Cluster Report no longer fails on workspaces with more than 200 conversation-theme clusters — every theme's win/loss outcome is now computed.
  • Scheduled/headless living-document runs are reliable again: the runner now always reaches the production data endpoint, fixing intermittent runs that halted with a connection error and left some living documents (persona voice, competitive divergence, weekly digest, content calendar) stale.
Added
  • Your pipeline funnel is now backtestable. Pass as_of to a data.query over the deal_funnel surface to see the per-pipeline/stage funnel as it stood on any past date — deals-ever-entered, progression rate, and median dwell reconstruct from your stage history — the same time-travel that already backed deals, deal_qualification, clusters, and positioning_claims. Stages with no history before that date report null rather than a fabricated zero, so a trend never invents numbers.
  • New API + MCP reads for a living document's data spine — and endpoints to review its suggestions. Every living document now exposes its underlying data over the API and MCP: its signal trends per bound query (GET /knowledge-base/:id/signals, knowledge_base://<id>/signals), its node → query tree (.../tree), and its pending data-driven suggestions (.../suggestions). Three companion endpoints let a reviewer act on a suggestion — accept it (apply the edit and produce the next version), dismiss it, or ask a grounded follow-up about why it fired — via REST and the in-app copilot. Accepting never overwrites a document whose current version you authored by hand. (Suggestions populate as the automatic refresh engine rolls out; the read + review surfaces are available now.)
  • Living documents now propose data-driven edits when their numbers move. When a recurring living document refreshes and a bound metric shifts materially since the last run, Amdahl now surfaces a single, evidence-backed suggestion on the affected section — each anchored to the exact number that changed, with the before/after trend behind it. Suggestions are proposed for your review (never applied automatically), so you decide what lands.
Changed
  • The Account Tier Intelligence, ICP Signal Report, and Deal Qualification Report living documents now end with a concrete Recommended Actions section — named accounts/verticals paired with a specific next step (or a short prioritized play list) — instead of stopping at diagnosis. Actions stay grounded in the underlying data: they cite each item's own signals (momentum, binding constraint, tier, undercovered dimension) and never introduce invented numbers, deadlines, or targets.
Fixed
  • Living-doc suggestions no longer surface a data-availability transition as a GTM change. When a document's only prior generation is a structural backfill seed (e.g. a deal_qualification as-of snapshot whose coverage lens is empty), the refresh engine now stays silent instead of narrating spurious "0→N" / "added N records" / "expanded coverage from 1 to 3" movements. A genuine value move against a real prior still emits as before.
Added
  • Canonical sales-pipeline metrics. New pipeline://metrics, pipeline://funnel, and pipeline://qualification-health reads (MCP resources + REST GET /pipeline/*) give one consistent, per-pipeline definition of win rate, sales cycle, funnel, and qualification — with the qualification / "stage 0" band ALWAYS shown but NEVER counted in a rate or cycle. data.query can also read the new deal_winloss (per-pipeline win rate + sales cycle) and deal_funnel (per-stage) tables directly.
  • Canonical funnel + per-pipeline win rate in the living docs. The Pipeline Health Report and GTM Health Report now read the shared deal_funnel / deal_winloss views, so the funnel (with the qualification / "stage 0" band shown but never counted in pipeline value), the qualified-pipeline value excluding stage 0, and the per-pipeline win rate + sales cycle (with qualification/stage-0 dwell excluded and reported separately) are all defined one consistent way instead of re-derived per doc.
Changed
  • Connect more than one instance of the same integration to a workspace. The API-key connectors (Fathom, Granola, Fireflies, Pipedrive, Pylon, Salesloft, Grain, Aircall) and the workspace OAuth connectors (HubSpot, Salesforce, Gong, Slack, Notion) now support several simultaneous connections per workspace instead of being capped at one. Gmail and Outlook (per-member) also work reliably when several members each connect their own account.
Fixed
  • Fixed a "The server ran into a problem" error when connecting an API-key integration where one already existed. The connect now returns a clear, actionable message instead of a generic 500 and never leaves a half-connected source behind.
Added
  • Workflow runs now record a per-step audit trail. Reading a run (blueprint_run://<id>, GET /blueprint-runs/:id, or context.review_kit) returns a step_states entry for every step the workflow executed — its kind, the tool it called, whether it succeeded, how long it took, and a short summary of what came back. When a step degrades rather than fails outright — a theme search that could not reach the warehouse, a query that returned no rows — the reason is now on the run record, instead of having to be inferred from the document the run produced. Step summaries are counts and status codes only; they never include the underlying rows or document text. Populated for workflows that run on the deterministic step executor.
  • New Actions connector category for outbound integrations — connectors that push your Amdahl data OUT to another tool rather than pulling data in. Notion Knowledge Sync is the first, and the connector catalog + the connections reads now group it under actions (filter with category=actions).
Changed
  • Living documents now read like reports, not compliance forms. Every recurring living document — GTM health, pipeline health, voice-of-customer, win/loss, ICP, account tier, and the rest — now follows a shared writing standard: it opens with the finding rather than the methodology, states a data caveat once where it applies instead of on every line, reserves bold for the few things you must not miss, and never leaks internal field names or CRM stage codes into the prose. Documents you generate from your own uploads get the same treatment. Every number stays verified against your data exactly as before.
  • Any signed-in user can now create a workspace. Workspace creation no longer rejects personal / free-mailbox email addresses.
  • You can now set your company domain while setting up a workspace. When the domain matches your own work email, it is verified automatically.
  • New discoverability control: choose whether teammates with a verified email on your company domain can find and join your workspace. It is off by default.
  • Pick your role during setup — Executive or Developer — and the console tailors where you land and what it shows first. You can change it any time in settings.
Fixed
  • A failed theme search now reports an error instead of looking like "no themes found." When the theme index is temporarily unavailable, data.cluster_search previously returned an empty result that was indistinguishable from a workspace that genuinely has no matching themes. It now surfaces as a tool error, so your agent can retry or tell you something went wrong. A workspace with no themes yet, or a search that simply matches nothing, still returns a normal empty result.
  • Scheduled living documents no longer publish a report when their theme search fails. If a workflow cannot read your conversation themes, the run now stops and the previously published version of the document stays in place, instead of quietly replacing it with a version missing its qualitative analysis.
  • Scheduled living documents no longer crowd each other out — or hold up a run you started yourself. Recurring documents used to all fire at the same moment and compete for the same processing capacity, which could cause a report to come back rate-limited and, occasionally, to arrive with nothing in it. Scheduled documents now drain through their own dedicated lane at a steady pace, so they generate reliably no matter how many are on your calendar. A document you run on demand starts right away instead of waiting behind the day's scheduled batch.
Added
  • data_access_rule:// read resource (data_access_rule://list, data_access_rule://<id>; REST GET /data-access-rules). Look up the verified data-access rules the platform maintains for the warehouse — field substitutions, surface-grain caveats, and aggregation patterns — so an agent queries your data correctly. Filter with ?surface=, ?kind=, or ?signature=.
  • New guided walkthrough prompt (system/guide_me) for MCP clients. Ask "what can this do?" or "guide me" and Amdahl runs a friendly, jargon-free, numbered-menu interview — it offers plain-English goals (meeting prep, customer voice, deal lookup, proof, research, dashboards, recurring reports), you pick a number, and it does the work and answers in plain language. Built for non-technical users who would rather pick from a menu than know which tool to call.
  • Default GTM living documents now schedule themselves for your workspace. Once a workspace has synced go-to-market data, Amdahl automatically schedules a starter bundle of recurring living documents — GTM health, pipeline health, voice-of-customer, win/loss, and a weekly priority briefing, plus deal-qualification, account-tier, and ICP reports once a CRM is connected — so useful reports start arriving with no setup. Workspaces that have already configured their own workflows are left untouched.
Changed
  • Win / Loss and GTM Health Report accuracy on high-volume workspaces. The won-vs-lost MEDDPICC differentiators, the score-calibration curve, and the per-competitor win rates in these living documents are now computed over your _entire_ closed-deal history in a single pass, instead of a recent sample. Workspaces that close thousands of deals per window — where these figures were previously computed on a truncated slice — now get complete, unbiased numbers.
  • New queryable fields on the `data` tool. deal_qualification now exposes realized_is_won / realized_is_closed (a company's realized deal outcome), and competitor_mentions now exposes competitor_class (rival vs. own-product / benchmark / model-vendor) plus the account's realized deal outcome. You can now compute score calibration and per-rival win rates in a single query, without bridging across tables, and the competitor win-rate read is backtestable with as_of.
  • data.query now accepts Common Table Expressions (WITH … AS (…)) and subqueries (in FROM and WHERE). Previously these were rejected; your workspace's tenant scoping is applied to every part of the query, so nested reads stay scoped automatically.
  • MCP-connected assistants now translate Amdahl's internal terms into plain business language when talking to you — "conversations" instead of "interactions", "topics" instead of "clusters", "a workflow" instead of "a blueprint" — and no longer surface SQL, tool names, or field names unless you ask how something works. The server instructions carry a built-in glossary so this holds across every client.
  • data.query now also accepts set-operations (UNION / UNION ALL / INTERSECT / EXCEPT). Previously these were rejected; each branch of the query is scoped to your workspace independently and the row limit applies to the combined result, so you can union or intersect results in a single query.
  • data.explore with sample values is now much faster on a cold call: the per-column distinct-value lookups collapse into a single scan of your data instead of one query per column (the first explore of a session no longer stalls for tens of seconds).
  • data.cluster_detail now accepts a durable lineage_id (surfaced on every data.cluster_search result) as an alternative to cluster_id. A theme's cluster_id changes each time topics are recomputed, so a saved handle could go stale; the lineage_id is stable across recomputes and is resolved server-side to the theme's current id, so a drill-in handle you saved earlier keeps working.
  • The GTM Health Report picks its own title again. A fixed title was briefly pinned onto the report. It was redundant — the report is already barred from describing itself, in its title, any heading, or the body, as covering a lookback period, a rolling window, a quarter, or "recent" deals — so the pin is gone and the report titles itself. The scope guarantee is unchanged.
Fixed
  • The grounding discipline behind every living GTM doc now encodes the deal-outcome surface rules directly: deal counts / revenue / win rate come from your CRM deal records, conversation-corpus outcome rates are labeled as such and never conflated with them, won+lost can exceed closed (an account can be both), numbers are stated exactly (no rounding), currency is formatted, and quotes stay in blockquotes. Applies across all ~18 living-doc generators, not just VoC and ICP.
  • The GTM Diagnostic's "where deals are lost" stage-leak analysis now works. The query behind it was silently rejected before it ever ran, so on every workspace the report saw an empty result and fell back to reporting cycle-time only (reading the blank as "no stage history"). It now computes the stage each lost deal died in, so the diagnostic can point at where in the funnel deals actually leak.
  • agents.run_blueprint now resolves a tenant blueprint by its identity.slug, not only by its UUID. Running a blueprint you authored by its slug used to return not_found; it now resolves the same way every other blueprint surface does.
  • Win / Loss Report no longer claims a 90-day window it never applied. The win-loss-report workflow declared a lookback_days input but none of its queries filtered on it, so a run headlined _all-time_ closed-deal totals under a "90-Day Close Period" title — the figures were real, the scope was not. The report now states its true basis, all closed deals to date, opens with a line giving the closed won/lost counts and noting that the MEDDPICC differentiators and per-rival win rates are computed over the closed-and-scored subset, and is barred from describing itself as covering any rolling window. The lookback_days input is removed: only the deals cut has a close date at all — the won/lost outcome reaches the qualification and competitor surfaces as a flag with no date — so windowing part of the report would have put a 90-day headline next to all-time win rates. Passing the old input is ignored rather than rejected, so existing schedules keep running. The document still refreshes monthly; its version history is the time series.
  • Champion & EB Voice Digest now actually covers the window it reports. The workflow declared a lookback_days input but none of its queries filtered on it, so the digest quoted stakeholders from your entire conversation history under a heading that implied a recent period. It now genuinely reads the last 60 days of champion and economic-buyer utterances, opens with a coverage line stating how many utterances and accounts fall inside the window out of your all-time totals, and says so plainly when nobody spoke in the window instead of padding the digest. The objection themes come from the theme index, which has no time filter, so they remain all-time and are now labeled as such. The lookback_days input is removed — the window is fixed at 60 days; passing the old input is ignored rather than rejected, so existing schedules keep running. Fork the workflow to change the window.
  • GTM Health Report no longer implies a lookback period it never applied. The workflow declared a lookback_days input (default 180) that no query filtered or weighted by. The report now states its true basis — all of your conversation and CRM data to date — and is barred from describing itself, or any finding, as covering a rolling window, a quarter, or "recent" deals. Its cuts genuinely cannot be windowed: the stage-transition analysis would silently mis-identify where deals die, the account funnel view would lose every open deal, and the score-calibration and competitor win-rate cuts carry a won/lost flag with no close date. Windowing only the rest would have put a windowed headline beside all-time win rates. The lookback_days input is removed; passing it is ignored rather than rejected. The report still refreshes on a cadence, and its version history is the time series.
  • The Champion & EB Voice Digest now carries its window in the title. The digest reads the last 60 days, but because it edits the prior version in place rather than rewriting it, a refresh could leave the old heading standing and mention the window only in a subtitle beneath it. The 60-day window is now written into the document's own title on every refresh.
  • The GTM Health Report's Home-card score reads reliably regardless of how the report is laid out. The score block that powers the card is now located wherever it appears in the document rather than only at the very top, so a report that opens with its heading can never blank the card.
Security
  • MCP OAuth: the scopes an OAuth-connected app receives are now clamped to the standard customer-agent capability set at authorization time. A requested scope beyond that ceiling (for example a wildcard *:*) is dropped rather than granted, so an OAuth token can never carry more access than the OAuth flow is meant to issue. Apps that request the advertised scopes are unaffected.
Added
  • Pages catalog: LineChart accepts yMin + yMax to pin the y-axis range, so a tight series can fit its scale to the data (no forced zero baseline) and show its variance.
  • Pages catalog: new Tabs component (labelled panels the viewer switches between — each tab carries an inline catalog subtree) and Carousel component (rotate sibling charts one slide at a time), plus QuoteList.expandable + QuoteList.detailFields for a per-quote read-in-place detail dialog with richer attribution.
  • Workspace README — a one-read orientation map of your workspace. Read workspace_readme://current over MCP (or GET /workspace/readme over REST) to get the workspace's pages, knowledge documents, agent workflows, and callable operation namespaces as name-plus-summary rows, together with a rendered markdown digest. Assembled live on every read, so it is never stale; sections your key lacks scope for are omitted with a reason instead of failing the call.
  • Salesloft connector. Connect a Salesloft workspace with an API key to sync emails, calls, conversations, transcripts, and meetings into your workspace.
  • Every Pages chart now accepts a height preset (sm | md | lg) and an ordered colors token list (chart-1..chart-5, positive, negative, neutral) so agents can size charts and paint series/slices deliberately — tokens resolve to theme variables, so white or raw hex are unrepresentable. RadarChart additionally accepts max to pin the radial scale (e.g. max: 5 for a /5 rubric fills the polygon), and FunnelChart no longer overruns its card border.
Changed
  • Living documents generated from an uploaded doc (knowledge_base.make_living) now author a deterministic, grounded refresh workflow: each regenerated version computes its figures directly from your data and verifies every number, so a living doc no longer states a figure that isn't backed by the underlying data.
  • Backtest-generated knowledge-base versions are now stamped with the as-of date they reflect. A cumulative backtest sweep of a living document produces a properly dated version history, and each slice builds on the prior one (knowledge_base.upload defaults version_as_of to the run's backtest cut; an explicit value still wins, and live runs are unchanged).
  • Competitive Divergence Map workflow (v2) now tracks divergence over time. Every run captures a structured divergence snapshot — counts by kind (internal-only / market-only / magnitude / polarity), themes marked new / persisting / resolved vs the prior run, and render health — in three places: the workflow run's outputs (blueprint_run://<id>), a machine-readable header on the living doc (so the knowledge-base version history is a parseable series), and an accumulating "Divergence over time" chart on an auto-upserted dashboard page. The run also factors in more data: per-rival market research (bounded by the new max_rivals input), your positioning docs from the knowledge base, and the Industry sweep corpus — and the category market read is force-refreshed so a scheduled run never records a cached brief as a fresh snapshot.
  • The Account Tier Intelligence and ICP Signal Report living documents are now generated by the deterministic step-executor — every figure (close-likelihood scores, engagement momentum, champion strength, win rates by vertical) is computed from your data in SQL/transform cells and number-verified, so the document can no longer state a value that isn't backed by the underlying data. Account tiering (Expand / Nurture / Watch / Triage) is computed deterministically from the real signals rather than narrated by the model.
  • Fixed a blueprint-runner bug where a workflow's transform step could not read a workflow input via the canonical $inputs.X reference, which could cause a defaulted threshold to be treated as unset.
  • The Deal Qualification Report living document is now generated by the deterministic step-executor: every figure (coverage-band distribution, per-dimension MEDDPICC/SPICED coverage percentages, binding constraints) is a verified data cell, the thinnest-coverage deals are named with real company names (bridged from the interactions surface), and dimension coverage is reported as a clear percentage of scored companies rather than an ambiguous count.
  • The Win / Loss Report living document is now generated by the deterministic step-executor. The two cross-surface analyses it previously asked the model to compute by hand — the won-vs-lost MEDDPICC coverage spread and the per-competitor win rate — are now pre-computed as verified transform cells, so the report states real, grounded win/loss differentiators and win rates (each gated to ≥8 accounts for reliability) instead of derived figures.
  • The Pipeline Health Report living document is now generated by the deterministic step-executor: the open-pipeline total is a verified totals query (rather than the model summing stage rows in its head), the at-risk / long-shot deals are named with real company names, and every stage figure is a grounded cell the number gate verifies.
  • The Win / Loss Report now handles a thin closed-won sample honestly: when there are too few scored closed deals to compute reliable MEDDPICC win/loss differentiators, the report says so and focuses on the qualitative loss themes instead of reporting shaky percentages, and it no longer derives win-rate or share percentages from the raw deal counts.
  • Living-doc workflows now share a canonical data-surfaces map (where each GTM data surface lives, its grain, purpose, scale, and join key) plus the query patterns that keep a computed figure correct under the query row cap — so a generated or hand-authored workflow aggregates in SQL over the full population instead of silently truncating a per-row query.
  • Added a truncation guard to the deterministic workflow executor: a data.query whose result exceeds its LIMIT (detected by a peek) is flagged, so a workflow can no longer state a real-but-wrong number computed over a silently-truncated sample (deliberate top-N queries opt in via allow_truncation).
  • The Account Tier, Win / Loss, Deal Qualification, and Pipeline Health living documents now compute their cross-surface figures over the FULL population instead of a silently-truncated sample: metrics that need every company (win/loss MEDDPICC differentiators, account tiering, named thin/at-risk deals) use an id-list bridge that aggregates in SQL over the exact set, so — for example — a tenant's win/loss report now reflects all of its won accounts rather than an arbitrary slice.
  • The living-doc query truncation guard is now enforced (not just observed): a workflow step whose query returns more rows than its limit fails the run instead of silently producing a document over a truncated sample. When a generated living doc hits this, the self-tuning generator now recognizes it as a truncation failure and revises the recipe to aggregate over the full data (via SQL aggregation or a bounded lookup) — so a generated doc self-heals to correct figures rather than shipping partial ones.
Fixed
  • Turning a knowledge base document into a living document now reliably produces a runnable refresh workflow. The generated recipe is constrained to the tools and step types the living-doc engine actually executes, and a malformed draft returns a clear validation error instead of failing the request.
  • Living GTM docs (Voice-of-Customer, ICP Signal) now report won/closed counts, win rate, and revenue from your CRM deal records, cleanly separated from the conversation-corpus outcome view — no more conflicting "won accounts" figures between sections. Currency is dollar-formatted, quoted customer figures are no longer mis-flagged, and the reports no longer state industry/segment splits that aren't in the data.
  • Living GTM docs now state every figure exactly as computed — the narrative no longer rounds or approximates a number (e.g. "~5,600" for 5,904), so the prose always matches the underlying data.
Fixed
  • The divergence map now renders on the `search` action too. A prior fix pinned the synthesis model path for the enrichment briefs but missed the plain search action, so search could still route its internal-vs-market synthesis to a slower back-end that timed out and returned an empty divergence map. All external-search synthesis paths now use the same fast, dedicated model, so the fused internal-vs-market view renders consistently on search as well as the company/person/topic enrichments.
Added
  • knowledge_base.promote and knowledge_base.dismiss. Approve a proposed living-doc version (promote it to the current, cited version) or dismiss it, from the console or REST.
  • Living-doc subscribers. Read and replace the members/roles notified when a document has a new version to review (GET .../knowledge-base/:id/subscribers, PATCH .../knowledge_base/documents/:id/subscribers).
  • Saved views. Personal named filter presets for the Living Knowledge list: knowledge_base.list_saved_views / create_saved_view / update_saved_view / delete_saved_view.
  • Documents now carry an owner and an audience role, and surface whether a proposed version is awaiting review.
  • Two new `data.query` surfaces: `competitor_mentions` and `deal_score_weights`. competitor_mentions (one row per company + competitor) lets you build an internal win-rate-by-competitor read straight from your conversation corpus — no external search — bridging to deal outcomes on company_id. deal_score_weights exposes the learned deal-score model: which signals predict close-likelihood for your tenant (a factor→weight map) and how well it performs (holdout vs baseline AUC). Both are discoverable via data://schema and honor data-scope — competitor_mentions is company-scoped like the deal-grain surfaces, and deal_score_weights is a business-level admin/GTM read.
  • POST /agent-blueprints/:id/activate — activate or deactivate a workspace workflow (body { enabled?: boolean }, default true; creates the activation row when enabling one that was never activated). Powers the Living Knowledge "Activate" affordance on a living document whose producing workflow is off.
  • Living Knowledge documents now carry source_run_status (the producing workflow's most-recent run status) and workflow_active (whether that workflow is enabled), so the list can badge "Last run failed" and filter "Runs failing".
  • Two new Page components: `CopyButton` and `Dialog`. CopyButton copies a value (a literal or a bound cell, e.g. a recommended message) straight to the clipboard on click — the one-click "grab and paste" affordance. Dialog adds a trigger button that opens a modal rendering its child components, so a page can tuck supporting detail (quotes, a table, a chart) behind a click-in instead of showing everything at once. Both are client-side only — they run in the browser and invoke no server operation.
  • Industry: dropped the market-themes and divergence-map reads (industry://themes / industry://divergence). Added a raw Industry Signals read (industry.list_signals / industry_signal://list, REST GET /industry/signals) over the collected corpus - the source documents the sweep landed, filterable by domain, source, angle, sweep, and collected/published date, with a free-text search over title and body, and paginated. industry.refresh and the industry_run:// run-history reads are unchanged.
  • Sync a single knowledge-base document to Notion on demand, and open it in Notion. If your workspace has Notion Knowledge Sync connected, you can now push one document to Notion immediately — instead of waiting for the automatic sync or the hourly reconcile — and read its live mirror state: whether it has a Notion page yet, a direct open-in-Notion link, and whether the mirror is behind the current version. New endpoints POST /notion-sync/documents/:document_group_id/sync and GET /notion-sync/documents/:document_group_id (plus the notion_sync://documents/<document_group_id> read resource for MCP).
  • New `industry.get_signal` operation — read one collected market-signal document in full. The industry signals list now returns a short preview (snippet) per document to keep the feed fast; fetch a single document's complete text on demand by id via industry.get_signal (industry_signal://<id> on MCP, GET /industry/signals/:id on REST), so the detail view shows the whole article instead of a truncated blurb.
  • Two new `data.query` surfaces: `deal_stage_history` and `ref_deal_stages`. Query your CRM deal stage-transition history and the stage dimension directly, so you can answer where deals actually die (the stage a lost deal sat in right before it closed) and how long they spend in each stage — questions the current-stage deals snapshot can't. deal_stage_history is one row per stage change (deal_id, the raw CRM deal_stage id, changed_at); ref_deal_stages maps each raw stage id to its human label, funnel position, and won/lost/open class. Read data://schema for the columns and example queries.
  • New starter workflow: Market Intelligence Digest. A weekly living document built from your Industry signals — the market-signal documents the platform sweeps from the web, news, and SEC filings. It surveys the collected feed, drills into the documents most relevant to your business (and named competitors) in full, and writes an executive read + what's-moving + competitor-watch digest that auto-promotes each run. Fork it from the starter workflows and enable its weekly schedule (Monday 08:00), or run it on demand. See the new Industry signals guide (Using Amdahl, under "See what it found") for the full workflow, the API, and how agents read the feed.
  • Remove a single document from Notion — and keep it removed. If your workspace has Notion Knowledge Sync connected, you can now remove one knowledge-base document from Notion without turning off the whole sync: it trashes that document's Notion page and mutes the document so no automatic sync, hourly reconcile, or backfill re-mirrors it (the fix for a _living_ document that would otherwise come back on its next update). Re-syncing the document brings it back — Remove and Sync are a clean toggle. New endpoint POST /notion-sync/documents/:document_group_id/unsync.
Changed
  • Faster external search. Market/topic research (search + enrich_topic) now spends less time ranking and synthesizing results: the relevance-ranking pass is capped to the strongest candidates per source, the source fan-out closes out sooner, and the summary step works from a tighter set of citations — trimming response time while keeping the same broad source coverage.
  • External search responds faster. The relevance-ranking step in market/topic research (search + enrich_topic) now works from a leaner pool of top candidates per source, cutting the time spent scoring results. Source coverage is unchanged — every source still contributes — so you get the same breadth of citations, sooner.
  • External search responses now include per-stage timing. A search result carries an optional _timing breakdown (query enrichment, source fan-out, ranking, relevance gate, and synthesis — with synthesis further split into clustering and each summary pane). It's additive and safe to ignore, but lets you see exactly where a call spends its time when diagnosing latency.
  • Every Amdahl workflow now works goal-first and recursively. All shipped workflows — plus any you fork from them, and any generated from an uploaded document via "make it living" — now share one investigation method: anchor on the goal, tie claims to a measurable outcome vs a baseline, and treat a finding as a lead (drill into what causes it and how to act on it) rather than filling a fixed template. The discipline is centralized in one place, so it keeps improving across every workflow at once.
  • GTM Health Report is now outcome-first and adaptive. The standing GTM health report was rebuilt to reverse-engineer the questions that matter for _your_ business and test each against a real outcome — win rate, cycle velocity, deal size — instead of leaning on conversation-theme summaries. It now computes the funnel and where it leaks by stage, the drivers that actually predict a win (stakeholder breadth, champion presence, and more), an internal competitive read, and message performance — all from your own calls, emails, and CRM, with named accounts and verbatim quotes behind every finding. Competitive position and messaging no longer come back "not computable" and there is no external dependency to fail on; every dimension is derived from your corpus.
  • Living Knowledge provenance. A living document's source_workflow now carries the producing workflow's name and blueprint_id, so the "Fed by <workflow>" label can name and deep-link the workflow instead of showing a generic "an Amdahl workflow" label.
  • Living Knowledge ownership. Backfilled the accountable owner on documents created before the owner field existed (using the workflow's activating user, else the uploader), so the Owner field is populated rather than empty.
  • GTM Health Report now reports stage velocity (median time-in-stage, won vs lost, and the stage where deals stall) and win rate _by competitor_ (grouped by rival type, with any win-rate inversion called out) — not just a rival mention list. It no longer emits "TBD" for time-in-stage; when a workspace lacks stage-transition history it says so and falls back to the overall won-vs-lost cycle spread.
  • Pipeline Health Report now flags stalled stages using the median age of open deals per stage.
  • GTM Health Report now reliably includes stage velocity (time-in-stage, won vs lost) and win rate by competitor — these are now required outputs of the diagnostic rather than optional, so they appear on every run where the data supports them (with an honest coverage-gate fallback when it doesn't).
  • Living-doc workflows authored by the "make it living" generator are now data-field-aware: the generator knows the tenant's high-value CRM signals (champion / economic-buyer flags, close-likelihood + binding-constraint scores, per-stage dwell time, and competitor win-rate) and composes queries that use them — so a generated living doc produces the sharp cuts, not a generic theme summary.
  • GTM Health Report now always includes a stage-velocity cut (median time-in-stage, won vs lost, and the stage where deals stall) and a win-rate-by-competitor table — computed as deterministic steps so they appear on every run, not just when the agent happens to author them.
  • Living documents that are analytical diagnostics now fully regenerate their numbers each run instead of minimal-editing the prior version, so newly added analysis sections take effect immediately (a full_regen_each_run blueprint setting).
  • Several Living GTM Doc reports now read high-value CRM signals they previously ignored, so they produce sharper analysis:
  • Win/Loss Report — won-deal MEDDPICC differentiators (which qualification dimensions separate wins from losses) and win rate by named competitor.
  • Deal Qualification Report — the most systematically undercovered MEDDPICC dimensions, computed directly rather than guessed.
  • Account Tier Intelligence — tiers now weigh stakeholder quality (champion / economic-buyer presence, champion score) and 30-day engagement momentum, not just raw activity volume.
  • Weekly GTM Digest — flags stalled pipeline stages by median deal age.
  • Competitive Divergence Map — names the actual recurring rivals (not just a competition-present count).
  • ICP Signal Report — leads with the firmographics of _won_ accounts (who actually buys) and flags ICP drift vs the overall funnel.
  • Persona Voice Deep-Dive — the role mix now surfaces champion / economic-buyer counts per role level.
  • The natural-language SQL helper (data.ask) learned two new analysis plays: per-stage dwell/velocity (from the interactions temporal-stage surface) and win-rate-by-competitor (bridging competitor mentions to deal outcomes).
  • Living documents now self-heal. If a scheduled or on-demand living-doc run is cut short before it writes its document (e.g. a transient rate limit on a large workspace), the platform now automatically re-runs it (bounded) instead of silently leaving the document stale — and a retried run is nudged to work more economically so it finishes within budget.
  • External search is faster and its divergence map now always renders. The result-ranking step switched from an LLM scoring pass to embedding similarity, cutting a large chunk of latency off every search / enrich_topic call. That freed budget goes to the fusion synthesis: the internal-vs-market divergence map (the tenant-only signal a plain web search can't produce) previously timed out and came back empty on most calls — it now completes and ships populated.
  • Content Calendar links straight to your drafts. The Content Calendar workflow's page now opens the full drafts from your Knowledge Base — the drafts living document stays the single source of truth, and a "Read the drafts" panel on the page links out to it, so the schedule and the full copy never drift apart. The knowledge_base.upload API now also returns a console_url deep link to the uploaded document, so an automation can point back at exactly what it just saved.
  • External search's divergence map now reliably renders (and the call stays safely under the timeout). Follow-up to the previous rerank change: the internal-vs-market divergence map — the tenant-only signal a plain web search can't produce — was still coming back empty because the synthesis panes generated more text than they had time to finish. Their output budgets were trimmed so each pane completes, and the overall time budget was tightened so a call can no longer run up against the 60-second ceiling and return nothing.
  • Living GTM documents now tie themes to outcomes, not just volume. Five of the standing GTM living documents got sharper by grounding what they surface in won-vs-lost deal outcomes instead of raw frequency:
  • VoC Cluster Report now leads with the themes that show up in _won_ deals and ranks by win-correlation (with a loss-correlated risk section), instead of ranking by how often a theme comes up.
  • GTM Proof & ROI Library now leads with competitive-displacement wins — accounts you won _and_ where a rival was named — as the strongest reusable proof.
  • Champion & EB Voice Digest now includes a dedicated objections & pushback section mined from what senior stakeholders actually push back on.
  • Cross-Persona Divergence Brief now calls out which persona layers, when engaged, predict wins — so multi-voice content leads with the framing that closes.
  • GTM Health Report now quantifies the top jobs-to-be-done as coverage on won vs lost deals ("pain quantified on 41% of won deals vs 6% of lost"), instead of a free-text ranking. Each keeps the same honesty discipline — a rate is only stated once a bucket has enough closed deals; below that it reports raw counts and marks the signal thin.
  • Read your Content Calendar drafts right on the page. The Content Calendar workflow's page now bakes each piece's full draft into the calendar itself: click any day to open a dialog and read that day's pieces in full (multiple per day supported), with a link to open the canonical, versioned copy in your Knowledge Base. The separate "Read the drafts" button is gone — the content reads in place. The Calendar and Board page components (authored via the pages tool) gained two optional props for this: contentField (the row column holding a piece's full markdown body) and docUrlField (a per-row deep link to the living doc); when set, each item becomes clickable and opens the read-in-place dialog.
  • The GTM Priority Briefing now ranks by grounded confidence, not a guess. When the weekly briefing scores each priority by impact × confidence × urgency ÷ effort, the _confidence_ term is now set from real signals: each source document's own stated confidence (the GTM Health Report's score header, per-finding high/medium/low, and "thin/directional/not measured" honesty markers), whether two or more documents independently corroborate the insight, and the knowledge-base retrieval match score. A loud-but-thin signal can no longer outrank a quiet-but-corroborated one, and every priority shows why its confidence was set the way it was.
Fixed
  • Issues inbox filtering. GET /console/issues?category=... now accepts the app-internal issue categories (e.g. proposed_kb_version) alongside the pipeline quality-event categories, so filtering the inbox by one returns results instead of a 400 invalid_category. This unblocks the Living Knowledge "needs review" count.
  • Living Knowledge workflow activation now reflects execution reality. The workflow_active field on documents and the POST /agent-blueprints/:id/activate toggle read/write blueprint_schedules.enabled — the cron scheduler's source of truth — instead of the workspace_workflows curation badge. Previously the flag under-reported which workflows actually run, and "Activate" moved a badge without starting or stopping any scheduled run.
  • The Content Calendar page's "Read the drafts" link now points at your real Knowledge Base doc. The deep link back to the full drafts is attached on the same path the scheduled workflow actually runs through, so the page links to your living document instead of a guessed URL. The knowledge_base tool (both the API operation and the MCP coarse tool) now returns a console_url for the uploaded document, so any automation can link back to what it just saved.
  • External search's fused brief (market summary, customer voice, and the internal-vs-market divergence map) now actually renders. The synthesis step was opening streaming connections that never completed in the production environment, so every search / enrich_topic call fell back to an empty brief. The synthesis calls were switched to a non-streaming request that completes reliably, so the market summary, your customer-voice pane, and the divergence map come back populated.
  • External search's divergence map renders more reliably. Follow-up to the non-streaming synthesis fix: the fusion panes now complete, but under concurrent load one pane occasionally ran a hair past its time budget, so the full brief (market summary + customer voice + divergence map) came back complete only intermittently. Their time budgets were given a small amount of headroom and their prompts trimmed slightly so each pane starts and finishes sooner — making the complete brief, including the internal-vs-market divergence map, render consistently.
  • External search's divergence map now renders reliably on every call. The final fix in the fusion-synthesis chain: the internal-vs-market divergence map (the tenant-only signal a plain web search can't produce) was completing only intermittently because all three synthesis panes generated at once and competed for throughput. The panes now run two-at-a-time — the divergence map gets a dedicated slot while the two summary panes share the other — so the divergence map finishes and comes back populated consistently.
  • Documents, pages, and artifacts produced by a workflow run are now attributed to the person who triggered the run. Previously anything a headless/scheduled workflow authored was credited to the workspace owner (the identity the runner acts under), so a report someone else kicked off showed the owner as its author. Outputs now carry the actual triggering user as their creator, while owner attribution is preserved when a run has no human trigger.
  • The Content Calendar workflow can now be forked into your workspace. Forking it previously failed validation because its tool allowlist named an internal coarse-tool action (context.entries_list) instead of the canonical operation id (context_entry.list); the entry is corrected, so POST /agent-blueprints/fork (and the blueprints fork action) now succeed. Behaviour of existing runs is unchanged. A new starter-forkability test pins every shipped starter's tool allowlist against the operation registry so no starter can ship unforkable again.
  • The market-intelligence divergence map now renders reliably on every search. External search and the company/person/topic enrichment briefs synthesize their internal-vs-market "divergence map" — the view that fuses your own CRM and call corpus against the public web — on a fixed, dedicated model path. Previously that synthesis could be silently routed to a slower back-end that couldn't answer within the call's time budget, so the divergence map came back empty. It now runs on the fast path every time, so the fused view (the part generic web search can't produce) shows up consistently.
  • The "open the living doc" link on a Knowledge Base upload response now resolves to the document instead of 404ing. The console_url attached to a knowledge_base.upload result (on both the operation and the MCP coarse tool) was built from the document's document_group_id, but the workspace KB detail route resolves a document id — so the link opened "Document not found". It now uses the freshly-written version's document id, so a living-doc → page workflow (e.g. the Content Calendar) links straight to the current version of its full drafts.
  • Five more workflow starters are now forkable. Following the content-calendar fix, the plan-and-draft-window, GTM diagnostic, positioning scorecard, Substack thought-leader newsletter, and content-calendar-routine starters each listed a tool in their allowlist that was not a registered operation (e.g. web_search, data.explore, and internal run-lifecycle controls), so forking them into your workspace failed the allowlist validator. Their allowlists now name only canonical operations, and a guard test (which was previously a no-op) now actually checks every starter, so each one can be forked.
  • "Your workflows" now lists every workflow that's actually running. A scheduled workflow only appeared under "Your workflows" if it had been switched on through the in-app activation toggle. Workflows put on a schedule another way — for example a recurring living-document report — kept running on their cron but were shown under "Library" as if they were still just an un-adopted template. The Workflows page now decides what's active directly from the schedules that fire, so anything running for your workspace shows up under "Your workflows".
Added
  • `data.query` on the theme clusters surface now supports `as_of` backtests. A SQL read like SELECT label, velocity FROM clusters … with an as_of date now resolves the cluster generation that was live on that date (via the warehouse clusters_as_of table-function) instead of being refused — so a blueprint that reads themes with raw SQL backtests cleanly. The append-only theme_lineage history surface still requires as_of reads to go through data.cluster_search / data.cluster_detail.
  • Living GTM Docs — 18 workflows + matching pages. Eighteen new Amdahl-shipped workflows generate versioned, human-promoted living documents in your knowledge base on a cadence — VoC cluster report, champion/EB voice digest, pipeline health, deal qualification, win/loss, ICP signal, account tiering, positioning scorecard, persona voice deep-dives, cross-persona divergence, competitive divergence map, ecosystem map, topic research briefs, GACCS content briefs, AEO health, the weekly GTM digest, the GTM proof & ROI library, and the meta GTM priority briefing. Each ships with a live page template (browse via page_template://list / author with pages.create_from_template) so the same intelligence is available as a live console dashboard, not just a narrative.
  • Pages can read clusters and the knowledge base. Page declared queries now support cluster_search and kb_search sources (in addition to sql and artifacts), so a page can surface conversation themes and reference-library matches live.
Fixed
  • External search now draws on every source it advertises, and returns faster. Market/topic research (search + enrich_topic) now fans out across the full set of registered sources — including the developer and review platforms (GitHub, Hacker News, dev.to, Medium, Stack Overflow, G2, Capterra, TrustRadius, Product Hunt) alongside web, Substack, and YouTube — so those citations actually show up in results. The same change removes a bottleneck in the relevance step that was roughly doubling response time on these calls.
  • Theme-based Living GTM Docs now backtest correctly. The Voice-of-Customer, Weekly GTM Digest, and GTM Priority Briefing living documents read your conversation themes through the point-in-time theme index, so running one as an as-of backtest reconstructs the themes that were live on that date instead of failing on the theme step. Their scheduled (live) runs are unchanged.
Added
  • New industry intelligence surface. A daily sweep gathers what the wider market is discussing (web, news, and SEC filings) and maps it against what your own customers raise on calls. Read it via the new industry.get_divergence (the four-axis market-vs-customer divergence map) and industry.list_themes (the clustered market themes) operations, or trigger a fresh sweep on demand with industry.refresh.
  • Substack and YouTube are now searched in external research. Founder newsletters, talks, and demos are strong go-to-market signal, so external_search (the search and enrich_topic actions) now covers Substack and YouTube alongside web, news, LinkedIn, and Crunchbase.
  • More platforms in topic and market search. Topic and market searches now also pull from Medium, Hacker News, dev.to, GitHub, Stack Overflow, G2, Capterra, TrustRadius, and Product Hunt (alongside the existing Substack and YouTube sources), giving broader coverage of practitioner writing, developer discussion, and buyer-intent review sites. New platforms are added through a single registry, so the source set will keep growing without changes to how you search.
  • agent_blueprint.list entries now include forked_from_amdahl — true when a workflow was forked from an Amdahl starter (via "Use template"), so you can tell an Amdahl-derived workflow from one you built from scratch.
  • Trigger and watch an industry sweep over MCP. The new industry tool's refresh action kicks off a "What's Going On In The Industry" sweep on demand instead of waiting for the daily run, and returns a run_id. Poll the new industry_run://list + industry_run://<id> reads to watch the run to completion and read its per-stage step log; read industry://themes / industry://divergence for the clustered market themes and the market-vs-customer divergence map.
Changed
  • External search is faster and no longer times out on long calls. Broad web and news results now run on the faster search backend (when configured), removing the slow source that previously gated every fan-out. The per-call deadline_ms is also clamped so the full pipeline (search + synthesis) stays under the tool-call ceiling — a large deadline_ms no longer causes the whole call to time out and return nothing; slow sources just surface as incomplete instead.
  • External search synthesis is faster and less likely to time out. The market/internal synthesis step now works over a tighter, top-ranked set of sources, so the brief comes back sooner — particularly on broad topics where the wider source mix previously pushed synthesis past its budget.
Removed
  • Reddit and X/Twitter removed from external research. Neither has a serviceable data provider, so external_search no longer searches them; the enrich_person action no longer attempts to resolve an X handle.
Fixed
  • External search no longer drops sources under load. When many searches run at once (e.g. competitor/market research alongside the industry sweep), requests are now smoothly rate-limited and automatically retried instead of failing, so a burst costs a little extra latency rather than missing results.
  • Embedded Pages now load on any site. A live Page embed (the <iframe> snippet from a Page's Embed button) fetches its data cross-origin, which was being blocked unless the embed was hosted on an Amdahl domain. The public page-embed API now permits cross-origin requests from any origin, so a pasted embed renders wherever you put it.
  • Fixed the embed signing-secret screen. Creating a signing secret showed a blank value and the secrets list showed an undefined prefix, due to a mismatch between the API responses and the console. Both now return the correct shape, so the one-time secret and the prefix display correctly.
  • `data.query` MCP calls now capture the question that triggered them. Previously, a data query (raw SQL) call could silently drop the verbatim user message even when your client supplied it, because that action logs through its own path rather than the shared one. The resources, social, pages, notifications, industry, and blueprints tools now also accept the same optional context so a client populating it gets full coverage across every tool, not just data / context / knowledge_base / external_search.
Added
  • Run and schedule Workflows directly from MCP. The blueprints coarse tool gains a run action (a one-shot headless run) alongside create_schedule for recurring cron runs, gated by the new blueprints:execute scope on the customer-agent key bundle. A workspace MCP key can now launch and schedule its own Workflows without any broader delegation grant.
  • Author a Page from a template over MCP. The pages coarse tool gains a create_from_template action that materializes a vetted, Amdahl-shipped page template by template_slug; browse the available templates via the page_template://list resource first.
  • Read drafted content pieces back over MCP. A new content_piece:// read scheme (content_piece://list + content_piece://<id>, gated by artifacts:read) surfaces the content pieces a content-calendar Workflow drafted, so an agent can review a run's output without leaving MCP.
  • agent_blueprint.list now returns is_active and source on every entry, so you can tell which workflows are active in a workspace and whether each is an Amdahl-managed default (source: "amdahl") or your own (source: "custom").
  • New workspaces start with the GTM Health Report workflow active by default.
Fixed
  • External search now returns social results again. Reddit, X/Twitter, LinkedIn, and Crunchbase sources are served via web search, so external_search (search and the enrich_* actions) surfaces social signal instead of coming back empty.
Added
  • Action page component + the pages.invoke_action operation, so a Page can now do something, not just display: an Action button invokes a write/compute operation (e.g. artifacts.update, agents.run_blueprint) as the clicking viewer. A button can only invoke an op the page declares, and the viewer must independently hold that op's scope — so a Page can never become a way to call an operation a viewer couldn't call directly.
  • pages.create_from_template operation. Author a new workspace page from a vetted Amdahl page template (a catalog-only spec) by slug — so the in-app copilot can stand up a page (e.g. a content-calendar page) and, paired with a workflow schedule, set it to auto-update on a recurring cadence.
  • { $row: 'field' } Action input. A page Action button can now act on the row the viewer clicked — its declared inputs resolve from the clicked row, so per-row buttons (move this piece, archive this row) no longer need a hard-coded id.
  • Page Action buttons can now invoke `workflow` operations (e.g. agents.run_blueprint), not just write/compute ops — so a Page button can kick off a workflow run, not only edit a row. The rule is now simply: a button may invoke any op that _does_ something (write / compute / workflow); a read stays the page's display channel. The same boundary holds — the op must be declared on the page and the clicking viewer must independently hold its scope (so a "Run workflow" button needs workflows:delegate).
  • Page Action buttons can also target external providers via an external:<id> address — a pluggable seam for actions that reach out (post to a channel, push to a CMS, fire a webhook), gated by the provider's required scope. A reference external:echo provider ships so the path is live; real providers are added behind it.
  • The Content Calendar page template gained two buttons — "New piece" (creates a placeholder content_piece) and "Run routine" (fires the new `content-calendar-routine` workflow on demand) — plus that workflow itself, a scheduled (weekly, off by default) routine that plans and drafts the upcoming window. Enable its schedule to keep the calendar full hands-off.
Fixed
  • Page embed signing secrets now load and manage correctly from Settings -> Embedding in the console. Listing, creating, and revoking a workspace's page-embed signing secrets works again (the surface previously failed with a "business_id is required" error).
Added
  • Calendar and Board page components, plus an artifacts query source, so a Page can render your workspace content (e.g. content pieces) as a live calendar or kanban board over the Postgres artifact store — not just the warehouse sql source.
  • A ready-made Content Calendar page template (content-calendar) that pairs the two views over your content_piece artifacts. Fetch it via pages.get_template and author a Page from it.
  • Positioning experiments. Read how your positioning claims perform — what buyers echo back versus what actually wins deals — across three new surfaces. Query the data directly: data.query now addresses two positioning surfaces, positioning_claims (your claim inventory: text, label, category, status) and positioning (per-claim performance: leading buyer-reception lift and matched-control lagging win-rate lift), discoverable via data://schema. Run the new Positioning Scorecard workflow for the labeled "what we say vs what lands vs what wins" read, including the inversion where the story you lean on most is the one that loses. Or author a live Positioning Experiments page from the new template (claim inventory, confidence-tier breakdown, and a reception-vs-outcome chart). The win-rate signal is a matched-control estimate (a correlation, not proof) — the surfaces are built to read claim _ordering_ over absolute numbers and flag a low-coverage signal as directional.
Changed
  • Positioning experiments now separate measured messaging from candidate positioning. Every claim carries a source: mined_llm claims are pulled from what your team actually says on calls (their lift is real messaging performance), while seed claims are hand-authored candidate positioning the engine matches by meaning (their lift is a "would this land" test, not a performance review). The Positioning Scorecard workflow and the Positioning Experiments page now keep the two in clearly separate sections and never present a seeded claim's numbers as measured performance, and the data.query schema guidance teaches the distinction so the assistant interprets it correctly in chat too.
Added
  • Connection data summaries. Each connected source now exposes a glanceable, per-connector snapshot of its own synced data - counts and totals (deals and pipeline value, meetings, messages, tickets, pages), the most recent items, and a weekly activity sparkline - at GET /connections/:id/summary and the connection://<id>/summary MCP resource. Computed live from your synced data, it shows what is unique to each connector type.
Changed
  • Relationship intelligence now lives directly on the data surface. Questions like "who is the champion or economic buyer on this deal", "what did a specific stakeholder actually say", "who do we keep losing to", and "which accounts are evaluating a competitor" are answered with data query / explore over the stakeholder columns folded onto the interactions surface (is_champion, is_economic_buyer, champion_score, role_level) plus the deal_qualification champion / economic-buyer / competition dimensions — taught in the schema explorer and the GTM routing guidance so the agent reaches for them automatically.
  • The data tool's schema catalog now advertises the native record_type column (call / email / meeting) for filtering interactions by category. Category-filter prompts and examples use it instead of the older interaction_type remap, so questions like "emails about pricing" or "calls per company" produce clearer, more accurate SQL.
Removed
  • Removed the include_knowledge_graph option on data cluster search. It read from a decommissioned dataset and added no signal beyond the stakeholder columns now folded onto the interactions surface, so the tool no longer accepts it.
Fixed
  • data tool: cluster_detail now accepts the source_id returned by cluster_search verbatim. Previously the cluster_id argument only accepted a number, so drilling into a theme failed with "Cluster not found" because theme ids are opaque strings — pass the source_id straight through and the drill-in works.
Added
  • A public changelog at docs.amdahl.co/changelog - a searchable, branded timeline of what's shipped, refreshed automatically with each release.
  • Notion Knowledge Sync — one-way mirror your Amdahl knowledge base into a Notion database in your own workspace. Connect Notion over OAuth, pick a parent page, and every promoted document syncs into Notion automatically, with an hourly reconcile that backfills and self-heals drift. Configure / monitor it over REST or read its status + activity ledger over MCP (notion_sync://).
  • The assistant can now draw on more of your data directly for richer answers: the deal stage as it stood at the time of each conversation, theme insight summaries and headline hooks, theme lifecycle and lineage over time, and additional per-company deal-qualification detail.
  • The assistant now answers deal-stage and pipeline-funnel questions at the deal level with human-readable stage names ("Discovery", "Closed Won", …), win/loss outcome, and true funnel ordering — instead of raw internal CRM stage codes — so per-stage pipeline value, win rate, and funnel breakdowns come back clean and correctly ordered.
  • The assistant can now name themes directly when tracking how they evolve over time (a stable theme name now travels with each theme's history), and deal-stage / pipeline-funnel questions now return human-readable stage names for Pipedrive-based workspaces too — so theme-trend and funnel answers read cleanly across more CRMs.
Changed
  • knowledge_base.upload: a document's description is now its stable purpose ("what is this document for"). When you append a new version to an existing living document, the purpose is carried forward automatically and any description you pass is ignored — record per-version "what changed" notes in version_changes instead.
  • Internal cleanup: retired the legacy V1 cluster-evidence channels behind external research. The customer-voice / divergence-map pane now sources cluster evidence entirely from the V2 pipeline — no customer-visible change, since these channels already returned empty once the V1 pipeline was decommissioned.
Fixed
  • The GTM health report now reads correct, human-readable deal stages from your CRM (e.g. "Discovery", "Research", "Closed Won") instead of raw internal stage codes, and no longer splits a single account across multiple stage rows.
  • Asking the assistant about themes, deal stages, or deal qualification is now materially more reliable. It reads the correct underlying fields (so theme-lifecycle and stage questions no longer fail on some workspaces), keeps the "how complete is this deal's qualification" and "how likely is it to close" scores distinct, and stops surfacing internal placeholder columns that don't apply to your data.
  • Fixed "Make it living" on a Knowledge Base document failing with a schema-validation error. The synthesized refresh workflow now always declares a valid output, so turning a static doc into a self-refreshing living doc succeeds reliably.
  • Fixed external_search (search / enrich_company / enrich_topic) dropping its web results and competitive divergence map. The web source's underlying search call was rejected by the model API; it now succeeds, so the divergence map and web summary come back reliably.
  • Knowledge base search and other embedding-backed features now reliably use the current embedding model. Previously, newly uploaded documents could fail to become searchable, and semantic search could silently fall back to keyword-only matching.
  • Restored the internal-evidence keyword aggregates in external research (the customer-voice / divergence-map pane) after the V1 pipeline decommission — they now read the live V2 unified view instead of the dropped V1 dataset.
Added
  • Blueprints - shareable, forkable recipes an agent reads and walks step by step. Author, validate, and fork via the blueprints tool family and REST; seven starter blueprints ship out of the box.
  • Connections - a unified connector catalog and connect / disconnect surface spanning CRM, calls, comms, docs, support, and social (X / LinkedIn). Poll connection health and sync-run history over connection:// reads and REST.
  • External search fuses your CRM + call corpus with live market sources into a single brief (the divergence map). Four actions: search, enrich_company, enrich_person, enrich_topic, with an opt-in SSE event stream.
  • Notifications - agents can email workspace members (member-only, rate-capped, idempotent) via the notifications tool and POST /notifications/email-member, and read the send ledger.
  • Pages - workspace-authored, data-backed UIs composed from a catalog of components (tables, charts, stats, signal maps) bound to your own queries. Author + validate via the pages tool family and REST, publish, and embed.
Changed
  • The performance.* operations were renamed to social.* (clean break, no aliases) for social account tracking and engagement reads.
Removed
  • Retired the customer-facing artifacts REST API (/artifacts*) and its public read endpoint. Pages and Blueprints are the structured-output primitives now; artifact webhook events and data models are unchanged.
Added
  • Generic Agent Runner. New agents.* tool family: agents.start, agents.status, agents.resume, agents.cancel, agents.list, agents.profiles_list.
  • Server-Sent Events stream for agent progress at GET /api/platform/v1/agents/:session_id/stream. Emits state and progress frames, :ping keepalives every 15 seconds, auto-closes on terminal status.
  • Three built-in agent profiles: content_writer (end-to-end content creation, 25-turn budget, emits content_piece artifacts), researcher (rigorous research with citations, 20-turn budget, emits research_report artifacts), copilot (general-purpose assistant, 15-turn budget, optional artifact output).
  • Three pause types on awaiting_input sessions: approval, question, continue_or_finish. Each carries a JSON Schema that agents.resume validates against.
  • Consumer docs tree at /docs/consumer/ with auto-generated API reference covering every tool, scope, webhook event, and data model.
Changed
  • Every agent tool call is now written to platform_audit_log with the caller's identity and correlation id, matching the audit trail direct REST and MCP calls already produced.
Added
  • platform_audit_log table. Every tool invocation across REST, MCP, and agent sessions is recorded with actor id, business id, tool id, inputs hash, status, duration, and correlation id.
  • audit_log.query tool for reading the audit trail. Scoped by business; platform admins can query across businesses.
  • Response-side redaction layer. Any field classified as sensitive is replaced with [MASKED] and paired with a <field>_masked: true sentinel in the response body.
Changed
  • Error responses now include X-Correlation-Id on every status code, not just 5xx, so failed requests can be traced end to end.
Added
  • Webhook extensions. POST /api/platform/v1/webhooks/:id/test fires a single test.ping delivery without affecting production aggregates. GET /api/platform/v1/webhooks/:id/deliveries returns the delivery history with status filtering and pagination.
  • is_test column on platform_webhook_deliveries to separate test fires from production traffic.
  • X-Webhook-Test: true header on test deliveries so receivers can branch on synthetic traffic.
Changed
  • Webhook delivery retry policy codified at 3 attempts with 1s and 10s backoff intervals; after the third attempt the failure is recorded and failure_count is incremented on the webhook.
Added
  • scope_mode column on API keys and OAuth clients, with per-tool scope enforcement. Tools whose declared scope is not in the caller's effective scope set are silently dropped from the runtime tool list.
  • Scope reference page at /docs/consumer/api-reference/scopes.md, auto-generated from the tool registry.
Changed
  • Role defaults tightened. New keys default to the minimum scope required for the role; existing keys retain their prior effective scope via the grandfather rule.
Added
  • Artifact unification. Bidirectional triggers between platform_artifacts and the legacy writing_samples, customer_posts, customer_testimonials, and style_collections tables. Reads from either side return identical data; writes propagate automatically.
  • platform_artifact_versions append-only version audit trail. Every write to platform_artifacts inserts a corresponding version row.
  • artifacts.get tool now supports include_versions: true to return the version history inline.
Changed
  • Artifact writes must supply expected_version for concurrent-edit safety. Mismatches return 409 version_conflict with the current version in the details block.
Added
  • Platform API v1 initial release. Base URL https://api.amdahl.com/api/platform/v1. Four-tool architecture: data.*, context.*, artifacts.*, plus the supporting surfaces for sessions, webhooks, scopes, and OpenAPI docs.
  • OpenAPI 3.1 spec at /api/platform/v1/openapi.json, regenerated on every release.
  • Canonical error envelope with code, message, and optional details.
  • Global per-IP rate limit of 100 requests per minute on /api/platform/v1/*.
  • OAuth 2.0 dynamic client registration per RFC 7591 at POST /oauth/register, with its own tighter limit of 5 registrations per minute per IP.
Changed
  • Nothing; this is the baseline.