Raw data stays where it is.
The account record moves to Snowflake.
Four agents become one incremental loop.
Totango and Salesforce extracts keep landing in S3 as an append-only archive — cheap, auditable, reprocessable. The account record is the part that outgrows S3. That moves to Snowflake, which the org already owns and the data team already governs.
Agents don’t overwrite one blob. They append facts to an event log, and the account row is rebuilt from that log by code. Every field traces to a real event. Auditable, replayable, rebuildable. That’s the governance moat, not a policy nobody reads.
Incremental updates — new tags today, next steps tomorrow — fall out naturally. A change to one account triggers that account’s steps only — signal, narrative, QA, action — not a whole-book rerun. Four steps in one loop with a shared log.
Reuses S3 + Snowflake + the governed AWS runtime. The LLM only narrates; the risk math stays in code. In a cybersecurity org where every key needs a blessing, the least-exciting answer is the right one.
The decision isn’t “S3 or Snowflake.” It’s which data lives in each. Confusing the two is how you end up storing the whole world in the wrong place.
The rule of thumb: if you append and archive it, it’s Tier 1. If you update and query it, it’s Tier 2.
The weekly whole-book run is the trap. The target is an event-driven loop: a change to one account triggers that account’s steps only.
| Step | What it does | Engine |
|---|---|---|
signal + tag | Turns raw signals into flags and tags on the account | Deterministic code |
narrative | Writes the plain-language story the CSM can defend | LLM, grounded in the flags |
QA | Checks the narrative cites real signals, kills drift | Rules + LLM check |
next step / action | Routes a playbook and a suggested action, human confirms | Code-routed + HITL |
Each step is idempotent and resumable. A step fails mid-account, you retry that step, not the book. The risk math lives in code; the LLM only narrates and is QA’d. That’s what keeps a 10,000-account run cheap and trustworthy.
The pitch to Mark: no new data store, no new provider, no new approval. You already own every piece. The skill set is wiring them and making the loop reliable — which is the role, not a rebuild.
Here’s what the whole thing actually looks like, end to end, using three example accounts: Meridian (at risk), Granite (churn-imminent), and Sunrise (healthy).
The data question: with 10,000 customer accounts, where does the account record live? Customer information is scattered across the tools the team already uses — Totango (post-sales) and Salesforce. You can’t act on a customer until those facts are pulled into one current record.
Tier one — the raw data stays put. Copies of the raw data land in an S3 bucket, a simple append-only archive. Nothing is ever edited there, so you can always re-check the original. This is what the team already runs today, and it stays.
Tier two — the account record moves to Snowflake. This is where the real work happens, in three tables: account (one current row per customer), account_event (the append-only log every observation gets added to), and score_history (snapshots over time). The account row is derived from the event log, never overwritten directly — so every number traces back to a real observation.
| account_id | customer | arr | health_score | status | risk_flags | next_action |
|---|---|---|---|---|---|---|
ACC-1002 | Meridian | $240k | 42 | at_risk | usage_down, renewal_60d | call about renewal |
ACC-1006 | Granite | $510k | 18 | churn_imminent | churn_signal | escalate to director |
ACC-1005 | Sunrise | $90k | 88 | healthy | — | offer expansion |
| event_id | account_id | ts | event_type | payload | source |
|---|---|---|---|---|---|
8411 | ACC-1002 | 08-28 | usage_drop | {wau: -22%} | usage_weekly |
8412 | ACC-1002 | 08-28 | ticket_spike | {tickets: +3} | support_tickets |
8413 | ACC-1002 | 08-29 | narrative_written | usage down three straight weeks | narrative_agent |
8414 | ACC-1002 | 08-29 | qa_verdict | {pass: true} | qa_agent |
8415 | ACC-1002 | 08-30 | next_step | schedule renewal check-in | action_agent |
8416 | ACC-1006 | 08-30 | churn_signal | {renewal_likelihood: low} | scoring |
8417 | ACC-1006 | 08-30 | escalation | {level: churn_imminent} | escalation_engine |
| account_id | snapshot_date | health_score | flags |
|---|---|---|---|
ACC-1002 | 2026-08-17 | 68 | {usage_down} |
ACC-1002 | 2026-08-24 | 55 | {usage_down, ticket_spike} |
ACC-1002 | 2026-08-31 | 42 | {usage_down, ticket_spike, renewal_60d} |
How they connect:
the account row isn’t stored independently — it’s derived from account_event.
Each observation (a usage drop, a narrative, a QA verdict, a next step) is appended; a deterministic layer recomputes the current account row from the log and pushes a fresh row into score_history.
Every number on Meridian’s record traces back to events 8411–8415 — nothing guessed, nothing overwritten.
The payload columns are JSON; shown here as {key: value} for readability.
| Priority | What we need to know | Why it matters / what it blocks |
|---|---|---|
| Blocks go/no-go | Can the pipeline write to Snowflake? Snowflake “should work well” — that’s hope, not approval. Does the loop get a role, or must writes route through the data-engineering team? Blocked by: the security-review guardrail on every integration. | This is the single biggest unknown. If the loop can’t write the record, the whole Tier 2 collapses and you’re back to S3. Everything else depends on the answer. |
| Blocks go/no-go | What does “quick” actually mean? “On demand, operating quick” — minutes, hours, or daily? The freshness SLA decides the trigger design: push-on-event vs. pull-on-cadence. Different architecture for near-real-time vs. overnight. | Near-real-time means event triggers and CDC from source systems. Daily means a batch that still updates one record per account. One is much harder in a governed org. |
| Blocks go/no-go | Account count and event volume “Thousands / 10,000 accounts.” But how many source events per account per week? Sizing drives Snowflake credits and LLM cost. Blocked by: volume data only they have. | A 1,000-account pilot and a 10,000-account rollout differ in cost and capacity by an order of magnitude. You can’t cost the plan without the number. |
| Blocks go/no-go | Which LLM runtimes are approved? Cybersecurity data-residency rules on customer data. Is Bedrock the only sanctioned runtime, or is a cheaper Llama path cleared? Blocked by: the security review + procurement. | Gates the narrative step. If only premium models are approved, per-account cost rises and the cheap-model play dies. |
| Refines the plan | Schema ownership and governance The data-analytics team owns Snowflake. Is the EWS schema inside their governed warehouse or isolated? Who reviews DDL? Blocks: the approval workflow, not the approach. | Determines who signs off schema changes and how fast you can iterate. Know the boundary before you design the tables. |
| Refines the plan | The fourth agent The roster is signals+tag, narrative, QA, and one more. We can’t finalize the loop without the full roster and their I/O. One quick follow-up to confirm. | Every step changes the event schema. Locking the roster early avoids a rework of the loop definition. |
| Refines the plan | Canonical account identity One customer, one ID across Totango and Salesforce. Is there a reconciled account key, or does the loop have to build one? Blocks: accurate records, not the architecture. | Score on split identity and you score wrong. This is the plumbing problem the repo’s “The Fix” page names — solve it before the loop matters. |
| Refines the plan | History for backfill To show historic trends now, you need history. Does existing S3/Snowflake data go back far enough, or do trends start at go-live? Blocks: the trend feature’s launch date. | “Historic trends” was a stated goal. If there’s no past data, score_history starts empty and trends only build over weeks — set the expectation now. |
| Refines the plan | Cost per account at 10,000 Unit economics of 4 steps × 10,000 accounts × weekly. Especially with the consumption-based pivot 18 months out. Blocks: budget approval. | A board-facing metric. Keeping the score in code and the LLM narrow is what holds per-account cost down — quantify it to defend it. |
| Assumption to confirm | The churn metric and attribution How is EWS success measured and tied to churn? The repo’s outcome tracker logs renewed / churned / expanded per intervention. Confirm the North Star with the stakeholders. | The org mandate is churn. Without a defined outcome metric, you can’t prove ROI to the two people who matter most. |
| Assumption we build against | Correction we can make later |
|---|---|
The loop gets a Snowflake write path A dedicated service role and its own namespace inside the governed warehouse, writes routed through the data-analytics team’s standard process. | If not blessed: the loop writes to a data-team-staged area and they fan it in. Same schema, one-hop difference. |
Daily incremental, not near-real-time Source data isn’t streaming and the weekly run already works. “New tags today, next steps tomorrow” fits a daily tick. | If the mandate needs faster: move to event-triggered. The event log makes that an additive upgrade, not a rewrite. |
10,000 accounts, proved at 1,000 Size for 10,000 accounts, four steps each, weekly. Measure real unit cost on a 1,000-account pilot before committing. | After pilot data lands: resize capacity and re-budget from measured, not assumed, per-account cost. |
Bedrock runtime, cheap model where it binds Narrative and QA run on the approved Bedrock runtime; a cheaper model (Llama) where cost is the constraint. The score stays in code either way. | As procurement clears: swap the model tier for a cheaper approved path. The loop definition doesn’t change. |
Totango is the master account key Totango holds the canonical account ID — it’s the post-sales system of record. Salesforce maps in as the sales-side reference. | If they have a golden-record system: reconcile to it once the data team confirms. One identity map, not a loop change. |
History seeds from the S3 JSONs Existing S3 extracts backfill roughly 8–12 weeks of score_history at go-live, so trends are usable immediately. | If they don’t: trends start empty and build over weeks. Set that expectation with stakeholders now. |
The fourth agent is the action step The loop is the three named steps plus a next-step/action agent — the gap the repo already closes. | If the fourth differs: confirm the roster and rework the loop. Early, cheap; later, expensive. |
Churn saved is the North Star Success = at-risk contracts saved and interventions acknowledged, tracked through the outcome log. | Lock the exact metric with the leadership: so attribution is real, not hoped for. |
The per-account incremental loop is event-driven: a change on one account triggers that account’s steps. But nothing confirms Totango or Salesforce can push that change. If they don’t expose webhooks or CDC — common behind a security review — there’s no real-time trigger, and the elegant loop quietly becomes a scheduled poll. Same architecture, different reality.
The mandate is churn — and churn is about what CSMs do, not where bytes sit. The existing S3 + weekly run already works at their actual scale. This plan licenses a lot of machinery for a scaling problem they may never hit. The outcome loop that ties to churn is a side note, not the spine.
account_event → materialize account is elegant for 12 accounts. At 10,000 it’s a real distributed problem: event ordering, idempotency, exactly-once, partial failures, a materialized view that stays in sync. And the four agents are a sequential pipeline — the record stalls on the slowest step, with no queue or backpressure.
The demo’s “CSM confirms every flag and draft” works at 12. At 10k no human reviews every signal. The plan never defines the triage threshold — what auto-routes, what escalates, what waits. That’s a product decision, not a storage one, and it’s the real scaling problem.
Narrative + QA per account per change, ×10,000 ×365. “Keep the score in code, LLM narrow” doesn’t settle it — we never put a unit number on the part that actually spends. With a consumption-based pivot in 18 months, this is board-visible.
Snowflake write access is blessable, Totango is the master key, Bedrock is approved for customer data — each is an assumption treated as a default. Identity is the sharpest: if one-customer-one-ID isn’t reconciled first, every account record is wrong at the foundation, and that’s hard to retrofit.