[{"data":1,"prerenderedAt":2571},["ShallowReactive",2],{"docs-nav":3,"docs-article-engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ftriggers":797},[4,17,27,44,55,67,75,82,94,106,114,122,129,135,144,153,161,169,177,189,202,211,218,229,240,248,260,268,276,286,296,305,314,323,331,337,343,350,356,364,371,378,383,393,401,410,415,422,432,439,444,451,458,462,467,475,487,499,509,516,525,533,539,545,551,557,563,567,579,593,603,614,621,626,633,640,646,653,658,665,673,678,686,692,699,704,710,721,730,740,747,753,761,767,776,782,791],{"path":5,"title":6,"description":7,"group":8,"section":6,"order":9,"tags":10,"lastUpdated":16},"\u002Fagents\u002Fagentic-crm","Agentic CRM","Research brief and build plan for an AgencyCore agentic CRM layer, rendered as an interactive page — the core operating loop, the target architecture, the typed-tool risk gateway, the proposed-actions review queue, and the four-slice MVP.","Agents",0,[11,12,13,14,15],"crm","agents","ai","architecture","research","2026-06-12",{"path":18,"title":19,"description":20,"group":8,"section":21,"order":22,"tags":23,"lastUpdated":26},"\u002Fagents\u002Fchat","Chat agent","High-level system design of the AgencyCore chat agent — core components, data flow, and the two abstractions that hold it together.","Reference",1,[12,14,24,25],"chat","system-design","2026-05-13",{"path":28,"title":29,"description":30,"group":8,"section":31,"order":32,"tags":33,"lastUpdated":43},"\u002Fagents\u002Fcompany-enrichment","Company Enrichment","The company enrichment workflow - a cache-first read in front of the company intelligence database that fills firmographic, contact and technographic facts via a fixed-order provider waterfall, and writes every resolved fact back with provenance so the first org pays once and every later search rides free.","Enrichment",2,[12,34,35,36,37,38,39,40,41,42],"workflow","enrichment","companies","waterfall","cache","intelligence-database","firmographics","provenance","sonar","2026-06-10",{"path":45,"title":46,"description":47,"group":8,"section":48,"order":9,"tags":49,"lastUpdated":54},"\u002Fagents\u002Fcompany-sonar","Company Signals","Signal-first company discovery for marketing agencies, on the Claude Agent SDK, with a global intelligence cache and deterministic composite scoring.","Company Sonar",[12,34,42,50,51,52,35,53,14],"company-search","signals","agent-sdk","scoring","2026-06-08",{"path":56,"title":57,"description":58,"group":8,"section":48,"order":22,"tags":59,"lastUpdated":66},"\u002Fagents\u002Fcompany-sonar\u002Fsignal-monitoring","Company Signals Monitoring","Realtime signal capture layer on top of the data graph. Detects hot events, scores them with a Claude managed agent against each agency's ICP, fans out alerts.",[14,51,60,61,62,63,64,65],"intel","icp","alerts","monitoring","sse","managed-agents","2026-06-09",{"path":68,"title":69,"description":70,"group":8,"section":71,"order":22,"tags":72,"lastUpdated":74},"\u002Fagents\u002Fconcepts\u002Fchat-agent-design-principles","Designing chat agents","The 2026 playbook for production chat agents that reach into internal systems via tools — context engineering, memory, tool design, when to add complexity.","Concepts",[12,14,24,73],"context-engineering","2026-05-14",{"path":76,"title":77,"description":78,"group":8,"section":71,"order":32,"tags":79,"lastUpdated":74},"\u002Fagents\u002Fconcepts\u002Fsystem-prompt-architecture","System prompt architecture","How to structure a production chat agent system prompt — eight sections, what each one does, and the rules vendors converge on.",[12,80,81],"prompt-engineering","system-prompt",{"path":83,"title":84,"description":85,"group":8,"section":84,"order":9,"tags":86,"lastUpdated":54},"\u002Fagents\u002Fenvoy","Envoy","High-level system design for the AI outreach engine — the sequence step state machine, the human-in-the-loop draft approval gate, multi-source context enrichment, and the inbox sentiment flow, rendered as an interactive page.",[12,87,88,89,90,91,92,93,14],"envoy","outreach","sales-engagement","sequences","state-machine","human-in-the-loop","nylas",{"path":95,"title":96,"description":97,"group":8,"section":98,"order":9,"tags":99,"lastUpdated":16},"\u002Fagents\u002Fheadhunter","Headhunter","The AI talent-search pipeline on one page - the production six-step design with its current-title relevance gate, and the 2.0 system design with internal-first waterfall sourcing, a pluggable source registry, automatic entity resolution, and a people intelligence graph that compounds every run.","General Search",[12,34,100,101,14,25,102,37,103,104,105],"headhunter","recruiting","multi-source","entity-resolution","people-intelligence","flywheel",{"path":107,"title":108,"description":109,"group":8,"section":21,"order":32,"tags":110,"lastUpdated":113},"\u002Fagents\u002Fpaperclip","Paperclip","Architecture deep dive into the Paperclip orchestration system.",[12,14,111,112],"orchestration","paperclip","2026-04-20",{"path":115,"title":116,"description":117,"group":8,"section":31,"order":22,"tags":118,"lastUpdated":16},"\u002Fagents\u002Fpeople-enrichment","People Enrichment","The people enrichment workflow - a cache-first read in front of the people intelligence database that fills profile, contact and employment facts via a fixed-order provider waterfall, keyed on the LinkedIn URL, and writes every resolved fact back with provenance so the first org pays once and every later search rides free. The fill step Headhunter and People Signals both call.",[12,34,35,119,37,38,39,120,41,100,121],"people","linkedin","people-sonar",{"path":123,"title":124,"description":125,"group":8,"section":126,"order":9,"tags":127,"lastUpdated":54},"\u002Fagents\u002Fpeople-sonar","People Signals","Signal-first people discovery for marketing agencies, built on the headhunter pipeline, with a composite score weighted by signal strength, source reputation, recency, and ICP fit.","People Sonar",[12,34,121,128,51,100,35,53,14],"people-search",{"path":130,"title":131,"description":132,"group":8,"section":126,"order":22,"tags":133,"lastUpdated":54},"\u002Fagents\u002Fpeople-sonar\u002Fpeople-signal-monitoring","People Signals Monitoring","Forward-looking design for the push layer that tracks known people - champions, past contacts, target-company decision-makers - and fires a warm lead the moment they change jobs, get promoted, or their company has an event.",[14,51,60,119,63,134],"warm-leads",{"path":136,"title":137,"description":138,"group":139,"section":140,"order":22,"tags":141,"lastUpdated":143},"\u002Fengineering\u002Fguides\u002Fagent-execution-stack","The Agent Execution Stack","Durable workflows over pluggable agent backends — how AgencyCore runs AI agents on Inngest over a webhook-driven Claude Managed Agents backend.","Engineering","Guides",[12,142,14,25],"inngest","2026-06-25",{"path":145,"title":146,"description":147,"group":139,"section":140,"order":9,"tags":148,"lastUpdated":143},"\u002Fengineering\u002Fguides\u002Fagent-runtime","Agent runtime","How AgencyCore runs AI agents on a provider-neutral runtime — the abstraction layer that lets us swap the agent backend, with Claude managed agents as the current provider.",[12,149,14,150,151,152,25],"runtime","anthropic","claude","providers",{"path":154,"title":155,"description":156,"group":139,"section":21,"order":157,"tags":158,"lastUpdated":160},"\u002Fengineering\u002Freference\u002Fagno-to-agent-sdk-migration","Agno → Claude Agent SDK migration","System-design spec for moving the ac-python-api workflow engine off Agno onto Anthropic's Claude Agent SDK \u002F Managed Agents, tiered by control-flow shape.",10,[12,14,159,52,65],"migration","2026-06-06",{"path":162,"title":163,"description":164,"group":139,"section":21,"order":22,"tags":165,"lastUpdated":54},"\u002Fengineering\u002Freference\u002Fcloudflare-agent-sandbox","Cloudflare agent sandbox","Cloudflare's Workers-based agent platform, evaluated as an alternative sandbox for our Agno workflows.",[12,166,167,168,159],"sandbox","cloudflare","workers",{"path":170,"title":171,"description":172,"group":139,"section":21,"order":32,"tags":173,"lastUpdated":176},"\u002Fengineering\u002Freference\u002Fvirtual-filesystem-rag","Virtual filesystem for AI assistants","How ChromaFs provides AI agents with structured file access.",[12,174,14,175],"rag","chromafs","2026-04-18",{"path":178,"title":179,"description":180,"group":139,"section":181,"order":182,"tags":183,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fcapabilities\u002Fstate-and-knowledge","State and knowledge","What a run may know. One deterministic context builder over application state, knowledge and memory, one owner for every fact, and memory that is written through a tool.","Agentic platform",11,[184,185,186,11,187],"context","memory","knowledge","pgvector","2026-08-31",{"path":190,"title":191,"description":192,"group":139,"section":181,"order":157,"tags":193,"lastUpdated":201},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fcapabilities\u002Ftools-and-integrations","Tools and integrations","A tool is the one way an agent reaches the world. AgencyCore owns the model facing contract, the invoke path, the credentials and the result boundary.",[194,195,196,197,198,199,200],"tools","integrations","mcp","agno","policy","security","idempotency","2026-09-04",{"path":203,"title":204,"description":205,"group":139,"section":181,"order":22,"tags":206,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fcontract","Platform contract","One platform behind chat, interactive channels, triggers, approvals and background runs, with one Agno runtime, one tool layer, one state layer, and three cross-cutting planes.",[12,14,197,142,194,207,149,208,198,209],"skills","channels","observability","2026-09-02",{"path":212,"title":181,"description":213,"group":139,"section":214,"order":22,"tags":215,"lastUpdated":201},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform","The whole agentic platform on one page - who starts a run, the one boundary every run passes, how the work executes, and what comes back.","System design",[12,14,216,197,142,217,198],"overview","runs",{"path":219,"title":220,"description":221,"group":139,"section":181,"order":222,"tags":223,"lastUpdated":228},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fagent-access","Agent access (CLI and MCP)","How an outside AI agent reaches AgencyCore. The ac CLI works today as a user seat. An MCP server is planned and not designed.",6,[224,196,12,151,225,226,227],"cli","access","auth","todo","2026-08-18",{"path":230,"title":231,"description":232,"group":139,"section":181,"order":233,"tags":234,"lastUpdated":239},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fchannel-gateway","Channel gateway","The only layer that knows both an interactive channel and the platform. One message shape converges inbound, one intent shape diverges outbound, and no model call happens here.",3,[208,235,236,237,238,199],"slack","web","identity","sessions","2026-08-30",{"path":241,"title":242,"description":243,"group":139,"section":181,"order":244,"tags":245,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ffront-door","Front door","The conversational control layer. It turns a request into one structured decision, then deterministic application code answers or hands work to RunManager.",4,[246,247,197,184,198,217],"front-door","routing",{"path":249,"title":250,"description":251,"group":139,"section":181,"order":32,"tags":252,"lastUpdated":259},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fsurfaces","Surfaces","Every product surface and its API contract. Web chat goes through the gateway; every schema-native surface calls the domain API.",[253,254,24,255,256,257,258,217,64],"surfaces","api","approvals","prospects","saved-searches","builder","2026-09-03",{"path":261,"title":262,"description":263,"group":139,"section":181,"order":264,"tags":265,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ftriggers","Triggers","A Run with no person. Every producer emits one Event, matching is deterministic, and dispatch reuses RunManager, Policy and Inngest.",5,[266,267,142,200],"triggers","events",{"path":269,"title":270,"description":271,"group":139,"section":181,"order":272,"tags":273,"lastUpdated":259},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fplanes\u002Fidempotency","Idempotency","One durable PostgreSQL key service prevents duplicate effects and freezes mutable input before selected Run starts. A Run start is guarded by a unique index on the Run row.",14,[200,217,194,274,275],"webhooks","reliability",{"path":277,"title":278,"description":279,"group":139,"section":181,"order":280,"tags":281,"lastUpdated":285},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fplanes\u002Fobservability-and-operations","Observability and operations","One run row, one span tree and one usage meter. Sentry reports system failure; AgencyCore spans explain what the agent did.",13,[209,217,282,283,64,284],"spans","usage","sentry","2026-08-26",{"path":287,"title":288,"description":289,"group":139,"section":181,"order":290,"tags":291,"lastUpdated":295},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fplanes\u002Fpolicy-and-governance","Policy and governance","One deterministic plane answers may this happen, at three checkpoints, with one grant model, one approval model and one decision log.",12,[198,292,255,293,294],"permissions","limits","governance","2026-08-25",{"path":297,"title":6,"description":298,"group":139,"section":299,"order":22,"tags":300,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fagentic-crm","The AgencyCore CRM loop for turning signals and discovery into qualified organization prospects, CRM relationships and outreach.","Agentic products",[11,301,51,302,256,35,303,304,87],"lead-generation","intelligence","signals-search","email-sequence",{"path":306,"title":307,"description":308,"group":139,"section":299,"order":264,"tags":309,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fbuilder-chat","Front door builder chat","Conversational authoring for organization-specific Agent and Workflow definitions, entered through the normal Front Door and backed by the existing DefinitionService.",[310,311,246,12,312,313,198],"authoring","definitions","workflows","templates",{"path":315,"title":316,"description":317,"group":139,"section":181,"order":318,"tags":319,"lastUpdated":201},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-contracts","Company, People and Signals contracts","The five Phase 7 product capabilities, their bounded inputs, stable references, permissions and results.",21,[320,321,119,51,322],"capabilities","company","contracts",{"path":324,"title":325,"description":326,"group":139,"section":181,"order":327,"tags":328,"lastUpdated":330},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-scenarios","Capability design scenarios","Normal, failure and recovery cases for the Phase 7 capability contracts, with implementation owners.",22,[320,329,321,119,51],"validation","2026-09-05",{"path":332,"title":333,"description":334,"group":139,"section":299,"order":233,"tags":335,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Femail-sequence","Email sequence workflow","Envoy durable outreach for one or many people, with fresh context, approvals, reply waits, follow-ups and Nylas transport.",[336,87,34,142,93,255],"email",{"path":338,"title":339,"description":340,"group":139,"section":299,"order":244,"tags":341,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fgeneral-chat","Front door general chat","The default conversational answer path for AgencyCore. It answers from supplied context, cites what it used, asks when context is insufficient, and delegates real work through the normal Front Door.",[24,246,186,184,247,342],"citations",{"path":344,"title":345,"description":346,"group":139,"section":299,"order":222,"tags":347,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fhuman-review","Human review inbox","One product page for every agentic action that is paused because a person must authorize an exact proposal. It is a view over the shared approval primitive, not a second review system.",[348,255,349,198,12],"human-review","inbox",{"path":351,"title":352,"description":353,"group":139,"section":299,"order":32,"tags":354,"lastUpdated":259},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fsignals-search","Signals Search","One bounded discovery workflow that finds companies, verifies signals, finds relevant people, and produces evidence-backed organization prospects without prematurely creating CRM records.",[303,355,36,119,51,302,256,11,35],"discovery",{"path":357,"title":358,"description":359,"group":139,"section":299,"order":360,"tags":361,"lastUpdated":188},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fworkflow-visualizer","Workflow visualizer","One constrained workflow graph, reused to author a draft, read a published definition, and watch a Run. Build mode edits the draft; run mode overlays Run and span state on the frozen snapshot.",7,[312,362,258,311,217,282,363,255],"visualizer","graph",{"path":365,"title":366,"description":367,"group":139,"section":181,"order":368,"tags":369,"lastUpdated":201},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fruntime\u002Fdefinitions","Runtime definitions","Editable drafts, one published configuration per definition, template forks, deterministic validation, and the Run snapshot that keeps in flight work stable.",8,[149,311,329,370],"publishing",{"path":372,"title":373,"description":374,"group":139,"section":181,"order":375,"tags":376,"lastUpdated":259},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fruntime\u002Fexecution","Runtime execution","The Run record, the Inngest step boundaries, agent segments, workflow nodes, approvals, cancellation, failure handling and live events.",9,[149,217,197,142,255,377,64],"cancellation",{"path":379,"title":380,"description":381,"group":139,"section":181,"order":360,"tags":382,"lastUpdated":285},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fruntime","Agentic runtime","One Run contract, one Agno agent runtime, one deterministic workflow model, and the component boundaries that keep the framework replaceable.",[149,217,197,312,207,142],{"path":384,"title":385,"description":386,"group":139,"section":387,"order":244,"tags":388,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Fcompany-context","Company context","L3. Company state, knowledge and memory are three different things. One deterministic builder turns them into one brief.","Mission Control",[389,390,186,185,184,391,11],"mission-control","company-state","retrieval","2026-08-12",{"path":394,"title":395,"description":396,"group":139,"section":387,"order":22,"tags":397,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Fexperience","Experience","L6. Where a person observes and controls the company, and the one rule that keeps the UI out of the business.",[389,398,399,255,400],"ui","control-plane","activity",{"path":402,"title":403,"description":404,"group":139,"section":387,"order":222,"tags":405,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Ffoundation","Foundation","L1. Generic infrastructure with no business logic in it. The test is that another product could run on it unchanged.",[389,406,407,408,267,409,226,209],"infrastructure","database","queue","storage",{"path":411,"title":387,"description":412,"group":139,"section":214,"order":233,"tags":413,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control","The internal Company OS. Six layers and one policy plane put a person in control of company state and of autonomous execution.",[389,414,14,12,312,198,399],"company-os",{"path":416,"title":417,"description":418,"group":139,"section":387,"order":32,"tags":419,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Fintelligence","Intelligence","L5. The agent is the primitive. A skill is how it works, a tool is how it reaches the world, and the two are never the same thing.",[389,12,207,420,421],"planning","reasoning",{"path":423,"title":424,"description":425,"group":139,"section":387,"order":368,"tags":426,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Fmetrics-and-connectors","Metrics and connectors","A worked example across every layer. Three vendors, one metric pipeline, three views, and the rule that decides what we store.",[389,427,195,428,429,284,430,431],"metrics","stripe","posthog","ingest","dashboards",{"path":433,"title":434,"description":435,"group":139,"section":387,"order":233,"tags":436,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Forchestration","Orchestration","L4. Workflow, run, step, trigger and event. Five nouns that turn a decision into durable execution.",[389,312,217,266,267,437,438],"durability","retry",{"path":440,"title":288,"description":441,"group":139,"section":387,"order":360,"tags":442,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Fpolicy-and-governance","A plane, not a layer. One place decides what an agent may do, under what conditions, and how much. Human approval is one of its three answers.",[389,198,294,255,292,293,443],"audit",{"path":445,"title":191,"description":446,"group":139,"section":387,"order":264,"tags":447,"lastUpdated":392},"\u002Fengineering\u002Fsystem-design\u002Fmission-control\u002Ftools-and-integrations","L2. One contract for every capability. The tool is the only route to the world, and it is where policy, audit and tenancy meet.",[389,194,195,448,449,450],"adapters","registry","credentials",{"path":452,"title":453,"description":454,"group":139,"section":455,"order":22,"tags":456,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fworkflows\u002Fcompany-search","Company search","Implementation notes for company.search. Search resolves and gates company identities; enrichment is a separate capability.","Workflows",[321,457,142,42],"search",{"path":459,"title":31,"description":460,"group":139,"section":455,"order":233,"tags":461,"lastUpdated":210},"\u002Fengineering\u002Fsystem-design\u002Fworkflows\u002Fenrichment","Reusable company and people enrichment workflows with canonical Intelligence write-back, existing tier freshness and bounded asynchronous email.",[35,321,119,142],{"path":463,"title":464,"description":465,"group":139,"section":455,"order":32,"tags":466,"lastUpdated":259},"\u002Fengineering\u002Fsystem-design\u002Fworkflows\u002Fpeople-search","People search","Implementation notes for people.search. Bounded company scope and persona gates return selectable person identities without enrichment.",[119,457,142,100],{"path":468,"title":469,"description":470,"group":139,"section":455,"order":244,"tags":471,"lastUpdated":474},"\u002Fengineering\u002Fsystem-design\u002Fworkflows\u002Fsignals-search","Signals search","Superseded. The earlier on-demand buying-signal search component, kept as a record of the design that the agentic platform Signals Search workflow replaces.",[51,12,142,472,473],"intelligence-databases","superseded","2026-08-28",{"path":476,"title":477,"description":478,"group":479,"section":480,"order":481,"tags":482,"lastUpdated":66},"\u002Flearnings\u002Fagentic-sdlc","The agentic SDLC","How AI agents move from autocomplete to owning the loop across the software lifecycle, and why that shifts the bottleneck from coding to verification.","Learnings",null,30,[12,483,484,485,486],"sdlc","engineering","verification","review",{"path":488,"title":489,"description":490,"group":479,"section":480,"order":491,"tags":492,"lastUpdated":498},"\u002Flearnings\u002Fagi-to-asi","From AGI to ASI","What lies beyond human-level AI. The four technological pathways from AGI to artificial superintelligence, the formal ceiling that bounds them, and the six bottlenecks that could stall the climb - distilled from the DeepMind report.",50,[493,494,495,496,497],"ai-futures","asi","agi","scaling","recursive-self-improvement","2026-06-19",{"path":500,"title":501,"description":502,"group":479,"section":480,"order":503,"tags":504,"lastUpdated":66},"\u002Flearnings\u002Fai-native-company-playbook","AI native company playbook","Why AI should be the operating system your company runs on, not a tool it uses, and the concrete practices that follow - closed loops, a queryable org, software factories, and token maxing.",40,[505,506,12,507,508],"ai-native","company-building","gtm","founders",{"path":510,"title":511,"description":512,"group":479,"section":480,"order":157,"tags":513,"lastUpdated":54},"\u002Flearnings\u002Fbuying-intent-signals","Buying intent signals","How buyers leak their intent before they ever fill in a form, and how to read those signals before the window closes.",[514,51,507,515],"intent","sales",{"path":517,"title":518,"description":519,"group":479,"section":480,"order":520,"tags":521,"lastUpdated":54},"\u002Flearnings\u002Fcold-outbound-system","Cold outbound system","A high-level study of an open-source 29-skill cold email system, organized into five sequential tracks from ICP to iteration.",20,[522,523,507,524],"outbound","cold-email","systems",{"path":526,"title":527,"description":528,"group":479,"section":480,"order":529,"tags":530,"lastUpdated":532},"\u002Flearnings\u002Fswan-gtm-skills-architecture","Swan GTM skills architecture","A research note on Swan AI's foundations and maps model for GTM agents, with ASCII diagrams and ideas AgencyCore can borrow.",60,[507,12,73,531,14],"swan","2026-07-01",{"path":534,"title":535,"description":536,"group":387,"section":480,"order":272,"tags":537,"lastUpdated":43},"\u002Fmission-control\u002Fciops-agent","CIOps agent","High-level system architecture and design notes for the Mission Control CIOps agent.",[389,12,538,14],"ciops",{"path":540,"title":541,"description":542,"group":387,"section":480,"order":182,"tags":543,"lastUpdated":43},"\u002Fmission-control\u002Fcostops-agent","CostOps agent","High-level system architecture and design notes for the Mission Control CostOps agent.",[389,12,544,14],"finops",{"path":546,"title":547,"description":548,"group":387,"section":480,"order":520,"tags":549,"lastUpdated":54},"\u002Fmission-control\u002Fdashboard","Dashboard","The Mission Control product UI - a dark cockpit with a fleet-nav rail, company-state grid, a working escalation queue, live ledger and a global kill switch.",[389,12,550,398],"dashboard",{"path":552,"title":553,"description":554,"group":387,"section":480,"order":280,"tags":555,"lastUpdated":43},"\u002Fmission-control\u002Fproduct-analytics-agent","ProductAnalytics agent","High-level system architecture and design notes for the Mission Control ProductAnalytics agent.",[389,12,556,14],"product-analytics",{"path":558,"title":559,"description":560,"group":387,"section":480,"order":290,"tags":561,"lastUpdated":43},"\u002Fmission-control\u002Frevenueops-agent","RevenueOps agent","High-level system architecture and design notes for the Mission Control RevenueOps agent.",[389,12,562,14],"revops",{"path":564,"title":214,"description":565,"group":387,"section":480,"order":157,"tags":566,"lastUpdated":54},"\u002Fmission-control\u002Fsystem-design","One screen for the whole company, watched by a guardrailed fleet of ops agents that explain, propose, act and learn overnight.",[389,12,544,14],{"path":568,"title":569,"description":570,"group":571,"section":480,"order":32,"tags":572,"lastUpdated":578},"\u002Fproduct-design\u002Fonboarding-flow","Onboarding flow","Product design for the signup wizard and how TAM building folds into it. Analyzes the flow today (account, profile, company), the gap (no ICP, empty dashboard), and the integration of a new \"who you sell to\" ICP step plus a build-and-reveal screen that lands the user on a populated, ranked list.","Product Design",[573,61,574,575,576,577],"onboarding","tam","activation","ux","user-journey","2026-06-11",{"path":580,"title":581,"description":582,"group":571,"section":480,"order":233,"tags":583,"lastUpdated":592},"\u002Fproduct-design\u002Fpricing-entitlements","Pricing tiers, entitlements and usage credits","Specification for subscription tiers with gated platform access: composable plan entitlements, a unified usage-credit currency, plan-sourced limits, per-module trials and a two-ticket delivery plan built on the Stripe billing foundation. Written for discussion; the Linear document is the canonical copy with ticket links.",[584,585,586,587,588,589,590,591],"pricing","entitlements","billing","credits","subscriptions","plans","seats","trials","2026-07-06",{"path":594,"title":595,"description":596,"group":571,"section":480,"order":233,"tags":597,"lastUpdated":578},"\u002Fproduct-design\u002Fsales-signals-ux","Designing Signals","Product design for the sales-signals experience in ac-frontend: the 14-type taxonomy and its color system, the anatomy of a signal card across four densities, the 0-10 lead score scale, the origin tag (sonar pull vs proactive push), the seven surfaces where signals render (launchpad, sonar app, company detail, timeline, activities, data layer, Envoy), and the interaction rules that keep them consistent.",[51,576,598,11,42,599,600,601,602],"design-system","lead-score","origin","pull","push",{"path":604,"title":605,"description":606,"group":607,"section":608,"order":244,"tags":609,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Factivities","Activities","Deep dive on crm_activities, the interaction + task log of the CRM — where it is served from, how a row is born and read, and its full schema, relationships and rules.","Proprietary data","CRM",[11,610,611,612,613],"activities","tasks","data-model","schema",{"path":615,"title":616,"description":617,"group":607,"section":608,"order":264,"tags":618,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Fcommunications","Communications","Deep dive on crm_communications and crm_communication_events, the unified email\u002Fcall\u002Fmessage log and its per-message engagement tracking — where it is served from, the outbound message lifecycle, and the full schema, relationships and rules.",[11,619,336,620,612],"communications","engagement",{"path":622,"title":623,"description":624,"group":607,"section":608,"order":22,"tags":625,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Fcompanies","Companies","Deep dive on crm_companies, the account record at the centre of the CRM — where it is served from, how a row is born and read, and its full schema, relationships and rules.",[11,36,612,613,14],{"path":627,"title":628,"description":629,"group":607,"section":608,"order":233,"tags":630,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Fdeals","Deals","Deep dive on the deal pipeline — crm_deals, crm_pipeline_stages and crm_pipeline_config. Where it is served from, the life of a deal, and its full schema, relationships and rules.",[11,631,632,612,613],"deals","pipeline",{"path":634,"title":635,"description":636,"group":607,"section":608,"order":222,"tags":637,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Flists","Lists","Deep dive on crm_lists and crm_list_members, the static or dynamic member collections of the CRM — where they are served from, how a list and its members come to be and are read, and their schema, relationships and rules.",[11,638,639,612,613],"lists","segments",{"path":641,"title":642,"description":643,"group":607,"section":608,"order":32,"tags":644,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Fpeople","People","Deep dive on crm_people, the contact record of the CRM — where it is served from, how a row is born and read, and its full schema, relationships and rules.",[11,119,645,612,613],"contacts",{"path":647,"title":648,"description":649,"group":607,"section":608,"order":368,"tags":650,"lastUpdated":43},"\u002Fproprietary-data\u002Fcrm\u002Fsaved-filters","Saved filters","Deep dive on crm_saved_filters, the named reusable filter snapshots over the company, person and signal list views — where it is served from, how a saved view is born and applied, and its full schema, relationships and rules.",[11,651,652,612,613],"saved-filters","views",{"path":654,"title":655,"description":656,"group":607,"section":608,"order":360,"tags":657,"lastUpdated":578},"\u002Fproprietary-data\u002Fcrm\u002Fsignals","Signals","Deep dive on the signals tables - signals, company_signals and person_signals, the CRM's sales-intelligence layer. Where signals are served from, how one is born and attached, and the full schema, relationships and rules.",[11,51,302,612,613],{"path":659,"title":660,"description":661,"group":607,"section":662,"order":22,"tags":663,"lastUpdated":43},"\u002Fproprietary-data\u002Fintelligence-databases\u002Fcompany-intelligence-database","Company Intelligence Database","Decided architecture for ENG-669, the cross-org company intelligence layer that acts as a read-through cache in front of enrichment providers, with public-facts-only privacy and provenance-tracked write-back.","Intelligence databases",[14,60,36,51,38,664],"eng-669",{"path":666,"title":667,"description":668,"group":607,"section":662,"order":244,"tags":669,"lastUpdated":578},"\u002Fproprietary-data\u002Fintelligence-databases\u002Forg-signal-feed","Org Signal Feed","The per-org activation layer on top of the shared signals store. One immutable intel_signals row fans out to many orgs through scoring (signal-type weight times ICP fit times recency decay) and materializes as ranked, tiered rows in intel_org_signal_feed - the only org-scoped, RLS-per-org table of the signal stack, the door the launchpad, inbox and digest all read through. Signals enter by two ingest classes - a user's sonar pull (ungated) or an automated push (gated by threshold plus an optional competitor-ICP check) - logged in intel_signal_ingests, and each feed row records its origin.",[14,60,51,670,53,671,672,575,430,601,602,600],"feed","decay","rls",{"path":674,"title":675,"description":676,"group":607,"section":662,"order":32,"tags":677,"lastUpdated":578},"\u002Fproprietary-data\u002Fintelligence-databases\u002Fpeople-intelligence-database","People Intelligence Database","Decided architecture for the cross-org people intelligence layer - a read-through cache in front of headhunter research and Hunter email lookups, with LinkedIn-URL identity, append-only employment edges, per-tier freshness stamps on the flat profile, shared intel_sources provenance, unified intel_signals, and a GDPR erasure path.",[14,60,119,51,38,100],{"path":679,"title":680,"description":681,"group":607,"section":662,"order":233,"tags":682,"lastUpdated":578},"\u002Fproprietary-data\u002Fintelligence-databases\u002Fsignals-intelligence-database","Signals Intelligence Database","Decided v1 architecture for the unified signal store - one polymorphic append-only intel_signals table that holds both company and person signals, with a shared taxonomy, source-ranked provenance, an intel_signal_ingests log that records which pipeline found each signal, decay at read time, and a person-to-company rollup so a champion job change surfaces on the company feed.",[14,60,51,683,671,684,670,685,41,601,602],"polymorphic","taxonomy","ingests",{"path":687,"title":688,"description":689,"group":607,"section":480,"order":9,"tags":690,"lastUpdated":16},"\u002Fproprietary-data\u002Foverview","Data Layer Overview","The AgencyCore data layer in one map - the org-scoped CRM plane in production today and the global intelligence plane designed to sit in front of it, with interactive diagrams of both, the end-to-end data flow, freshness and precedence rules, the privacy seam, and the rollout path.",[691,14,60,11,51,38,25,216],"data-layer",{"path":693,"title":694,"description":695,"group":696,"section":480,"order":9,"tags":697,"lastUpdated":54},"\u002Froadmap","Roadmap - June 2026","June 2026 product plan across four themes. The spine is moving our agents onto an isolated sandbox runtime and rebuilding the core agents and workflows on it, then standing up a read-through intelligence data store and shipping the Stripe billing system. Knowledge base, assistant, and credit tracking carry into the July roadmap.","Roadmap",[698,420],"roadmap",{"path":700,"title":701,"description":702,"group":696,"section":480,"order":22,"tags":703,"lastUpdated":54},"\u002Froadmap\u002Fjuly-2026","Roadmap - July 2026","July 2026 product plan across three themes, all carried over from June. Building on June's sandbox runtime, July grounds the agents in a knowledge base, launches the AI chat assistant, and meters every action with per-action credit tracking that reconciles into the Stripe billing system shipped in June.",[698,420],{"path":705,"title":706,"description":707,"group":696,"section":480,"order":32,"tags":708,"lastUpdated":532},"\u002Froadmap\u002Fjune-2026-slides","Roadmap slides - June 2026","Board-review slide deck for the June 2026 product roadmap, rendered directly from the original PPTX in the docs site.",[698,420,709],"slides",{"path":711,"title":712,"description":713,"group":714,"section":8,"order":520,"tags":715,"lastUpdated":16},"\u002Fsymphony\u002Fagents\u002Fdevops-agent","DevOps agent","Interactive design for a Slack-first Symphony DevOps agent that wraps production promotion, rollback, audit, and operational jobs behind policy gates, typed runbooks, and an auditable ledger.","Symphony",[716,235,717,718,719,720],"symphony","devops","production","runbooks","operations",{"path":722,"title":723,"description":724,"group":714,"section":8,"order":157,"tags":725,"lastUpdated":16},"\u002Fsymphony\u002Fagents\u002Foncall-agent","Oncall agent","Interactive design for a Symphony oncall agent that turns Sentry incidents into rich Linear tickets, investigates with Codex, opens fix PRs, and resolves Sentry after merge.",[716,284,726,727,728,729],"linear","oncall","incident-response","codex",{"path":731,"title":732,"description":733,"group":714,"section":734,"order":157,"tags":735,"lastUpdated":16},"\u002Fsymphony\u002Fhousekeeping\u002Fcodex-vacuum","Codex vacuum","Interactive design for the Symphony housekeeping timer that checkpoints and vacuums Codex sqlite stores on the VPS.","Housekeeping",[716,736,737,729,738,739],"timed-jobs","housekeeping","sqlite","vps",{"path":741,"title":742,"description":743,"group":714,"section":734,"order":481,"tags":744,"lastUpdated":16},"\u002Fsymphony\u002Fhousekeeping\u002Fhost-cleanup","Host cleanup","Interactive design for the Symphony housekeeping timer that removes stale \u002Ftmp debris, vacuums the journal, and optionally cleans the apt package cache.",[716,736,737,739,745,746],"disk","cleanup",{"path":748,"title":749,"description":750,"group":714,"section":734,"order":520,"tags":751,"lastUpdated":16},"\u002Fsymphony\u002Fhousekeeping\u002Fworkspace-cleanup","Workspace cleanup","Interactive design for the Symphony housekeeping timer that prunes idle per-issue workspaces after their TTL.",[716,736,737,752,746,739],"workspaces",{"path":754,"title":755,"description":756,"group":714,"section":480,"order":9,"tags":757,"lastUpdated":66},"\u002Fsymphony","Symphony orchestration","How AgencyCore runs OpenAI Symphony as a long-running daemon that turns Linear tickets into isolated, autonomous Codex runs, reviewed by Claude and merged by humans. High-level workflow, system architecture, and the engineer playbook.",[716,729,726,758,111,739,759,760],"claude-review","qa","automation",{"path":762,"title":763,"description":764,"group":714,"section":214,"order":22,"tags":765,"lastUpdated":392},"\u002Fsymphony\u002Fsystem-design\u002Fhigh-level-design","High-level design","The Symphony daemon end to end — the standing agent workforce and its label-routed workflows, then the runtime that polls, dispatches, runs and writes back.",[716,14,111,12,729,726,766],"systemd",{"path":768,"title":769,"description":770,"group":714,"section":771,"order":503,"tags":772,"lastUpdated":16},"\u002Fsymphony\u002Ftimed-jobs\u002Fdaily-security-agent","Daily security agent","Interactive design for a report-only Symphony timed job that reviews the last 24h of commits, scans the system for vulnerabilities, and opens focused follow-up tickets.","Timed jobs",[716,199,736,729,773,774,775],"semgrep","threat-model","ownership",{"path":777,"title":778,"description":779,"group":714,"section":771,"order":481,"tags":780,"lastUpdated":16},"\u002Fsymphony\u002Ftimed-jobs\u002Fdaily-sentry-triage","Daily Sentry triage","Interactive design for the Symphony timed job that performs read-only Sentry triage, deduplicates existing tracked clusters, and creates focused ENG bugs for new actionable errors.",[716,736,284,209,781,726],"triage",{"path":783,"title":784,"description":785,"group":714,"section":771,"order":157,"tags":786,"lastUpdated":16},"\u002Fsymphony\u002Ftimed-jobs\u002Fnightly-local-staging-e2e","Nightly local staging E2E","Interactive design for the Symphony timed job that seeds local Supabase, runs ac-frontend Playwright E2E against the local staging stack, uploads evidence, and cleans artifacts.",[716,736,787,788,789,790],"e2e","playwright","staging","frontend",{"path":792,"title":793,"description":794,"group":714,"section":771,"order":520,"tags":795,"lastUpdated":16},"\u002Fsymphony\u002Ftimed-jobs\u002Fnightly-staging-qa","Nightly staging QA","Interactive design for the Symphony timed job that seeds a staging QA Linear issue, runs an agent-browser crawl, validates feature-map coverage, and files focused follow-up work.",[716,736,789,759,796,726],"agent-browser",{"id":798,"title":262,"body":799,"customComponent":480,"description":263,"extension":2565,"group":139,"lastUpdated":188,"meta":2566,"navigation":1831,"order":264,"path":261,"related":480,"section":181,"seo":2567,"stem":2568,"tags":2569,"__hash__":2570},"docs\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ftriggers.md",{"type":800,"value":801,"toc":2539},"minimark",[802,805,809,817,828,835,840,843,849,908,911,956,963,966,970,1059,1063,1069,1254,1271,1275,1330,1347,1357,1362,1369,1375,1390,1396,1403,1407,1422,1431,1510,1513,1517,1523,1531,1537,1540,1553,1566,1572,1575,1581,1585,1609,1612,1618,1621,1669,1673,1685,1691,1694,1703,1735,1757,1761,1776,1782,1788,1792,1868,1879,1887,1891,1901,1916,1947,1954,1967,1993,1997,2062,2065,2069,2075,2078,2081,2085,2091,2094,2097,2100,2106,2115,2124,2244,2264,2314,2319,2323,2326,2346,2361,2376,2379,2383,2389,2394,2397,2401,2453,2457,2535],[803,804,262],"h1",{"id":266},[806,807,808],"p",{},"Triggers start work without an interactive user.",[806,810,811,812,816],{},"One event has ",[813,814,815],"strong",{},"two"," possible readers. It can start new work, and it can resume work that already waits for it.",[818,819,825],"pre",{"className":820,"code":822,"language":823,"meta":824},[821],"language-text","raw source -> EventProducer -> PlatformEvent -> EventRouter\n                                            │\n                        ┌───────────────────┴───────────────────┐\n                        ▼                                       ▼\n                  TriggerMatcher                         Inngest, as\n                        │                            platform\u002F\u003Cevent.type>\n                        ▼                                       │\n                TriggerDispatchService                          ▼\n                        │                              a waiting Run resumes\n                        ▼\n                 StartRunCommand\n                        │\n                        ▼\n                    RunManager\n","text","",[826,827,822],"code",{"__ignoreMap":824},[806,829,830,831,834],{},"The Trigger layer stops at ",[826,832,833],{},"RunManager",". It does not execute Agents, implement policy, or own another scheduler\u002Fqueue.",[836,837,839],"h2",{"id":838},"the-event-router","The event router",[806,841,842],{},"An inbound email reply must do two different things. It may start a Run, when a Trigger matches it. It must also resume the email sequence Run that already waits for that reply. The second reader was missing: an Inngest wait resolves only when an Inngest event arrives, and no producer sends one.",[806,844,845,848],{},[826,846,847],{},"EventRouter"," is that one fan-out point.",[818,850,854],{"className":851,"code":852,"language":853,"meta":824,"style":824},"language-python shiki shiki-themes github-dark","class EventRouter:\n    async def dispatch(self, event: PlatformEvent) -> None:\n        # 1. resume: every platform event reaches Inngest under one name\n        await self.inngest.send(InngestEvent(\n            name=f'platform\u002F{event.type}',\n            id=f'event:{event.id}',        # the same stable ID the producer minted\n            data=event.as_dict(),\n        ))\n        # 2. start: the existing trigger path, unchanged\n        await self.trigger_dispatch.handle(event)\n","python",[826,855,856,863,868,873,878,883,888,893,898,903],{"__ignoreMap":824},[857,858,860],"span",{"class":859,"line":22},"line",[857,861,862],{},"class EventRouter:\n",[857,864,865],{"class":859,"line":32},[857,866,867],{},"    async def dispatch(self, event: PlatformEvent) -> None:\n",[857,869,870],{"class":859,"line":233},[857,871,872],{},"        # 1. resume: every platform event reaches Inngest under one name\n",[857,874,875],{"class":859,"line":244},[857,876,877],{},"        await self.inngest.send(InngestEvent(\n",[857,879,880],{"class":859,"line":264},[857,881,882],{},"            name=f'platform\u002F{event.type}',\n",[857,884,885],{"class":859,"line":222},[857,886,887],{},"            id=f'event:{event.id}',        # the same stable ID the producer minted\n",[857,889,890],{"class":859,"line":360},[857,891,892],{},"            data=event.as_dict(),\n",[857,894,895],{"class":859,"line":368},[857,896,897],{},"        ))\n",[857,899,900],{"class":859,"line":375},[857,901,902],{},"        # 2. start: the existing trigger path, unchanged\n",[857,904,905],{"class":859,"line":157},[857,906,907],{},"        await self.trigger_dispatch.handle(event)\n",[806,909,910],{},"Two properties make this cheap.",[912,913,914,929,946],"ul",{},[915,916,917,920,921,924,925,928],"li",{},[813,918,919],{},"No wait index table."," A ",[826,922,923],{},"wait"," node declares its own correlation, and Inngest matches it. Nothing tracks which Runs wait for what. This page is where that rule is stated; the other pages point here. ",[826,926,927],{},"agent.provider_jobs"," is not an exception to it: it holds one external job's state, and the wait on it still carries its own correlation.",[915,930,931,937,938,941,942,945],{},[813,932,933,934,936],{},"The whole envelope is the payload, and a ",[826,935,923],{}," node reads two levels of it."," The expression tests ",[826,939,940],{},"async.data.organization_id"," for the tenant and ",[826,943,944],{},"async.data.data.\u003Ckey>"," for the correlation. Send the business fields alone and every wait times out reporting that nobody sent an event somebody did send.",[915,947,948,951,952,955],{},[813,949,950],{},"No producer learns about Runs."," A producer still emits one ",[826,953,954],{},"PlatformEvent"," and knows nothing below it.",[806,957,958,959,962],{},"The send is idempotent on ",[826,960,961],{},"event.id",", so a duplicate delivery resumes one Run once, exactly as it starts one Run once.",[806,964,965],{},"A platform event that no Run waits for and no Trigger matches is dropped by both readers. That is normal, and it costs one Inngest event.",[836,967,969],{"id":968},"components","Components",[971,972,973,986],"table",{},[974,975,976],"thead",{},[977,978,979,983],"tr",{},[980,981,982],"th",{},"Component",[980,984,985],{},"Job",[987,988,989,998,1007,1017,1029,1039,1049],"tbody",{},[977,990,991,995],{},[992,993,994],"td",{},"Trigger row",[992,996,997],{},"Event pattern\u002Ffilter, target Agent\u002FWorkflow, input template, enabled\u002Fconfig",[977,999,1000,1004],{},[992,1001,1002],{},[826,1003,954],{},[992,1005,1006],{},"Canonical transient envelope",[977,1008,1009,1014],{},[992,1010,1011],{},[826,1012,1013],{},"EventProducer",[992,1015,1016],{},"Verify\u002Fnormalize source-specific input",[977,1018,1019,1023],{},[992,1020,1021],{},[826,1022,847],{},[992,1024,1025,1026,1028],{},"Fan one ",[826,1027,954],{}," to Inngest and to the matcher",[977,1030,1031,1036],{},[992,1032,1033],{},[826,1034,1035],{},"TriggerMatcher",[992,1037,1038],{},"Deterministically find matching enabled triggers",[977,1040,1041,1046],{},[992,1042,1043],{},[826,1044,1045],{},"TriggerDispatchService",[992,1047,1048],{},"Apply trigger-specific guards and request exactly one Run",[977,1050,1051,1056],{},[992,1052,1053],{},[826,1054,1055],{},"TriggerInputBuilder",[992,1057,1058],{},"Build the Run input the trigger row declares",[836,1060,1062],{"id":1061},"the-trigger-row","The trigger row",[806,1064,1065,1068],{},[826,1066,1067],{},"agent.triggers"," holds one row per machine entry point. The row is the whole\nconfiguration. There is no second table and no per-row scheduler state.",[971,1070,1071,1081],{},[974,1072,1073],{},[977,1074,1075,1078],{},[980,1076,1077],{},"Column",[980,1079,1080],{},"Holds",[987,1082,1083,1097,1107,1123,1137,1150,1163,1173,1186,1196,1213,1223,1239],{},[977,1084,1085,1094],{},[992,1086,1087,1090,1091],{},[826,1088,1089],{},"id",", ",[826,1092,1093],{},"organization_id",[992,1095,1096],{},"Identity and tenant",[977,1098,1099,1104],{},[992,1100,1101],{},[826,1102,1103],{},"name",[992,1105,1106],{},"What an admin reads in the list",[977,1108,1109,1114],{},[992,1110,1111],{},[826,1112,1113],{},"kind",[992,1115,1116,1119,1120],{},[826,1117,1118],{},"event"," or ",[826,1121,1122],{},"schedule",[977,1124,1125,1130],{},[992,1126,1127],{},[826,1128,1129],{},"event_type",[992,1131,1132,1133,1136],{},"The ",[826,1134,1135],{},"PlatformEvent.type"," this row matches. Null on a schedule row",[977,1138,1139,1144],{},[992,1140,1141],{},[826,1142,1143],{},"conditions",[992,1145,1132,1146,1149],{},[826,1147,1148],{},"ConditionEvaluator"," expression, over the event fact map",[977,1151,1152,1160],{},[992,1153,1154,1090,1157],{},[826,1155,1156],{},"cron",[826,1158,1159],{},"timezone",[992,1161,1162],{},"The schedule expression and its zone. Null on an event row",[977,1164,1165,1170],{},[992,1166,1167],{},[826,1168,1169],{},"target_definition_id",[992,1171,1172],{},"The Agent or Workflow the Run starts",[977,1174,1175,1183],{},[992,1176,1177,1090,1180],{},[826,1178,1179],{},"input_builder",[826,1181,1182],{},"input_config",[992,1184,1185],{},"Which builder renders the Run input, and what it reads",[977,1187,1188,1193],{},[992,1189,1190],{},[826,1191,1192],{},"scopes",[992,1194,1195],{},"The tool names the admin authored",[977,1197,1198,1203],{},[992,1199,1200],{},[826,1201,1202],{},"authored_by",[992,1204,1205,1206,1209,1210,1212],{},"The admin. ",[826,1207,1208],{},"PrincipalFactory"," meets ",[826,1211,1192],{}," with this person's live rights",[977,1214,1215,1220],{},[992,1216,1217],{},[826,1218,1219],{},"enabled",[992,1221,1222],{},"The kill switch",[977,1224,1225,1236],{},[992,1226,1227,1090,1230,1090,1233],{},[826,1228,1229],{},"last_outcome",[826,1231,1232],{},"last_outcome_at",[826,1234,1235],{},"last_run_id",[992,1237,1238],{},"What the last dispatch did",[977,1240,1241,1249],{},[992,1242,1243,1090,1246],{},[826,1244,1245],{},"created_at",[826,1247,1248],{},"updated_at",[992,1250,1251,1253],{},[826,1252,1248],{}," is the write token",[806,1255,1256,1257,1259,1260,1262,1263,1265,1266,1262,1268,1270],{},"A schedule row carries ",[826,1258,1156],{}," and ",[826,1261,1159],{}," and no ",[826,1264,1129],{},". An event row\ncarries ",[826,1267,1129],{},[826,1269,1156],{},". A check constraint holds that, because a row\nthat carries both is a row two producers claim.",[836,1272,1274],{"id":1273},"the-event-contract","The event contract",[818,1276,1278],{"className":851,"code":1277,"language":853,"meta":824,"style":824},"@dataclass(frozen=True)\nclass PlatformEvent:\n    id: str\n    organization_id: str\n    type: str\n    producer: str\n    occurred_at: datetime\n    subject: EventSubject | None\n    data: dict\n    metadata: dict\n",[826,1279,1280,1285,1290,1295,1300,1305,1310,1315,1320,1325],{"__ignoreMap":824},[857,1281,1282],{"class":859,"line":22},[857,1283,1284],{},"@dataclass(frozen=True)\n",[857,1286,1287],{"class":859,"line":32},[857,1288,1289],{},"class PlatformEvent:\n",[857,1291,1292],{"class":859,"line":233},[857,1293,1294],{},"    id: str\n",[857,1296,1297],{"class":859,"line":244},[857,1298,1299],{},"    organization_id: str\n",[857,1301,1302],{"class":859,"line":264},[857,1303,1304],{},"    type: str\n",[857,1306,1307],{"class":859,"line":222},[857,1308,1309],{},"    producer: str\n",[857,1311,1312],{"class":859,"line":360},[857,1313,1314],{},"    occurred_at: datetime\n",[857,1316,1317],{"class":859,"line":368},[857,1318,1319],{},"    subject: EventSubject | None\n",[857,1321,1322],{"class":859,"line":375},[857,1323,1324],{},"    data: dict\n",[857,1326,1327],{"class":859,"line":157},[857,1328,1329],{},"    metadata: dict\n",[806,1331,1332,1335,1336,1259,1339,1342,1343,1346],{},[826,1333,1334],{},"metadata"," carries transport\u002Fcausality such as ",[826,1337,1338],{},"caused_by_run_id",[826,1340,1341],{},"caused_by_trigger_id",". Business matching fields live in ",[826,1344,1345],{},"data",".",[806,1348,1349,1350,1352,1353,1356],{},"A ",[826,1351,954],{}," is ",[813,1354,1355],{},"not another event store",". The producer source is authoritative; Runs\u002Fspans become the durable execution record after dispatch.",[1358,1359,1361],"h3",{"id":1360},"the-event-type-carries-its-version","The event type carries its version",[806,1363,1364,1365,1368],{},"A platform event the platform itself produces ends its type with a version:\n",[826,1366,1367],{},"agentic.provider_job.completed.v1",". A wait node stores that string in a\npublished definition, and a definition outlives the code that emits the event.",[818,1370,1373],{"className":1371,"code":1372,"language":823,"meta":824},[821],"platform produced   \u003Cdomain>.\u003Csubject>.\u003Cverb>.v\u003Cn>\nvendor sourced      \u003Cprovider>.\u003Cverb>            the vendor owns the name\n",[826,1374,1372],{"__ignoreMap":824},[806,1376,1377,1378,1381,1382,1385,1386,1389],{},"Bump the version when the fields a ",[826,1379,1380],{},"match"," may read change name, type or\nmeaning. A new version is a new event type. A Run parked on ",[826,1383,1384],{},".v1"," never matches ",[826,1387,1388],{},".v2",".\nIt times out on its own clock instead of reading a payload it cannot parse.",[806,1391,1392,1393,1395],{},"Adding a field is not a bump. A ",[826,1394,1380],{}," reference must resolve to a scalar, so a\nreader never sees the whole payload and a new field breaks nothing.",[806,1397,1398,1399,1402],{},"⚠️ ",[813,1400,1401],{},"A vendor event type carries no version, because the platform does not own\nit."," The adapter catches a vendor that changes its payload. It is the one place\nthat reads the vendor's vocabulary.",[836,1404,1406],{"id":1405},"producers","Producers",[818,1408,1410],{"className":851,"code":1409,"language":853,"meta":824,"style":824},"class EventProducer(Protocol):\n    async def produce(self, raw_event) -> PlatformEvent: ...\n",[826,1411,1412,1417],{"__ignoreMap":824},[857,1413,1414],{"class":859,"line":22},[857,1415,1416],{},"class EventProducer(Protocol):\n",[857,1418,1419],{"class":859,"line":32},[857,1420,1421],{},"    async def produce(self, raw_event) -> PlatformEvent: ...\n",[806,1423,1424,1425,1427,1428,1430],{},"Every producer does the same four things: verify the source, normalize it, mint a stable ",[826,1426,961],{},", and emit one ",[826,1429,954],{},". Only the last two columns below actually differ, so read the table as one shape with four id rules.",[971,1432,1433,1451],{},[974,1434,1435],{},[977,1436,1437,1440,1443,1446],{},[980,1438,1439],{},"Producer",[980,1441,1442],{},"Example",[980,1444,1445],{},"Trust",[980,1447,1448,1450],{},[826,1449,961],{}," comes from",[987,1452,1453,1469,1483,1497],{},[977,1454,1455,1458,1463,1466],{},[992,1456,1457],{},"Nylas",[992,1459,1460],{},[826,1461,1462],{},"email.received",[992,1464,1465],{},"transport authenticated; email body untrusted",[992,1467,1468],{},"the vendor delivery id",[977,1470,1471,1474,1477,1480],{},[992,1472,1473],{},"Vendor webhook",[992,1475,1476],{},"provider event",[992,1478,1479],{},"signature checked; content still untrusted",[992,1481,1482],{},"the provider delivery id",[977,1484,1485,1488,1491,1494],{},[992,1486,1487],{},"Cron",[992,1489,1490],{},"scheduled tick",[992,1492,1493],{},"ours",[992,1495,1496],{},"the schedule id + the tick timestamp",[977,1498,1499,1502,1505,1507],{},[992,1500,1501],{},"CRM change",[992,1503,1504],{},"row\u002Flifecycle change",[992,1506,1493],{},[992,1508,1509],{},"the row id + its version",[806,1511,1512],{},"The id rule is the load-bearing column. It must be stable for one logical occurrence and derived, never random, or a retried delivery or a replayed tick starts a second Run. A producer never queries trigger definitions or creates Runs.",[1358,1514,1516],{"id":1515},"a-webhook-producer-acknowledges-first","A webhook producer acknowledges first",[806,1518,1519,1522],{},[826,1520,1521],{},"EventRouter.dispatch()"," sends to Inngest, matches triggers and starts a Run. That is too much work to hold a vendor webhook open for. Nylas and every other provider retries on a slow answer, so an inline dispatch turns one reply into a retry storm.",[806,1524,1525,1526,1530],{},"A webhook producer therefore uses the same boundary the ",[1527,1528,1529],"a",{"href":230},"Channel Gateway"," already uses.",[818,1532,1535],{"className":1533,"code":1534,"language":823,"meta":824},[821],"vendor webhook\n   ├─ verify the signature and the timestamp window\n   ├─ produce the PlatformEvent, so the ID is minted from the vendor delivery ID\n   ├─ IdempotencyService.claim(UUID(organization_id), 'webhook.\u003Cprovider>',\n   │                           delivery_id, sha256(raw_body))\n   ├─ durable enqueue                -> Inngest\n   └─ ACK                            -> under 3 seconds\n                │\n                ▼\n          EventRouter.dispatch()     -> on a worker, not on the web request\n",[826,1536,1534],{"__ignoreMap":824},[806,1538,1539],{},"The claim completes only after the enqueue succeeds, so a failed enqueue leaves the vendor free to retry. This is one shared rule with two callers, not a second gateway.",[806,1541,1542,1543,1546,1547,1550,1551,1346],{},"The claim answers ",[826,1544,1545],{},"processing"," for a delivery already in flight and ",[826,1548,1549],{},"conflict"," for a delivery id the vendor re-sent with different content. This boundary answers an ACK in both cases and never raises, exactly as the Gateway does: a 5xx tells the vendor that a delivery it made successfully failed. See ",[1527,1552,200],{"href":269},[806,1554,1349,1555,1558,1559,1562,1563,1565],{},[826,1556,1557],{},"CronProducer"," and a ",[826,1560,1561],{},"CrmChangeProducer"," need none of this. Neither has a caller waiting on an HTTP answer, so both call ",[826,1564,1521],{}," directly.",[806,1567,1568,1571],{},[813,1569,1570],{},"A provider callback uses this boundary and starts no Run."," It resumes one.",[806,1573,1574],{},"The route adds two steps to the four above. It reads the job row first, because\nthe claim is organization scoped and a vendor callback names no organization. It\nstores the state transition before it enqueues, because the row is the durable\ntruth and the event is only a wake-up signal.",[806,1576,1577,1578,1346],{},"That page owns the full order, and this page does not restate it. See\n",[1527,1579,1580],{"href":190},"asynchronous provider jobs",[1358,1582,1584],{"id":1583},"the-schedule-producer-is-one-platform-cron","The schedule producer is one platform cron",[806,1586,1587,1588,1591,1592,1595,1596,1598,1599,1602,1603,1605,1606,1608],{},"An Inngest cron trigger is declared at registration, so a schedule an admin\nwrites at 10:00 cannot become one. The platform registers ",[813,1589,1590],{},"one"," Inngest cron,\n",[826,1593,1594],{},"trigger.cron",", at a one minute cadence. It reads the enabled schedule rows,\nevaluates each ",[826,1597,1156],{}," expression with ",[826,1600,1601],{},"croniter"," in that row's ",[826,1604,1159],{},", and\nemits one ",[826,1607,954],{}," for each row that is due.",[806,1610,1611],{},"This is a producer and not a scheduler. It owns no queue, no durable timer and\nno per-row cursor.",[818,1613,1616],{"className":1614,"code":1615,"language":823,"meta":824},[821],"trigger.cron  (* * * * *)\n  -> read enabled rows of kind 'schedule'\n  -> croniter(row.cron, row.timezone): is this minute a match?\n  -> PlatformEvent(type='schedule.tick', id=f'schedule:{row.id}:{local_minute}')\n  -> EventRouter.dispatch()\n",[826,1617,1615],{"__ignoreMap":824},[806,1619,1620],{},"Four rules make the tick safe.",[912,1622,1623,1632,1653,1659],{},[915,1624,1625,1631],{},[813,1626,1627,1628,1346],{},"The id names the matched minute, and not ",[826,1629,1630],{},"now()"," A tick at 09:00:03 and\nits retry at 09:00:47 floor to one value, so they start one Run.",[915,1633,1634,1637,1638,1641,1642,1645,1646,1648,1649,1652],{},[813,1635,1636],{},"The minute is the local wall clock minute, and it carries no offset."," The\nrow is written by a person who meant a wall clock time, so ",[826,1639,1640],{},"0 1 * * *"," fires\nonce a day. On the autumn shift 01:30 happens twice, and both instants floor\nto ",[826,1643,1644],{},"2026-10-25T01:30",": the second start key conflicts, ",[826,1647,833],{}," answers\n",[826,1650,1651],{},"duplicate",", and the dispatcher records one skip. A UTC id would mint two ids\nfor that one wall clock tick and start two Runs. On the spring shift the\nnamed minute does not exist, and no tick fires. That is what every cron does,\nand the row is not special.",[915,1654,1655,1658],{},[813,1656,1657],{},"A missed tick is not replayed."," The producer emits the current minute\nalone. A worker that was down for thirty minutes emits one event and not\nthirty. A catch-up turns one outage into a run storm that the day cap eats.",[915,1660,1661,1664,1665,1668],{},[813,1662,1663],{},"The scan is bounded."," The read is one indexed query with a hard row cap,\nas ",[826,1666,1667],{},"idempotency.sweeper"," is. Past the cap the producer logs a warning that\nnames the count.",[1358,1670,1672],{"id":1671},"the-crm-change-producer-polls-and-keeps-no-cursor","The CRM change producer polls, and keeps no cursor",[806,1674,1675,1676,1678,1679,1681,1682,1684],{},"A CRM row is written by the legacy product stack and by the platform tools, so\nno single write path can emit the event. ",[826,1677,1561],{}," runs on the same\n",[826,1680,1594],{}," minute and reads the rows whose ",[826,1683,1248],{}," moved inside a short\nwindow. The window overlaps on purpose.",[818,1686,1689],{"className":1687,"code":1688,"language":823,"meta":824},[821],"trigger.cron\n  -> read public.crm_companies where updated_at > now() - interval '5 minutes'\n  -> PlatformEvent(type='crm.company.updated',\n                   id=f'crm.company:{row.id}:{row.updated_at}')\n  -> EventRouter.dispatch()\n",[826,1690,1688],{"__ignoreMap":824},[806,1692,1693],{},"The id is the row id and its version, so an overlapping window re-emits an event\nInngest already holds and a start key already claims. Both readers drop it. That\nis why the producer needs no cursor table and no exactly-once read.",[806,1695,1696,1699,1700,1702],{},[813,1697,1698],{},"The producer emits every changed row, and the trigger row decides which\nmatter."," Scoping is a ",[826,1701,1143],{}," expression an admin writes, and not a filter\ninside the producer. One producer then serves every organization.",[806,1704,1705,1708,1709,1090,1712,1090,1715,1090,1718,1721,1722,1090,1725,1090,1728,1259,1731,1734],{},[813,1706,1707],{},"The event carries a fixed fact set, and the names are the column names."," A\ncondition reads ",[826,1710,1711],{},"data.id",[826,1713,1714],{},"data.name",[826,1716,1717],{},"data.lifecycle_stage",[826,1719,1720],{},"data.industry",",\n",[826,1723,1724],{},"data.country",[826,1726,1727],{},"data.lead_score",[826,1729,1730],{},"data.tags",[826,1732,1733],{},"data.owner_id",". A path\noutside the set resolves absent, answers False, and the row never fires.",[912,1736,1737,1751],{},[915,1738,1739,1742,1743,1746,1747,1750],{},[813,1740,1741],{},"A soft deleted row is not a change this producer emits."," ",[826,1744,1745],{},"deleted_at"," moves\non the delete, so the row would otherwise start a Run against a company the\nproduct already removed. The platform mints no ",[826,1748,1749],{},"crm.company.deleted"," yet.",[915,1752,1753,1756],{},[813,1754,1755],{},"A full scan warns, and it does not raise."," This differs from the schedule\nscan on purpose. A platform holding more than 500 enabled schedule rows is a\nbound an operator raises once; one bulk import writes more CRM rows in five\nminutes than any bound holds. Raising would emit nothing for the whole import\nand retry a doomed read every minute, so the pass stays best effort and names\nthe count.",[836,1758,1760],{"id":1759},"matching","Matching",[818,1762,1764],{"className":851,"code":1763,"language":853,"meta":824,"style":824},"class TriggerMatcher:\n    async def match(self, event: PlatformEvent) -> list[TriggerDefinition]: ...\n",[826,1765,1766,1771],{"__ignoreMap":824},[857,1767,1768],{"class":859,"line":22},[857,1769,1770],{},"class TriggerMatcher:\n",[857,1772,1773],{"class":859,"line":32},[857,1774,1775],{},"    async def match(self, event: PlatformEvent) -> list[TriggerDefinition]: ...\n",[806,1777,1778,1779,1781],{},"Use the same deterministic ",[826,1780,1148],{}," as Policy. Different callers expose different fields, but the platform does not grow a second expression language.",[818,1783,1786],{"className":1784,"code":1785,"language":823,"meta":824},[821],"event.type = crm.company.updated\nfilter     = event.data.lifecycle_stage == 'qualified'\ntarget     = sonar\n",[826,1787,1785],{"__ignoreMap":824},[836,1789,1791],{"id":1790},"dispatch","Dispatch",[818,1793,1795],{"className":851,"code":1794,"language":853,"meta":824,"style":824},"class TriggerDispatchService:\n    async def handle(self, event: PlatformEvent) -> None:\n        for trigger in await matcher.match(event):\n            if event.metadata.get('caused_by_trigger_id') == trigger.id:\n                skip('self_reentry')\n                continue\n\n            await run_manager.start(StartRunCommand(\n                definition_id=trigger.target_definition_id,\n                input=render_input(trigger, event),\n                actor=service_identity_for(trigger, event),\n                source=RunSource.trigger(trigger.id),\n                idempotency_key=f'trigger:{trigger.id}:event:{event.id}',\n            ))\n",[826,1796,1797,1802,1807,1812,1817,1822,1827,1833,1838,1843,1848,1853,1858,1863],{"__ignoreMap":824},[857,1798,1799],{"class":859,"line":22},[857,1800,1801],{},"class TriggerDispatchService:\n",[857,1803,1804],{"class":859,"line":32},[857,1805,1806],{},"    async def handle(self, event: PlatformEvent) -> None:\n",[857,1808,1809],{"class":859,"line":233},[857,1810,1811],{},"        for trigger in await matcher.match(event):\n",[857,1813,1814],{"class":859,"line":244},[857,1815,1816],{},"            if event.metadata.get('caused_by_trigger_id') == trigger.id:\n",[857,1818,1819],{"class":859,"line":264},[857,1820,1821],{},"                skip('self_reentry')\n",[857,1823,1824],{"class":859,"line":222},[857,1825,1826],{},"                continue\n",[857,1828,1829],{"class":859,"line":360},[857,1830,1832],{"emptyLinePlaceholder":1831},true,"\n",[857,1834,1835],{"class":859,"line":368},[857,1836,1837],{},"            await run_manager.start(StartRunCommand(\n",[857,1839,1840],{"class":859,"line":375},[857,1841,1842],{},"                definition_id=trigger.target_definition_id,\n",[857,1844,1845],{"class":859,"line":157},[857,1846,1847],{},"                input=render_input(trigger, event),\n",[857,1849,1850],{"class":859,"line":182},[857,1851,1852],{},"                actor=service_identity_for(trigger, event),\n",[857,1854,1855],{"class":859,"line":290},[857,1856,1857],{},"                source=RunSource.trigger(trigger.id),\n",[857,1859,1860],{"class":859,"line":280},[857,1861,1862],{},"                idempotency_key=f'trigger:{trigger.id}:event:{event.id}',\n",[857,1864,1865],{"class":859,"line":272},[857,1866,1867],{},"            ))\n",[806,1869,1870,1871,1874,1875,1878],{},"The unique key on ",[826,1872,1873],{},"agent.runs"," makes ",[826,1876,1877],{},"(trigger_id, event_id)"," atomic: the insert is the claim, so the second delivery reads the first delivery's Run back. There is no Idempotency Service on the start path and no transaction. Do not implement a read-then-insert duplicate check.",[806,1880,1881,1883,1884,1886],{},[826,1882,961],{}," must therefore be stable per logical occurrence. A vendor webhook uses the provider delivery ID. A ",[826,1885,1557],{}," must derive the ID from the schedule and the tick timestamp, never from a random value, or a retried tick starts a second Run.",[1358,1888,1890],{"id":1889},"render_input-is-a-seam-and-not-a-template","render_input is a seam, and not a template",[806,1892,1893,1894,1897,1898,1900],{},"A trigger row names a builder, and the registry answers it. The default builder\nis ",[826,1895,1896],{},"static",": it renders ",[826,1899,1182],{}," as the Run input and reads nothing.",[818,1902,1904],{"className":851,"code":1903,"language":853,"meta":824,"style":824},"class TriggerInputBuilder(Protocol):\n    async def build(self, trigger: TriggerDefinition, event: PlatformEvent) -> dict: ...\n",[826,1905,1906,1911],{"__ignoreMap":824},[857,1907,1908],{"class":859,"line":22},[857,1909,1910],{},"class TriggerInputBuilder(Protocol):\n",[857,1912,1913],{"class":859,"line":32},[857,1914,1915],{},"    async def build(self, trigger: TriggerDefinition, event: PlatformEvent) -> dict: ...\n",[806,1917,1918,1921,1924,1925,1928,1929,1931,1932,1935,1936,1939,1940,1943,1944,1346],{},[813,1919,1920],{},"A static template cannot start a scheduled saved search.",[826,1922,1923],{},"SmartFeedPublisher"," reads ",[826,1926,1927],{},"run.input['baseline_run_id']"," and refuses a run whose\nbaseline is not the saved search's current ",[826,1930,1235],{},". That value changes on\nevery run, so a fixed template publishes a diff once and answers ",[826,1933,1934],{},"stale"," for\never after. The ",[826,1937,1938],{},"saved_search"," builder reads ",[826,1941,1942],{},"SavedSearchRepository"," and returns\nthe same input the interactive route freezes today. See\n",[1527,1945,1946],{"href":351},"signals search",[806,1948,1949,1950,1953],{},"The registry keeps the layer clean. A product registers its builder, and the\ndispatcher imports the registry alone. ",[826,1951,1952],{},"triggers\u002F"," imports no product package.",[806,1955,1956,1957,1959,1960,1962,1963,1966],{},"The dispatcher passes a service ",[813,1958,237],{},". ",[826,1961,833],{}," mints the service ",[826,1964,1965],{},"Principal"," at admission.",[806,1968,1969,1972,1973,1959,1975,1978,1979,1981,1982,1985,1986,1989,1990,1346],{},[826,1970,1971],{},"service_identity_for()"," fills three fields off the trigger row: the trigger id, the scopes the admin authored, and ",[826,1974,1202],{},[826,1976,1977],{},"ActorIdentity"," refuses a trigger actor that names no author, and ",[826,1980,1208],{}," meets the authored scopes with that admin's rights, so a trigger is never wider than its author. Author the row with tool names alone: ",[826,1983,1984],{},"run.start"," is added before that meet, and the author's role decides whether it survives. A trigger row that carries ",[813,1987,1988],{},"no"," scopes mints a grant that holds nothing, and every run it starts is denied at admission. See ",[1527,1991,1992],{"href":287},"policy and governance",[836,1994,1996],{"id":1995},"protections","Protections",[971,1998,1999,2009],{},[974,2000,2001],{},[977,2002,2003,2006],{},[980,2004,2005],{},"Protection",[980,2007,2008],{},"Owner",[987,2010,2011,2019,2027,2035,2043,2050],{},[977,2012,2013,2016],{},[992,2014,2015],{},"Enabled\u002Fkill switch",[992,2017,2018],{},"Trigger row + matcher\u002Fdispatcher",[977,2020,2021,2024],{},[992,2022,2023],{},"Duplicate event",[992,2025,2026],{},"shared Run\u002Fidempotency boundary",[977,2028,2029,2032],{},[992,2030,2031],{},"Direct self-reentry",[992,2033,2034],{},"Trigger dispatcher via causality metadata",[977,2036,2037,2040],{},[992,2038,2039],{},"Debounce",[992,2041,2042],{},"Inngest flow control\u002Fconfig",[977,2044,2045,2048],{},[992,2046,2047],{},"Concurrency",[992,2049,2042],{},[977,2051,2052,2055],{},[992,2053,2054],{},"Budget\u002Fcost",[992,2056,2057,2058,2061],{},"The accrual gate inside ",[826,2059,2060],{},"RunManager.start()",", before any Run exists",[806,2063,2064],{},"Do not build general graph-cycle detection in V1. Direct self-reentry is blocked; indirect loops are bounded by budget, concurrency and kill switch until a real need proves otherwise.",[836,2066,2068],{"id":2067},"example-inbound-email","Example: inbound email",[818,2070,2073],{"className":2071,"code":2072,"language":823,"meta":824},[821],"Nylas webhook\n  -> verify transport\n  -> PlatformEvent(email.received, stable vendor id)\n  -> EventRouter\n       ├─ platform\u002Femail.received -> the email sequence Run waiting on this thread resumes\n       └─ TriggerMatcher -> TriggerDispatchService\n              -> StartRunCommand(idempotency = trigger + event)\n              -> RunManager\n",[826,2074,2072],{"__ignoreMap":824},[806,2076,2077],{},"Three retries of the same Nylas webhook create one Run, and resume one waiting Run once. The email body remains untrusted content inside that Run.",[806,2079,2080],{},"Both readers can act on one event, and that is correct. A reply resumes the sequence that sent the mail, and a separate Trigger may still file the reply against the CRM.",[836,2082,2084],{"id":2083},"example-scheduled-workflow","Example: scheduled workflow",[818,2086,2089],{"className":2087,"code":2088,"language":823,"meta":824},[821],"CronProducer -> PlatformEvent -> EventRouter -> TriggerMatcher -> RunManager -> Workflow Run\n",[826,2090,2088],{"__ignoreMap":824},[806,2092,2093],{},"A Workflow gets no private scheduler. A schedule is a Trigger like every other machine entry point.",[836,2095,2096],{"id":209},"Observability",[806,2098,2099],{},"Every skip has a distinct reason:",[818,2101,2104],{"className":2102,"code":2103,"language":823,"meta":824},[821],"disabled | duplicate | self_reentry | debounced | concurrency | budget\n",[826,2105,2103],{"__ignoreMap":824},[806,2107,2108,2114],{},[813,2109,2110,2111,2113],{},"Three of the six come back from ",[826,2112,2060],{},", and the rest are decided here."," The dispatcher owns the mapping, and it is the whole of it.",[806,2116,2117,2123],{},[813,2118,2119,2122],{},[826,2120,2121],{},"StartOutcome"," has ten members, and the dispatcher maps every one."," A member\nthis table did not name would reach the counter as an unknown answer.",[971,2125,2126,2138],{},[974,2127,2128],{},[977,2129,2130,2135],{},[980,2131,2132],{},[826,2133,2134],{},"StartRunResult.outcome",[980,2136,2137],{},"Skip reason",[987,2139,2140,2152,2162,2174,2184,2194,2204,2214,2224,2234],{},[977,2141,2142,2147],{},[992,2143,2144],{},[826,2145,2146],{},"organization_budget_exhausted",[992,2148,2149],{},[826,2150,2151],{},"budget",[977,2153,2154,2158],{},[992,2155,2156],{},[826,2157,1651],{},[992,2159,2160],{},[826,2161,1651],{},[977,2163,2164,2169],{},[992,2165,2166],{},[826,2167,2168],{},"definition_not_published",[992,2170,2171],{},[826,2172,2173],{},"disabled",[977,2175,2176,2181],{},[992,2177,2178],{},[826,2179,2180],{},"definition_not_found",[992,2182,2183],{},"not a skip; the trigger points at a definition that is gone, which is a fault",[977,2185,2186,2191],{},[992,2187,2188],{},[826,2189,2190],{},"input_too_large",[992,2192,2193],{},"not a skip; the builder made a payload over 32 KB, which is a fault in the trigger",[977,2195,2196,2201],{},[992,2197,2198],{},[826,2199,2200],{},"snapshot_too_large",[992,2202,2203],{},"not a skip; the published definition is too large to freeze, which is a fault",[977,2205,2206,2211],{},[992,2207,2208],{},[826,2209,2210],{},"snapshot_unbuildable",[992,2212,2213],{},"not a skip; a tool or a skill the definition names no longer resolves, which is a fault",[977,2215,2216,2221],{},[992,2217,2218],{},[826,2219,2220],{},"policy_unavailable",[992,2222,2223],{},"not a skip; the day cap gate could not decide, which is a fault",[977,2225,2226,2231],{},[992,2227,2228],{},[826,2229,2230],{},"parent_cancelled",[992,2232,2233],{},"not reachable; a trigger start names no parent run",[977,2235,2236,2241],{},[992,2237,2238],{},[826,2239,2240],{},"started",[992,2242,2243],{},"not a skip",[806,2245,1398,2246,2251,2253,2254,2256,2257,1259,2260,2263],{},[813,2247,2248,2250],{},[826,2249,2240],{}," covers three different ends, and only one of them executes.",[826,2252,2060],{}," answers ",[826,2255,2240],{}," for a run it dispatched, for a run it\nparked on an admission approval, and for a run a policy rule denied. The\ndispatcher therefore reads ",[826,2258,2259],{},"result.run.status",[826,2261,2262],{},"result.error_code",", and\nnever the outcome alone.",[971,2265,2266,2278],{},[974,2267,2268],{},[977,2269,2270,2275],{},[980,2271,2272],{},[826,2273,2274],{},"run.status",[980,2276,2277],{},"What happened",[987,2279,2280,2290,2300],{},[977,2281,2282,2287],{},[992,2283,2284],{},[826,2285,2286],{},"queued",[992,2288,2289],{},"The run is dispatched",[977,2291,2292,2297],{},[992,2293,2294],{},[826,2295,2296],{},"waiting",[992,2298,2299],{},"The run is parked on an admission approval",[977,2301,2302,2307],{},[992,2303,2304],{},[826,2305,2306],{},"failed",[992,2308,2309,2310,2313],{},"A rule denied it, and ",[826,2311,2312],{},"error_code"," names which",[806,2315,2316,2317,1346],{},"A parked machine run is the case that dies quietly, because it has no\nconversation. See ",[1527,2318,1992],{"href":287},[1358,2320,2322],{"id":2321},"where-a-skip-is-recorded","Where a skip is recorded",[806,2324,2325],{},"A skip writes no Run, so it writes no span, and it is not a policy decision. Two\nrecords carry it, and neither is a new table.",[912,2327,2328,2340],{},[915,2329,2330,1742,2333,1090,2335,1259,2337,2339],{},[813,2331,2332],{},"The trigger row.",[826,2334,1229],{},[826,2336,1232],{},[826,2338,1235],{}," hold\nthe answer of the last dispatch. Trigger admin shows it, so the admin who\nwrote the row reads why nothing happened.",[915,2341,2342,2345],{},[813,2343,2344],{},"A structured log line",", carrying the organization, the trigger, the event\nand the reason. A fault also reports to Sentry. A skip does not: a budget\nskip is an ordinary answer, and paging on it trains an operator to ignore the\nchannel.",[806,2347,2348,2350,2351,2353,2354,2356,2357,2360],{},[826,2349,2151],{}," comes from the accrual gate before a Run exists. ",[826,2352,1651],{}," cannot be decided here, because the rule above forbids a read-then-insert check: the start key is the only duplicate guard, so its answer is the only one the dispatcher has. ",[826,2355,2173],{}," can be decided here for a definition that was already disabled at match time, and it still arrives from ",[826,2358,2359],{},"start()"," when an admin disables it in the window between the match and the start.",[806,2362,2363,1090,2366,1259,2369,2372,2373,2375],{},[826,2364,2365],{},"self_reentry",[826,2367,2368],{},"debounced",[826,2370,2371],{},"concurrency"," are decided here and reach ",[826,2374,2359],{}," never.",[806,2377,2378],{},"A silent no-op is indistinguishable from broken automation, so skip outcomes must be counted\u002Frecorded.",[836,2380,2382],{"id":2381},"code-shape","Code shape",[818,2384,2387],{"className":2385,"code":2386,"language":823,"meta":824},[821],"triggers\u002F\n  models.py             PlatformEvent\n  producers\u002F\n    base.py             EventProducer\n    nylas.py\n    webhook.py\n    cron.py\n    crm.py\n  router.py             EventRouter\n  matcher.py            TriggerMatcher\n  dispatcher.py         TriggerDispatchService\n  input_builders.py     TriggerInputBuilder, and the static builder\n",[826,2388,2386],{"__ignoreMap":824},[806,2390,2391,2392,1953],{},"The registry holds the builders. A product owns its own builder and registers\nit, so ",[826,2393,1952],{},[806,2395,2396],{},"Neither the router, the matcher nor the dispatcher imports Agno.",[836,2398,2400],{"id":2399},"rules","Rules",[912,2402,2403,2406,2409,2418,2421,2424,2429,2435,2438,2444,2447,2450],{},[915,2404,2405],{},"A new source means a new producer, not new dispatch infrastructure.",[915,2407,2408],{},"A webhook producer verifies, claims, enqueues and acknowledges. It never dispatches inline.",[915,2410,2411,2412,2414,2415,2417],{},"Every ",[826,2413,954],{}," reaches both readers through ",[826,2416,847],{},". A producer never calls one of them directly.",[915,2419,2420],{},"A resume needs no index. The waiting node owns its own correlation.",[915,2422,2423],{},"Matching is deterministic and model-free.",[915,2425,2426,2427,1346],{},"Reuse ",[826,2428,1148],{},[915,2430,2431,2432,2434],{},"A Trigger starts an Agent or Workflow through ",[826,2433,833],{}," only.",[915,2436,2437],{},"Email is produced by Nylas directly; it does not route through Channel Gateway.",[915,2439,2440,2441,2443],{},"An interactive channel is not an event producer. An unknown sender receives a link invitation from the ",[1527,2442,1529],{"href":230},", and starts no Run.",[915,2445,2446],{},"Duplicate delivery is harmless.",[915,2448,2449],{},"Direct self-reentry is denied by default.",[915,2451,2452],{},"Policy and Inngest retain their existing responsibilities.",[836,2454,2456],{"id":2455},"minimum-contract-tests","Minimum contract tests",[912,2458,2459,2465,2471,2474,2477,2480,2483,2486,2489,2494,2500,2506,2509,2512,2515,2518,2527,2532],{},[915,2460,2461,2462,2464],{},"Same ",[826,2463,1877],{}," creates one Run under concurrency.",[915,2466,2467,2468,2470],{},"One ",[826,2469,954],{}," reaches the trigger matcher and Inngest, and a duplicate delivery does neither twice.",[915,2472,2473],{},"An email reply resumes the waiting sequence Run, and may also match a Trigger.",[915,2475,2476],{},"A trigger cannot directly fire itself from its own caused event by default.",[915,2478,2479],{},"Nylas retries do not duplicate work.",[915,2481,2482],{},"A webhook producer answers in under 3 seconds, whatever the router does afterwards.",[915,2484,2485],{},"A failed enqueue leaves the webhook claim unfinished, so the vendor retry still works.",[915,2487,2488],{},"Trigger filters and Policy conditions share evaluator semantics.",[915,2490,2491,2492,1346],{},"Disabled trigger never reaches ",[826,2493,833],{},[915,2495,2496,2497,2499],{},"Budget denial is produced by the accrual gate in ",[826,2498,2060],{},", not duplicated in trigger code.",[915,2501,2502,2503,2505],{},"A budget denied trigger creates no Run row, and it is counted as a ",[826,2504,2151],{}," skip.",[915,2507,2508],{},"Two ticks of one minute, forty seconds apart, start one Run.",[915,2510,2511],{},"A producer that was down for thirty minutes emits one event, and not thirty.",[915,2513,2514],{},"A schedule in a local zone keeps one id across a daylight saving shift.",[915,2516,2517],{},"Two enabled triggers matching one event start two Runs, on two start keys.",[915,2519,2520,2521,2523,2524,2526],{},"A run the admission parked answers ",[826,2522,2240],{}," with status ",[826,2525,2296],{},", and the\ndispatcher records it as parked and not as started.",[915,2528,2529,2530,1346],{},"A second scheduled saved search run carries the baseline the first run wrote,\nso the Smart Feed publishes a diff instead of answering ",[826,2531,1934],{},[915,2533,2534],{},"A trigger row that names an unknown input builder is refused at save.",[2536,2537,2538],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":824,"searchDepth":32,"depth":233,"links":2540},[2541,2542,2543,2544,2547,2552,2553,2556,2557,2558,2559,2562,2563,2564],{"id":838,"depth":32,"text":839},{"id":968,"depth":32,"text":969},{"id":1061,"depth":32,"text":1062},{"id":1273,"depth":32,"text":1274,"children":2545},[2546],{"id":1360,"depth":233,"text":1361},{"id":1405,"depth":32,"text":1406,"children":2548},[2549,2550,2551],{"id":1515,"depth":233,"text":1516},{"id":1583,"depth":233,"text":1584},{"id":1671,"depth":233,"text":1672},{"id":1759,"depth":32,"text":1760},{"id":1790,"depth":32,"text":1791,"children":2554},[2555],{"id":1889,"depth":233,"text":1890},{"id":1995,"depth":32,"text":1996},{"id":2067,"depth":32,"text":2068},{"id":2083,"depth":32,"text":2084},{"id":209,"depth":32,"text":2096,"children":2560},[2561],{"id":2321,"depth":233,"text":2322},{"id":2381,"depth":32,"text":2382},{"id":2399,"depth":32,"text":2400},{"id":2455,"depth":32,"text":2456},"md",{},{"title":262,"description":263},"engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ftriggers",[266,267,142,200],"Wsu8rKssziVJC6pMlQ3NdJZCGqTDUMPXPvjbDwCLXjE",1788650191611]