[{"data":1,"prerenderedAt":7080},["ShallowReactive",2],{"docs-nav":3,"docs-article-engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fsurfaces":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":250,"body":799,"customComponent":480,"description":251,"extension":7066,"group":139,"lastUpdated":259,"meta":7067,"navigation":6071,"order":32,"path":249,"related":7068,"section":181,"seo":7076,"stem":7077,"tags":7078,"__hash__":7079},"docs\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fsurfaces.md",{"type":800,"value":801,"toc":7007},"minimark",[802,805,809,818,821,826,942,964,967,972,975,1139,1142,1152,1157,1167,1170,1180,1187,1200,1205,1209,1215,1228,1249,1255,1266,1270,1273,1279,1289,1505,1522,1541,1583,1605,1608,1628,1637,1650,1659,1782,1800,1810,1841,1845,1879,1891,1904,1916,1928,1932,2013,2016,2025,2031,2042,2046,2133,2137,2143,2149,2152,2161,2165,2243,2247,2292,2295,2301,2313,2319,2325,2329,2373,2377,2407,2413,2644,2714,2783,2852,2863,2867,2907,2911,2918,2924,2985,2989,3092,3098,3100,3113,3119,3123,3137,3164,3167,3170,3198,3214,3223,3227,3251,3254,3311,3325,3328,3334,3341,3344,3355,3358,3364,3375,3385,3388,3406,3410,3424,3445,3476,3493,3504,3534,3541,3571,3574,3610,3623,3626,3629,3632,3646,3654,3670,3690,3693,3696,3831,3844,3848,3855,3946,3959,3963,3970,4039,4092,4140,4150,4154,4161,4174,4177,4180,4184,4190,4201,4204,4219,4226,4230,4236,4239,4242,4249,4257,4261,4267,4270,4273,4286,4296,4300,4306,4309,4322,4329,4339,4346,4357,4361,4367,4370,4386,4389,4392,4399,4405,4411,4421,4429,4433,4436,4439,4446,4453,4456,4462,4469,4472,4478,4490,4519,4532,4540,4562,4575,4707,4728,4731,4899,4903,4906,4951,4960,4963,4969,4983,5000,5003,5017,5041,5061,5067,5073,5076,5082,5223,5236,5252,5277,5285,5288,5292,5302,5412,5416,5423,5442,5464,5489,5504,5508,5514,5520,5534,5548,5551,5576,5592,5622,5643,5664,5668,5671,5674,5738,5744,5747,5791,5795,5798,5804,5839,5859,5876,5921,5937,5955,5981,5997,6000,6189,6196,6235,6238,6282,6296,6305,6329,6332,6339,6376,6388,6391,6398,6404,6411,6421,6442,6445,6476,6480,6498,6502,7003],[803,804,250],"h1",{"id":253},[806,807,808],"p",{},"A surface is a place where a person uses the platform. One question decides its path:",[810,811,812],"blockquote",{},[806,813,814],{},[815,816,817],"strong",{},"Does the surface speak the AgencyCore platform schema?",[806,819,820],{},"A conversational client does not. It sends free text, so it goes through a channel adapter and the Front Door. A control surface does speak the schema, so it calls the domain API.",[822,823],"doc-diagram",{"caption":824,"name":825},"The whole interfaces layer on one page. Four kinds of caller, three ways in, and one boundary they all end at. A conversational surface speaks free text, so it takes two hops through the Channel Gateway and the Front Door. A control surface already speaks the schema, so it calls the domain API directly, as the ac CLI does. A machine reports an event, and the Event Router fans it to two readers: one may start a run, and one resumes a run that already waits. The last band is the way back, which is the only part of the layer that diverges.","1-interfaces",[827,828,829,845],"table",{},[830,831,832],"thead",{},[833,834,835,839,842],"tr",{},[836,837,838],"th",{},"Surface",[836,840,841],{},"Path",[836,843,844],{},"Purpose",[846,847,848,860,871,881,891,901,912,922,932],"tbody",{},[833,849,850,854,857],{},[851,852,853],"td",{},"Web chat",[851,855,856],{},"Web adapter -> Front Door",[851,858,859],{},"Conversational control",[833,861,862,865,868],{},[851,863,864],{},"Approval Inbox",[851,866,867],{},"Direct API",[851,869,870],{},"Resolve pending policy decisions",[833,872,873,876,878],{},[851,874,875],{},"Prospect review",[851,877,867],{},[851,879,880],{},"Read and curate organization prospects",[833,882,883,886,888],{},[851,884,885],{},"Saved searches",[851,887,867],{},[851,889,890],{},"Save a repeatable brief, start it and read its latest diff",[833,892,893,896,898],{},[851,894,895],{},"Agent Builder",[851,897,867],{},[851,899,900],{},"Edit, validate, publish and disable definitions",[833,902,903,906,909],{},[851,904,905],{},"Run Explorer",[851,907,908],{},"Direct API + SSE",[851,910,911],{},"Inspect Run state, spans, usage and live progress",[833,913,914,917,919],{},[851,915,916],{},"Trigger admin",[851,918,867],{},[851,920,921],{},"Author what starts a Run without a person",[833,923,924,927,929],{},[851,925,926],{},"Policy admin",[851,928,867],{},[851,930,931],{},"Author the rules and the cost ceilings",[833,933,934,937,939],{},[851,935,936],{},"Connections",[851,938,867],{},[851,940,941],{},"Install and revoke provider credentials. Remote channels and MCP servers are deferred",[806,943,944,945,949,950,949,953,949,956,959,960,963],{},"No ",[946,947,948],"code",{},"AgentBuilderService",", ",[946,951,952],{},"ApprovalInboxService",[946,954,955],{},"ProspectReviewService",[946,957,958],{},"SavedSearchSurfaceService"," or ",[946,961,962],{},"RunExplorerService"," layer exists.\nEach surface is a projection over a domain component that is already built.",[806,965,966],{},"The first six get one section each below. The last three are ordinary configuration CRUD, and one section covers all three together.",[968,969,971],"h2",{"id":970},"shared-api-rules","Shared API rules",[806,973,974],{},"These apply to every route on this page.",[827,976,977,987],{},[830,978,979],{},[833,980,981,984],{},[836,982,983],{},"Concern",[836,985,986],{},"Rule",[846,988,989,1010,1018,1050,1058,1070,1089,1104,1121],{},[833,990,991,993],{},[851,992,841],{},[851,994,995,996,999,1000,1003,1004,1009],{},"Every route sits under ",[946,997,998],{},"\u002Fapi\u002Fv1\u002Fagentic\u002F",". The prefix names the platform boundary, and one entry in ",[946,1001,1002],{},"ac-cli\u002Fscripts\u002Faudit_endpoints.py"," covers every surface. See ",[1005,1006,1008],"a",{"href":1007},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fcontract#coexistence-with-the-live-agent-stack","coexistence",".",[833,1011,1012,1015],{},[851,1013,1014],{},"Authentication",[851,1016,1017],{},"The caller sends a Supabase user JWT. The organization comes from the token claims, never from the request body.",[833,1019,1020,1023],{},[851,1021,1022],{},"Tenancy",[851,1024,1025,1026,1029,1030,1033,1034,1037,1038,1041,1042,1045,1046,1049],{},"A row in another organization returns ",[946,1027,1028],{},"404",", never ",[946,1031,1032],{},"403",". A repository for the ",[946,1035,1036],{},"agent"," schema writes the ",[946,1039,1040],{},"organization_id"," filter itself. A repository for product state in ",[946,1043,1044],{},"public",", including prospects, takes ",[946,1047,1048],{},"scoped_db(organization_id)",". No router writes either filter.",[833,1051,1052,1055],{},[851,1053,1054],{},"Authorization",[851,1056,1057],{},"A surface checks visibility. Policy decides governed effects below it. Product curation, such as watch and dismiss, is not an approval.",[833,1059,1060,1063],{},[851,1061,1062],{},"Pagination",[851,1064,1065,1066,1069],{},"Every list of stored rows is cursor paginated, and none answers an unbounded set. ",[946,1067,1068],{},"GET \u002Ftools"," is exempt while its catalogue is process-local. The organization scoped catalogue pages like every other list.",[833,1071,1072,1074],{},[851,1073,270],{},[851,1075,1076,1077,1080,1081,1084,1085,1088],{},"A route that starts work needs an ",[946,1078,1079],{},"Idempotency-Key"," header. Every start accepts 1 to 255 characters. A missing or oversized key is ",[946,1082,1083],{},"400",". The route namespaces it per caller before it becomes ",[946,1086,1087],{},"StartRunCommand.idempotency_key",", because the stored key is unique per organization and two people share one organization.",[833,1090,1091,1094],{},[851,1092,1093],{},"Repeated writes",[851,1095,1096,1097,1100,1101,1009],{},"A repeated resolve or cancel returns ",[946,1098,1099],{},"200"," with the current state. It does not return ",[946,1102,1103],{},"409",[833,1105,1106,1109],{},[851,1107,1108],{},"Repeated start",[851,1110,1111,1112,1114,1115,1117,1118,1120],{},"A start whose ",[946,1113,1079],{}," already created a Run returns ",[946,1116,1099],{}," with that Run. There is no ",[946,1119,1103],{},": the key lives on the Run row, so a duplicate always reads a committed Run.",[833,1122,1123,1126],{},[851,1124,1125],{},"Lifecycle fields",[851,1127,1128,1129,1132,1133,959,1136,1009],{},"The client sends an intent, such as ",[946,1130,1131],{},"publish",". The client never writes ",[946,1134,1135],{},"state",[946,1137,1138],{},"status",[968,1140,853],{"id":1141},"web-chat",[806,1143,1144,1145,1148,1149,1151],{},"Web chat is a first-party client. It is authenticated, so the adapter verifies no signature, resolves no workspace and links no external user. What it keeps is the shape a later channel lands against: one ",[946,1146,1147],{},"NormalizedMessage",", one Front Door turn, one Run boundary. ",[1005,1150,231],{"href":230}," § What the web slice builds lists what this slice does not build.",[1153,1154,1156],"h3",{"id":1155},"the-turn","The turn",[1158,1159,1165],"pre",{"className":1160,"code":1162,"language":1163,"meta":1164},[1161],"language-text","POST \u002Fconversations\u002F{id}\u002Fmessages\n      -> WebChannelAdapter decodes the request\n      -> durable enqueue, concurrency key = conversation_id\n      -> 202 {message_id}\n\nworker\n      -> NormalizedMessage -> FrontDoorService.handle()\n      -> answer | clarify -> conversation stream\n      -> delegate product -> CapabilityStarter.start_resolved() -> RunManager.start() -> run.started\n      -> delegate custom  -> RunManager.start() -> run.started\n\nclient\n      -> conversation stream for the turn\n      -> Run stream for the work\n","text","",[946,1166,1162],{"__ignoreMap":1164},[806,1168,1169],{},"The HTTP request never waits for a model call. The answer arrives on the stream.",[806,1171,1172,1175,1176,1179],{},[815,1173,1174],{},"The enqueue is keyed on the conversation, not on the message."," One turn at a time per conversation. A key on ",[946,1177,1178],{},"message_id"," would let two front door turns interleave in one conversation, and the second turn would read context the first had not written yet.",[806,1181,1182,1183,1186],{},"⚠️ ",[815,1184,1185],{},"The key buys mutual exclusion, and it does not buy ordering."," Two messages\nenqueue in the order the route sends them, and an idle queue starts them in that\norder. A retry breaks it: a run in backoff holds no concurrency slot, so a later\nmessage can start while an earlier turn waits to try again. Mutual exclusion is\nthe property the paragraph above needs, and it holds in every case, retries\nincluded. Order holds on the path where neither turn retried. So the contract\ntest asserts that two turns never interleave, and it asserts their order only\nwhen neither retried.",[806,1188,1189,1196,1197,1199],{},[815,1190,1191,1192,1195],{},"The message row is written before the ",[946,1193,1194],{},"202",", not by the worker."," The response names a ",[946,1198,1178],{},", and a client that reloads immediately must see its own message. A row the worker wrote would appear only after the queue drained.",[806,1201,1202,1204],{},[946,1203,1178],{}," is still the idempotency key for the message itself. It stops one duplicate POST becoming two turns.",[1153,1206,1208],{"id":1207},"routes","Routes",[1158,1210,1213],{"className":1211,"code":1212,"language":1163,"meta":1164},[1161],"GET    \u002Fapi\u002Fv1\u002Fagentic\u002Fconversations                   newest activity first\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fconversations                   {title?} -> 201\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fconversations\u002F{id}\u002Fmessages     newest first\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fconversations\u002F{id}\u002Fmessages     {text} -> 202 {message}\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fconversations\u002F{id}\u002Fstream\n",[946,1214,1212],{"__ignoreMap":1164},[806,1216,1217,1220,1221,1223,1224,1227],{},[946,1218,1219],{},"POST \u002Fconversations"," starts no work, so it takes no ",[946,1222,1079],{},". It reads\nthe organization and the creator from the token, and it takes one optional\n",[946,1225,1226],{},"title"," of 200 characters at most.",[806,1229,1230,1233,1234,1236,1237,1240,1241,1244,1245,1248],{},[946,1231,1232],{},"POST \u002Fmessages"," takes one ",[946,1235,1163],{}," field. It takes no ",[946,1238,1239],{},"role",", no\n",[946,1242,1243],{},"attachment_count"," and no sender: the route writes ",[946,1246,1247],{},"role = 'user'"," and the\nsender of the token. Both writes answer the whole message row, so a client that\nposts renders the message without a second read.",[806,1250,1251,1254],{},[815,1252,1253],{},"Each list pages newest first."," A person opens a conversation on the last\nturn, and opens the list on the conversation that moved last. The two indexes\nENG-2240 built serve a backward scan unchanged. The comment in that migration\nsays oldest first, and it is stale.",[806,1256,1257,1265],{},[815,1258,1259,1261,1262,1009],{},[946,1260,1232],{}," moves ",[946,1263,1264],{},"last_activity_at"," The list orders on that column,\nso the route that writes the message writes the clock. It is a second\nstatement and not a transaction: a bump that fails leaves the order stale, and\nthe next turn repairs it.",[1153,1267,1269],{"id":1268},"conversation-stream","Conversation stream",[806,1271,1272],{},"The stream carries the turn, not the work.",[1158,1274,1277],{"className":1275,"code":1276,"language":1163,"meta":1164},[1161],"message.created     the inbound message was persisted\nprogress            thinking | checking_context | finding_capability | preparing_task\nmessage.completed   an answer, a clarify question, a cancel confirmation, or a refusal\nrun.started         carries run_id; the Run stream owns everything after this\nturn.failed         carries a reason; no Run was created\n",[946,1278,1276],{"__ignoreMap":1164},[806,1280,1281,1282,1285,1286,1288],{},"Each frame carries ",[946,1283,1284],{},"conversation_id"," and the inbound ",[946,1287,1178],{},", so a client\nthat holds two turns attributes each frame to one of them.",[1158,1290,1294],{"className":1291,"code":1292,"language":1293,"meta":1164,"style":1164},"language-json shiki shiki-themes github-dark","{\"type\": \"message.created\",   \"conversation_id\": \"…\", \"message_id\": \"…\", \"message\": {…}}\n{\"type\": \"progress\",          \"conversation_id\": \"…\", \"message_id\": \"…\", \"state\": \"thinking\"}\n{\"type\": \"message.completed\", \"conversation_id\": \"…\", \"message_id\": \"…\", \"message\": {…}}\n{\"type\": \"run.started\",       \"conversation_id\": \"…\", \"message_id\": \"…\", \"run_id\": \"…\"}\n{\"type\": \"turn.failed\",       \"conversation_id\": \"…\", \"message_id\": \"…\", \"reason\": \"…\"}\n","json",[946,1295,1296,1350,1391,1428,1467],{"__ignoreMap":1164},[1297,1298,1300,1304,1308,1311,1315,1318,1321,1323,1326,1328,1331,1333,1335,1337,1340,1343,1347],"span",{"class":1299,"line":22},"line",[1297,1301,1303],{"class":1302},"s95oV","{",[1297,1305,1307],{"class":1306},"sDLfK","\"type\"",[1297,1309,1310],{"class":1302},": ",[1297,1312,1314],{"class":1313},"sU2Wk","\"message.created\"",[1297,1316,1317],{"class":1302},",   ",[1297,1319,1320],{"class":1306},"\"conversation_id\"",[1297,1322,1310],{"class":1302},[1297,1324,1325],{"class":1313},"\"…\"",[1297,1327,949],{"class":1302},[1297,1329,1330],{"class":1306},"\"message_id\"",[1297,1332,1310],{"class":1302},[1297,1334,1325],{"class":1313},[1297,1336,949],{"class":1302},[1297,1338,1339],{"class":1306},"\"message\"",[1297,1341,1342],{"class":1302},": {",[1297,1344,1346],{"class":1345},"s6RL2","…",[1297,1348,1349],{"class":1302},"}}\n",[1297,1351,1352,1354,1356,1358,1361,1364,1366,1368,1370,1372,1374,1376,1378,1380,1383,1385,1388],{"class":1299,"line":32},[1297,1353,1303],{"class":1302},[1297,1355,1307],{"class":1306},[1297,1357,1310],{"class":1302},[1297,1359,1360],{"class":1313},"\"progress\"",[1297,1362,1363],{"class":1302},",          ",[1297,1365,1320],{"class":1306},[1297,1367,1310],{"class":1302},[1297,1369,1325],{"class":1313},[1297,1371,949],{"class":1302},[1297,1373,1330],{"class":1306},[1297,1375,1310],{"class":1302},[1297,1377,1325],{"class":1313},[1297,1379,949],{"class":1302},[1297,1381,1382],{"class":1306},"\"state\"",[1297,1384,1310],{"class":1302},[1297,1386,1387],{"class":1313},"\"thinking\"",[1297,1389,1390],{"class":1302},"}\n",[1297,1392,1393,1395,1397,1399,1402,1404,1406,1408,1410,1412,1414,1416,1418,1420,1422,1424,1426],{"class":1299,"line":233},[1297,1394,1303],{"class":1302},[1297,1396,1307],{"class":1306},[1297,1398,1310],{"class":1302},[1297,1400,1401],{"class":1313},"\"message.completed\"",[1297,1403,949],{"class":1302},[1297,1405,1320],{"class":1306},[1297,1407,1310],{"class":1302},[1297,1409,1325],{"class":1313},[1297,1411,949],{"class":1302},[1297,1413,1330],{"class":1306},[1297,1415,1310],{"class":1302},[1297,1417,1325],{"class":1313},[1297,1419,949],{"class":1302},[1297,1421,1339],{"class":1306},[1297,1423,1342],{"class":1302},[1297,1425,1346],{"class":1345},[1297,1427,1349],{"class":1302},[1297,1429,1430,1432,1434,1436,1439,1442,1444,1446,1448,1450,1452,1454,1456,1458,1461,1463,1465],{"class":1299,"line":244},[1297,1431,1303],{"class":1302},[1297,1433,1307],{"class":1306},[1297,1435,1310],{"class":1302},[1297,1437,1438],{"class":1313},"\"run.started\"",[1297,1440,1441],{"class":1302},",       ",[1297,1443,1320],{"class":1306},[1297,1445,1310],{"class":1302},[1297,1447,1325],{"class":1313},[1297,1449,949],{"class":1302},[1297,1451,1330],{"class":1306},[1297,1453,1310],{"class":1302},[1297,1455,1325],{"class":1313},[1297,1457,949],{"class":1302},[1297,1459,1460],{"class":1306},"\"run_id\"",[1297,1462,1310],{"class":1302},[1297,1464,1325],{"class":1313},[1297,1466,1390],{"class":1302},[1297,1468,1469,1471,1473,1475,1478,1480,1482,1484,1486,1488,1490,1492,1494,1496,1499,1501,1503],{"class":1299,"line":264},[1297,1470,1303],{"class":1302},[1297,1472,1307],{"class":1306},[1297,1474,1310],{"class":1302},[1297,1476,1477],{"class":1313},"\"turn.failed\"",[1297,1479,1441],{"class":1302},[1297,1481,1320],{"class":1306},[1297,1483,1310],{"class":1302},[1297,1485,1325],{"class":1313},[1297,1487,949],{"class":1302},[1297,1489,1330],{"class":1306},[1297,1491,1310],{"class":1302},[1297,1493,1325],{"class":1313},[1297,1495,949],{"class":1302},[1297,1497,1498],{"class":1306},"\"reason\"",[1297,1500,1310],{"class":1302},[1297,1502,1325],{"class":1313},[1297,1504,1390],{"class":1302},[806,1506,1507,1517,1518,1521],{},[815,1508,1509,1512,1513,1516],{},[946,1510,1511],{},"message"," is the row shape that ",[946,1514,1515],{},"GET \u002Fmessages"," answers, and it is the same\nshape in both frames."," So the client holds one message list and applies one\nupsert by ",[946,1519,1520],{},"id",", whatever the source. A frame and a page never disagree about\nwhat a message is.",[806,1523,1524,1527,1528,1530,1531,1534,1535,1537,1538,1540],{},[815,1525,1526],{},"The turn function publishes all five, and the route publishes none."," One\npublisher keeps the order of a turn true, and it leaves ",[946,1529,1232],{}," a write\nand an enqueue. The cost is named: the concurrency key admits one turn per\nconversation, so a second message queued behind a live turn carries no\n",[946,1532,1533],{},"message.created"," until that turn ends. The poster already holds its own row off\nthe ",[946,1536,1194],{},", and a second tab reconciles on the ",[946,1539,1515],{}," it reads when it\nopens the stream.",[806,1542,1543,1550,1551,1554,1555,1558,1559,1562,1563,1566,1567,1570,1571,1574,1575,1578,1579,1582],{},[815,1544,1545,1546,1549],{},"The four ",[946,1547,1548],{},"progress"," states reach the stream through a callback, and not\nthrough a wrapper."," ",[946,1552,1553],{},"FrontDoorService.handle()"," takes ",[946,1556,1557],{},"on_progress",". It emits\n",[946,1560,1561],{},"checking_context"," before the context wait and ",[946,1564,1565],{},"finding_capability"," before the\ncapability wait. A turn function that wrapped ",[946,1568,1569],{},"handle()"," from the outside could\npublish ",[946,1572,1573],{},"thinking"," and ",[946,1576,1577],{},"preparing_task"," only, and the whole span the person\nwaits through would be silent. So the turn function passes a callback that\npublishes to ",[946,1580,1581],{},"CONVERSATION_EVENTS",", and the default is a no-op for a caller\nthat streams nothing.",[806,1584,1585,1591,1592,949,1595,1574,1598,1601,1602,1604],{},[815,1586,1587,1590],{},[946,1588,1589],{},"run.started"," is a conversation event, and it is not a run event."," The run\npublisher writes ",[946,1593,1594],{},"run.updated",[946,1596,1597],{},"run.completed",[946,1599,1600],{},"run.failed",", and it writes\nno ",[946,1603,1589],{},". This frame says the turn handed the work over. Everything\nafter it is on the Run stream.",[806,1606,1607],{},"The Front Door returns structured output, so an answer cannot stream tokens. Web chat shows progress, then one complete answer. Token deltas belong to an Agent Run, and they arrive on the Run stream.",[806,1609,1182,1610,1613,1614,1617,1618,1620,1621,1550,1624,1627],{},[815,1611,1612],{},"Subscribe, then send."," Redis Pub\u002FSub keeps no backlog, so an event published before the client subscribed reaches nobody. A fast ",[946,1615,1616],{},"answer"," can complete between the ",[946,1619,1194],{}," and a stream the client opens afterwards, and the turn then appears to hang until the person reloads. The client opens ",[946,1622,1623],{},"GET \u002Fconversations\u002F{id}\u002Fstream",[815,1625,1626],{},"before"," it posts the message, and it keeps that stream open for the life of the conversation view.",[806,1629,1182,1630,1633,1634,1636],{},[815,1631,1632],{},"The stream has no terminal event, so it never closes on content."," A Run ends; a conversation does not. The only bound is the connection lifetime, after which the client reconnects and refetches ",[946,1635,1515],{},". This is the same reconnect contract the Run stream uses, and § Reconnect owns it.",[806,1638,1182,1639,1642,1643,1645,1646,1649],{},[815,1640,1641],{},"A delegate turn leaves two streams open."," The conversation stream stays open after ",[946,1644,1589],{},", because the next turn and any ",[946,1647,1648],{},"turn.failed"," arrive on it. The Run stream carries the work. The client closes the Run stream on a terminal event, and it closes the conversation stream when the person leaves the view.",[806,1651,1652,1655,1656,1658],{},[815,1653,1654],{},"Every outcome writes one durable row, and that is what makes the refetch\ntotal."," A live event is a hint here exactly as it is on the Run stream. A\nclient that missed a frame reads ",[946,1657,1515],{}," and sees the same answer, so no\noutcome can leave a turn hanging.",[827,1660,1661,1674],{},[830,1662,1663],{},[833,1664,1665,1668,1671],{},[836,1666,1667],{},"Outcome",[836,1669,1670],{},"What the stream carries",[836,1672,1673],{},"What the turn writes",[846,1675,1676,1690,1704,1725,1740,1752,1765],{},[833,1677,1678,1682,1687],{},[851,1679,1680],{},[946,1681,1616],{},[851,1683,1684],{},[946,1685,1686],{},"message.completed",[851,1688,1689],{},"one assistant row",[833,1691,1692,1697,1702],{},[851,1693,1694],{},[946,1695,1696],{},"clarify",[851,1698,1699,1701],{},[946,1700,1686],{},", holding the question",[851,1703,1689],{},[833,1705,1706,1712,1719],{},[851,1707,1708,1711],{},[946,1709,1710],{},"delegate",", admitted",[851,1713,1714,1716,1717],{},[946,1715,1686],{},", then ",[946,1718,1589],{},[851,1720,1721,1722],{},"one assistant row, carrying ",[946,1723,1724],{},"run_id",[833,1726,1727,1733,1738],{},[851,1728,1729,1732],{},[946,1730,1731],{},"control_run",", cancel",[851,1734,1735,1737],{},[946,1736,1686],{},", holding the confirmation",[851,1739,1689],{},[833,1741,1742,1745,1750],{},[851,1743,1744],{},"Policy denied the delegation",[851,1746,1747,1749],{},[946,1748,1686],{},", holding the denial reason",[851,1751,1689],{},[833,1753,1754,1757,1762],{},[851,1755,1756],{},"The organization is over its day ceiling",[851,1758,1759,1761],{},[946,1760,1686],{},", holding the refusal text",[851,1763,1764],{},"one assistant row, and no usage row",[833,1766,1767,1770,1775],{},[851,1768,1769],{},"The turn failed",[851,1771,1772,1774],{},[946,1773,1648],{},", holding one reason",[851,1776,1777,1778,1781],{},"one ",[946,1779,1780],{},"system"," row, holding the same reason",[806,1783,1784,1550,1789,1792,1793,1796,1797,1799],{},[815,1785,1786,1787,1009],{},"A denial carries no ",[946,1788,1589],{},[946,1790,1791],{},"RunManager"," writes a run row for a\npolicy denial, and the run holds the answer, but nothing on it is worth\nwatching. A frame that opened a Run stream over a run that is already ",[946,1794,1795],{},"failed","\nwould answer one terminal frame and close. The denial reason is the whole\nanswer, and ",[946,1798,1686],{}," carries it.",[806,1801,1802,1809],{},[815,1803,1804,1805,1808],{},"A delegation writes its own row, and ",[946,1806,1807],{},"agent.conversation_messages.run_id"," is\nwhat it fills."," Without it a reload shows the question and no reply, and the\nperson cannot reach the work their message started. The row names the capability\nand carries the run, and the Run stream carries everything after it.",[806,1811,1182,1812,1818,1821,1822,1825,1826,1828,1829,1832,1833,1836,1837,1840],{},[815,1813,1814,1815,1817],{},"A failed turn writes a ",[946,1816,1780],{}," row, and ENG-2240 could not hold one.",[946,1819,1820],{},"conversation_messages_reply_shape"," read ",[946,1823,1824],{},"(role = 'assistant') = (in_reply_to IS NOT NULL)",", so a ",[946,1827,1780],{}," row carried a null ",[946,1830,1831],{},"in_reply_to",", and\n",[946,1834,1835],{},"uq_conversation_messages_reply"," compares NULLS DISTINCT. The failure row\ntherefore carried no unique guard, and a retried turn appended a second copy of\nit. ENG-2244 widened the CHECK to ",[946,1838,1839],{},"(role \u003C> 'user') = (in_reply_to IS NOT NULL)",". Every row the platform writes is now keyed by the message it answers,\nand the guard that protects an answer protects a failure too.",[1153,1842,1844],{"id":1843},"what-the-client-renders-from","What the client renders from",[806,1846,1847,1850,1574,1852,1854,1855,1857,1858,1860,1861,949,1863,1865,1866,1868,1869,1871,1872,1875,1876,1878],{},[815,1848,1849],{},"The durable row is the render source, and a frame is a hint that one moved.",[946,1851,1533],{},[946,1853,1686],{}," carry the whole row, so the client\nupserts by ",[946,1856,1520],{},". The other three carry no row: ",[946,1859,1589],{}," names a run the\nassistant row already holds in ",[946,1862,1724],{},[946,1864,1648],{}," names a reason the\n",[946,1867,1780],{}," row already holds in ",[946,1870,1163],{},", and ",[946,1873,1874],{},"stream.lagged"," names nothing. All\nthree therefore mean one thing to the client, which is to read ",[946,1877,1515],{},"\nagain. One render path then serves a live turn and a reload.",[806,1880,1881,1886,1887,1890],{},[815,1882,1883,1884,1009],{},"A message is immutable, so the merge is a union by ",[946,1885,1520],{}," No message row is\nupdated after it is written. The span merge keeps the newer ",[946,1888,1889],{},"updated_at",", and\nthis one needs no such test. A page merges into what the client holds, and never\nreplaces it: the read starts after the subscribe, so a frame can land before the\npage returns, and a replace would drop it.",[806,1892,1182,1893,1896,1897,1899,1900,1903],{},[815,1894,1895],{},"The reconcile runs after the resubscribe, and not on the next frame."," The\nRun stream may defer it, because the server answers a terminal frame off the row\non every connect, so a frame always follows the gap. Nothing on this channel is\nsynthesized. A ",[946,1898,1686],{}," published inside the gap is the last frame of\nthat turn, so a client that waits for a later frame waits for ever, and the\nmessage stays pending. The connection ends at ",[946,1901,1902],{},"MAX_STREAM_SECONDS",", about\nsixteen times in a working day, so this gap is ordinary.",[806,1905,1906,1909,1910,1912,1913,1915],{},[815,1907,1908],{},"The client sends one message at a time."," The turn queue admits one turn per\nconversation, so a second message carries no ",[946,1911,1533],{}," until the first\nturn ends. The client holds its pending message off the ",[946,1914,1194],{}," and blocks the\nnext send, so no turn is silent.",[806,1917,1182,1918,1927],{},[815,1919,1920,1921,1923,1924,1926],{},"A repeated ",[946,1922,1079],{}," answers ",[946,1925,1099],{}," with the first message."," The\nanswer is not the text of the second request. A client that renders what it sent\nshows the wrong words, so it renders what the route returned.",[1153,1929,1931],{"id":1930},"rules","Rules",[1933,1934,1935,1939,1942,1945,1950,1963,1972,1981,1995,2004],"ul",{},[1936,1937,1938],"li",{},"Web chat has no private path around the Front Door.",[1936,1940,1941],{},"A conversation belongs to one organization. It records its creator, and each message records its own sender.",[1936,1943,1944],{},"A web chat conversation has one person. A shared channel thread can have more, so every turn uses the actor of the person who wrote that message.",[1936,1946,1947,1949],{},[946,1948,1284],{}," is the platform identifier. A later Channel Gateway maps an external thread onto it.",[1936,1951,1952,1953,1955,1956,1958,1959,1962],{},"The route refuses a message over 8000 characters with ",[946,1954,1083],{},", and it refuses an empty one with ",[946,1957,1083],{},". There is no gateway in front of it to drop one. The bound is a route bound: ",[946,1960,1961],{},"agent.conversation_messages.text"," carries no length check, and it refuses only the empty string.",[1936,1964,1965,1966,1968,1969,1971],{},"The client sends ",[946,1967,1079],{},", and the route answers ",[946,1970,1099],{}," with the message a repeated key already created.",[1936,1973,1974,1550,1977,1980],{},[815,1975,1976],{},"The route namespaces the key per caller and per conversation.",[946,1978,1979],{},"uq_conversation_messages_client_key"," is unique on the organization, and one organization holds many people and many conversations. A key namespaced on the caller alone would answer one conversation with the message of another.",[1936,1982,1983,1986,1987,1990,1991,1994],{},[815,1984,1985],{},"A duplicate sends the durable event again."," The event id is ",[946,1988,1989],{},"conversation.turn:\u003Cmessage_id>",", so Inngest drops the second copy inside ",[946,1992,1993],{},"EVENT_DEDUPE_WINDOW"," and no second turn runs. A first send that never landed then recovers on the retry. Without the re-send that message is never answered, and no error says so.",[1936,1996,1997,2000,2001,2003],{},[815,1998,1999],{},"A conversation is read by the person who created it."," The list filters on the creator, and every route answers ",[946,2002,1028],{}," for a colleague's conversation. A shared channel thread relaxes this, and the Channel Gateway owns it.",[1936,2005,2006,1550,2009,2012],{},[815,2007,2008],{},"An impersonated session reads a conversation and writes none.",[946,2010,2011],{},"sender_user_id"," would name the person acted for, and the turn would spend that organization's ceiling under a name nobody can correct. The Approval Inbox refuses a resolution for the same reason.",[968,2014,864],{"id":2015},"approval-inbox",[806,2017,2018,2019,2021,2022,2024],{},"The Approval Inbox resolves the shared approval row. ",[1005,2020,288],{"href":287}," owns the decision, the expiry clock, the state writers and the re-checks before an approved action runs. ",[1005,2023,345],{"href":344}," owns the product view.",[1158,2026,2029],{"className":2027,"code":2028,"language":1163,"meta":1164},[1161],"GET  \u002Fapi\u002Fv1\u002Fagentic\u002Fapprovals?status=pending&cursor=\nGET  \u002Fapi\u002Fv1\u002Fagentic\u002Fapprovals\u002F{id}\nPOST \u002Fapi\u002Fv1\u002Fagentic\u002Fapprovals\u002F{id}\u002Fapprove\nPOST \u002Fapi\u002Fv1\u002Fagentic\u002Fapprovals\u002F{id}\u002Freject\n",[946,2030,2028],{"__ignoreMap":1164},[806,2032,2033,2034,2037,2038,2041],{},"It does ",[815,2035,2036],{},"not"," expose ",[946,2039,2040],{},"resume_run",". Resumption is a consequence of resolving the row.",[1153,2043,2045],{"id":2044},"the-list","The list",[1933,2047,2048,2054,2065,2084,2106,2123],{},[1936,2049,2050,2051,2053],{},"The list is scoped to the organization, and ",[946,2052,1138],{}," is its one filter.",[1936,2055,2056,2064],{},[815,2057,2058,2060,2061,1009],{},[946,2059,1138],{}," defaults to ",[946,2062,2063],{},"pending"," The page is the work that waits, and a list of every status reads no index range.",[1936,2066,2067,2076,2077,2079,2080,2083],{},[815,2068,2069,2070,2073,2074,1009],{},"One sort key: ",[946,2071,2072],{},"expires_at"," ascending, then ",[946,2075,1520],{}," The product asks for the soonest expiry first. ",[946,2078,1520],{}," breaks the tie, because two approvals of one ",[946,2081,2082],{},"parallel"," node share one expiry.",[1936,2085,1182,2086,1550,2089,2091,2092,2095,2096,2098,2099,2101,2102,2105],{},[815,2087,2088],{},"The tie break carries no meaning, and it does not answer \"oldest first\".",[946,2090,1520],{}," is a random UUID, so two rows of one expiry come back in an arbitrary but stable order. A page that promised the oldest request first would key ",[946,2093,2094],{},"created_at"," as a fifth index column, and V1 needs no such promise. Nor is ",[946,2097,2072],{}," order the same as ",[946,2100,2094],{}," order: ",[946,2103,2104],{},"ApprovalService.create()"," takes the smaller of the rule's TTL and the run's own deadline, so a later request can expire first.",[1936,2107,1182,2108,2111,2112,2115,2116,2118,2119,2122],{},[815,2109,2110],{},"A pending row that expires mid-page is withheld from the rest of that page."," Each page reads the clock again, so a row whose expiry passes between page one and page two fails the ",[946,2113,2114],{},"expires_at > now"," filter and no later pending page carries it. It is not lost: it moves to the expired list, where the reader finds it. The expired list has no such gap, because ",[946,2117,2072],{}," is frozen and every member of that set already satisfies ",[946,2120,2121],{},"expires_at \u003C= now",", so a row joins it after the cursor and never before it.",[1936,2124,2125,2128,2129,2132],{},[946,2126,2127],{},"idx_approvals_org_status_expires"," replaces ",[946,2130,2131],{},"idx_approvals_org_status_created",", which no query reads. It answers the pending page as one ordered range scan. It cannot answer the expired page in order, because that page is two ranges: the planner reads both and sorts the whole set of one tenant before the limit. That is the History tab, and it is read rarely.",[1153,2134,2136],{"id":2135},"the-expired-state-a-row-derives","The expired state a row derives",[806,2138,2139,2140,2142],{},"A list computes the expired state from ",[946,2141,2072],{},", so the queue never offers a dead row before the wait timeout fires.",[1158,2144,2147],{"className":2145,"code":2146,"language":1163,"meta":1164},[1161],"status=pending   ->  status = 'pending' AND expires_at > now\nstatus=expired   ->  status = 'expired' OR (status = 'pending' AND expires_at \u003C= now)\nany other        ->  status = \u003Cvalue>\n",[946,2148,2146],{"__ignoreMap":1164},[806,2150,2151],{},"The second line keeps a lapsed row reachable. Without it a row sits in no list at all between its expiry and the timeout write.",[806,2153,1182,2154,2157,2158,2160],{},[815,2155,2156],{},"One clock answers one request."," The API reads its own clock once, and the filter and the response field both take that value. Two clocks let the list and the row disagree at the boundary, and a person then reads ",[946,2159,2063],{}," on a row the queue withheld.",[1153,2162,2164],{"id":2163},"the-two-writes","The two writes",[1933,2166,2167,2173,2185,2194,2211,2217,2237],{},[1936,2168,2169,2170,2172],{},"A repeated resolve answers ",[946,2171,1099],{}," with the current state.",[1936,2174,2175,2181,2182,2184],{},[815,2176,2177,2178,2180],{},"A write refuses one row, and only one: a row that still reads ",[946,2179,2063],{}," and whose expiry has passed."," It answers ",[946,2183,1099],{}," with the current state and writes nothing. A queue that never offers a dead row must not accept a decision on one, or the record says a person authorized work that never ran.",[1936,2186,1182,2187,2190,2191,2193],{},[815,2188,2189],{},"Every other row reaches the resolver, and a row already resolved must."," The resolver sends the wake event whether or not it wrote, and that second send is the only repair for an event lost between the write and the first send. A route that answered a resolved row from its own read would remove the repair: the run would sleep until its wait timed out, and it would then report that nobody answered after a person had answered. Nothing is written or sent twice. The update is conditional on ",[946,2192,2063],{},", and the send refuses a status no person wrote.",[1936,2195,1182,2196,2199,2200,2203,2204,2207,2208,2210],{},[815,2197,2198],{},"The lapse guard protects the record, and it is not the safety net."," The route reads the row, then writes it, and the row can lapse between the two. ",[946,2201,2202],{},"ApprovalService.authorizes()"," check 2 still refuses a decision whose ",[946,2205,2206],{},"resolved_at"," is later than ",[946,2209,2072],{},", so a lapse inside that window stops the effect. The two rules do not contradict each other: check 2 measures when the decision was made, and the guard measures whether the row was still open when a person pressed.",[1936,2212,2213,2214,2216],{},"Two concurrent decisions produce one transition. The update is conditional on ",[946,2215,2063],{},", and the loser reads the winner's state.",[1936,2218,2219,2181,2222,2224,2225,2228,2229,2232,2233,2236],{},[815,2220,2221],{},"An impersonated session may read, and it may not resolve.",[946,2223,1032],{},". ",[946,2226,2227],{},"AuthContext"," resolves the effective user, so ",[946,2230,2231],{},"resolved_by"," would name the person acted for, and ",[946,2234,2235],{},"agent.approvals"," holds no free text field that names the caller behind them. A human decision that authorizes an effect must name the person who made it.",[1936,2238,2239,2240,1009],{},"V1 checks visibility alone. The scope of the approver is checked before the effect runs, and not here. See ",[1005,2241,2242],{"href":287},"policy and governance",[1153,2244,2246],{"id":2245},"what-the-projection-carries","What the projection carries",[1933,2248,2249,2255,2261,2279],{},[1936,2250,2251,2254],{},[946,2252,2253],{},"proposed_arguments"," holds business data, such as an email body. It follows the Tool result redaction rules, which are a key name rule, so the response redacts a credential and the stored row stays executable.",[1936,2256,2257,2260],{},[946,2258,2259],{},"preview"," is the line a handler rendered. It is text, so no key name rule reaches it.",[1936,2262,2263,2264,2267,2268,2271,2272,1574,2275,2278],{},"The projection carries no ",[946,2265,2266],{},"continuation"," and no ",[946,2269,2270],{},"idempotency_key",". Both are runtime plumbing, exactly as ",[946,2273,2274],{},"lane",[946,2276,2277],{},"execution_ref"," are on a run.",[1936,2280,2281,2287,2288,2291],{},[815,2282,2283,2284,2286],{},"V1 answers ",[946,2285,1724],{},", and no definition name."," The row holds no definition, and a join would put the ",[946,2289,2290],{},"agent.runs"," shape inside a policy repository. A reader opens the run.",[968,2293,875],{"id":2294},"prospect-review",[806,2296,2297,2298,2300],{},"Prospect review is business curation over the Signals Search result. It is not an approval. It creates no ",[946,2299,2235],{}," row.",[806,2302,2303,2304,2306,2307,2309,2310,2312],{},"The product tables live in ",[946,2305,1044],{},". This surface uses ",[946,2308,1048],{}," instead of the ",[946,2311,1036],{}," schema client.",[1158,2314,2317],{"className":2315,"code":2316,"language":1163,"meta":1164},[1161],"GET  \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects?review_state=new&cursor=&limit=\nGET  \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\nGET  \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fpeople?cursor=&limit=\nGET  \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fsignals?cursor=&limit=\nPOST \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fwatch\nPOST \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fdismiss\nPOST \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fpromote\n",[946,2318,2316],{"__ignoreMap":1164},[806,2320,2321,2322,1009],{},"Promotion is not a state patch. The route resolves or creates the CRM records before it writes ",[946,2323,2324],{},"promoted",[1153,2326,2328],{"id":2327},"the-three-pages","The three pages",[1933,2330,2331,2341,2350,2356,2359,2362],{},[1936,2332,2333,2334,2337,2338,1009],{},"The prospect list requires one ",[946,2335,2336],{},"review_state",". It defaults to ",[946,2339,2340],{},"new",[1936,2342,2343,2344,2346,2347,2349],{},"Every page orders by ",[946,2345,2094],{}," descending, then ",[946,2348,1520],{}," descending. Both values are immutable.",[1936,2351,2352,2353,2355],{},"The cursor is opaque. A timestamp tie cannot drop or repeat a row because ",[946,2354,1520],{}," breaks it.",[1936,2357,2358],{},"A row inserted after page one appears before that page's cursor. It appears after a refresh and does not shift the remaining pages.",[1936,2360,2361],{},"A score or evidence update cannot move a row between pages. A review-state change can remove a row from its old filtered list, which is the intended result.",[1936,2363,2364,2365,2367,2368,1574,2370,2372],{},"The list, people page and signals page each have an index on ",[946,2366,1040],{},", the parent or state filter, ",[946,2369,2094],{},[946,2371,1520],{}," in query order.",[1153,2374,2376],{"id":2375},"the-projections","The projections",[806,2378,2379,2380,949,2383,1574,2386,2389,2390,2393,2394,2396,2397,1574,2400,2224,2403,2406],{},"The API uses these exact JSON fields. ",[946,2381,2382],{},"UUID",[946,2384,2385],{},"datetime",[946,2387,2388],{},"decimal"," values use their normal JSON string form. Every field marked ",[946,2391,2392],{},"null"," is present but can be JSON ",[946,2395,2392],{},".\nEach page returns ",[946,2398,2399],{},"items",[946,2401,2402],{},"next_cursor: string | null",[946,2404,2405],{},"limit"," defaults to 50 and accepts 1 through 100.",[806,2408,2409,2412],{},[946,2410,2411],{},"ProspectSummary"," contains:",[827,2414,2415,2428],{},[830,2416,2417],{},[833,2418,2419,2422,2425],{},[836,2420,2421],{},"Field",[836,2423,2424],{},"Type",[836,2426,2427],{},"Source",[846,2429,2430,2443,2458,2472,2496,2511,2525,2539,2561,2575,2590,2604,2618,2631],{},[833,2431,2432,2436,2438],{},[851,2433,2434],{},[946,2435,1520],{},[851,2437,2382],{},[851,2439,2440],{},[946,2441,2442],{},"prospects.id",[833,2444,2445,2450,2453],{},[851,2446,2447],{},[946,2448,2449],{},"company_name",[851,2451,2452],{},"string | null",[851,2454,2455],{},[946,2456,2457],{},"intel_companies.name",[833,2459,2460,2465,2467],{},[851,2461,2462],{},[946,2463,2464],{},"company_domain",[851,2466,2452],{},[851,2468,2469],{},[946,2470,2471],{},"intel_companies.domain",[833,2473,2474,2478,2491],{},[851,2475,2476],{},[946,2477,2336],{},[851,2479,2480,2482,2483,2482,2486,2482,2489],{},[946,2481,2340],{}," | ",[946,2484,2485],{},"watching",[946,2487,2488],{},"dismissed",[946,2490,2324],{},[851,2492,2493],{},[946,2494,2495],{},"prospects.review_state",[833,2497,2498,2503,2506],{},[851,2499,2500],{},[946,2501,2502],{},"opportunity_score",[851,2504,2505],{},"integer | null",[851,2507,2508],{},[946,2509,2510],{},"prospects.opportunity_score",[833,2512,2513,2518,2520],{},[851,2514,2515],{},[946,2516,2517],{},"opportunity_reason",[851,2519,2452],{},[851,2521,2522],{},[946,2523,2524],{},"prospects.opportunity_reason",[833,2526,2527,2532,2534],{},[851,2528,2529],{},[946,2530,2531],{},"recommended_action",[851,2533,2452],{},[851,2535,2536],{},[946,2537,2538],{},"prospects.recommended_action",[833,2540,2541,2546,2556],{},[851,2542,2543],{},[946,2544,2545],{},"people_state",[851,2547,2548,2482,2550,2482,2553],{},[946,2549,2063],{},[946,2551,2552],{},"found",[946,2554,2555],{},"no_matching_people",[851,2557,2558],{},[946,2559,2560],{},"prospects.people_state",[833,2562,2563,2568,2570],{},[851,2564,2565],{},[946,2566,2567],{},"people_state_reason",[851,2569,2452],{},[851,2571,2572],{},[946,2573,2574],{},"prospects.people_state_reason",[833,2576,2577,2582,2585],{},[851,2578,2579],{},[946,2580,2581],{},"crm_company_id",[851,2583,2584],{},"UUID | null",[851,2586,2587],{},[946,2588,2589],{},"prospects.crm_company_id",[833,2591,2592,2597,2599],{},[851,2593,2594],{},[946,2595,2596],{},"first_seen_at",[851,2598,2385],{},[851,2600,2601],{},[946,2602,2603],{},"prospects.first_seen_at",[833,2605,2606,2611,2613],{},[851,2607,2608],{},[946,2609,2610],{},"last_seen_at",[851,2612,2385],{},[851,2614,2615],{},[946,2616,2617],{},"prospects.last_seen_at",[833,2619,2620,2624,2626],{},[851,2621,2622],{},[946,2623,2094],{},[851,2625,2385],{},[851,2627,2628],{},[946,2629,2630],{},"prospects.created_at",[833,2632,2633,2637,2639],{},[851,2634,2635],{},[946,2636,1889],{},[851,2638,2385],{},[851,2640,2641],{},[946,2642,2643],{},"prospects.updated_at",[806,2645,2646,2649,2650,2652,2653,2655,2656,949,2659,949,2662,949,2665,949,2668,949,2671,949,2674,949,2677,949,2680,949,2683,949,2686,949,2689,949,2692,949,2695,949,2698,949,2701,949,2704,949,2707,1574,2710,2713],{},[946,2647,2648],{},"ProspectDetail"," contains all ",[946,2651,2411],{}," fields plus one ",[946,2654,321],{}," object. That object contains ",[946,2657,2658],{},"id: UUID",[946,2660,2661],{},"linkedin_url: string | null",[946,2663,2664],{},"website: string | null",[946,2666,2667],{},"industry: string | null",[946,2669,2670],{},"sub_industry: string | null",[946,2672,2673],{},"business_model: string | null",[946,2675,2676],{},"location: string | null",[946,2678,2679],{},"employee_count_exact: integer | null",[946,2681,2682],{},"employee_count_band: string | null",[946,2684,2685],{},"annual_revenue: decimal | null",[946,2687,2688],{},"revenue_band: string | null",[946,2690,2691],{},"revenue_currency: string | null",[946,2693,2694],{},"revenue_year: integer | null",[946,2696,2697],{},"funding_round: string | null",[946,2699,2700],{},"funding_amount: string | null",[946,2702,2703],{},"fetched_cold_at: datetime | null",[946,2705,2706],{},"fetched_warm_at: datetime | null",[946,2708,2709],{},"fetched_hot_at: datetime | null",[946,2711,2712],{},"last_enriched_at: datetime",". It embeds no people, signals or sources.",[806,2715,2716,2717,949,2719,949,2722,949,2725,949,2728,949,2731,949,2734,949,2737,949,2740,949,2743,2746,2747,2750,2751,949,2753,949,2756,949,2759,949,2762,949,2765,949,2768,949,2770,949,2773,1574,2776,2778,2779,2782],{},"Each people-page item contains ",[946,2718,2658],{},[946,2720,2721],{},"prospect_id: UUID",[946,2723,2724],{},"crm_person_id: UUID | null",[946,2726,2727],{},"persona_fit_score: integer | null",[946,2729,2730],{},"persona_fit_reason: string | null",[946,2732,2733],{},"contact_state: unverified | verified | unavailable",[946,2735,2736],{},"first_seen_at: datetime",[946,2738,2739],{},"last_seen_at: datetime",[946,2741,2742],{},"created_at: datetime",[946,2744,2745],{},"updated_at: datetime"," and one ",[946,2748,2749],{},"person"," object. The person object contains ",[946,2752,2658],{},[946,2754,2755],{},"linkedin_url: string",[946,2757,2758],{},"full_name: string | null",[946,2760,2761],{},"avatar_url: string | null",[946,2763,2764],{},"current_title: string | null",[946,2766,2767],{},"current_company_text: string | null",[946,2769,2676],{},[946,2771,2772],{},"country: string | null",[946,2774,2775],{},"email: string | null",[946,2777,2712],{},". It returns no ",[946,2780,2781],{},"source_ids",", provider payload, phone number or profile history.",[806,2784,2785,2786,949,2788,949,2790,949,2793,949,2796,949,2798,949,2800,2746,2802,2805,2806,949,2808,949,2811,949,2814,949,2817,949,2820,949,2823,1574,2826,2829,2830,2833,2834,949,2837,1574,2840,2843,2844,2847,2848,2851],{},"Each signals-page item contains ",[946,2787,2658],{},[946,2789,2721],{},[946,2791,2792],{},"signal_score: integer | null",[946,2794,2795],{},"signal_reason: string | null",[946,2797,2736],{},[946,2799,2742],{},[946,2801,2745],{},[946,2803,2804],{},"signal"," object. The signal object contains ",[946,2807,2658],{},[946,2809,2810],{},"subject_type: company | person",[946,2812,2813],{},"subject_id: UUID",[946,2815,2816],{},"signal_type: string",[946,2818,2819],{},"description: string | null",[946,2821,2822],{},"observed_at: datetime",[946,2824,2825],{},"ingested_at: datetime",[946,2827,2828],{},"source: object | null",". When present, ",[946,2831,2832],{},"source"," contains ",[946,2835,2836],{},"provider: string",[946,2838,2839],{},"ref: string | null",[946,2841,2842],{},"fetched_at: datetime",". The API resolves it from the nullable ",[946,2845,2846],{},"intel_signals.source_id","; it does not require an ",[946,2849,2850],{},"intel_signal_sources"," primary row. It returns no provider payload, price or corroborating source array.",[806,2853,2854,2855,2858,2859,2862],{},"The global Intelligence tables deny ",[946,2856,2857],{},"authenticated",", but this API reads them with the service role. The root ",[946,2860,2861],{},"scoped_db"," query still filters the organization-owned prospect row. The response selects only the fields above.",[1153,2864,2866],{"id":2865},"watch-and-dismiss","Watch and dismiss",[1933,2868,2869,2880,2893,2898,2901],{},[1936,2870,1965,2871,959,2874,2877,2878,1009],{},[946,2872,2873],{},"watch",[946,2875,2876],{},"dismiss"," as a route intent. It never sends ",[946,2879,2336],{},[1936,2881,2882,2883,949,2885,959,2887,2889,2890,2892],{},"Either action can move ",[946,2884,2340],{},[946,2886,2485],{},[946,2888,2488],{},". A repeated action returns ",[946,2891,1099],{}," and writes nothing.",[1936,2894,2895,2897],{},[946,2896,2324],{}," is terminal for these two actions. A conditional update cannot overwrite it, including during a promotion race.",[1936,2899,2900],{},"Concurrent watch and dismiss requests use the last accepted database write. Each client refetches durable detail after its write.",[1936,2902,2903,2904,1009],{},"Watching and dismissing create no approval and never call ",[946,2905,2906],{},"ToolInvoker",[1153,2908,2910],{"id":2909},"promote","Promote",[806,2912,2913,2914,2917],{},"Promotion is the one route of this surface that writes CRM. It shares one service with the ",[946,2915,2916],{},"prospect.promote"," tool, so a person and an agent promote by the same rules.",[1158,2919,2922],{"className":2920,"code":2921,"language":1163,"meta":1164},[1161],"POST \u002Fapi\u002Fv1\u002Fagentic\u002Fprospects\u002F{id}\u002Fpromote\n{ \"person_ids\": [UUID], \"list_id\": UUID | null }\n",[946,2923,2921],{"__ignoreMap":1164},[1933,2925,2926,2929,2935,2944,2970,2973,2978],{},[1936,2927,2928],{},"The path names the prospect. The body never does, and it names no organization.",[1936,2930,2931,2934],{},[946,2932,2933],{},"person_ids"," holds prospect person ids, not CRM ids. It accepts at most 25, refuses a repeat, and an empty list promotes the company alone.",[1936,2936,2937,2940,2941,2943],{},[946,2938,2939],{},"list_id"," names one static CRM list. The ",[815,2942,321],{}," joins it. The route reads the list before the first CRM write, so a list it cannot use refuses the call before it creates a row.",[1936,2945,2946,2947,949,2950,949,2953,2955,2956,2958,2959,949,2962,1574,2965,1871,2968,1009],{},"The answer carries ",[946,2948,2949],{},"prospect_id",[946,2951,2952],{},"review_state: promoted",[946,2954,2581],{},", one ",[946,2957,119],{}," row for each selection with ",[946,2960,2961],{},"prospect_person_id",[946,2963,2964],{},"intel_person_id",[946,2966,2967],{},"crm_person_id",[946,2969,2939],{},[1936,2971,2972],{},"Every step resolves rather than creates. A second promotion writes nothing and answers the same references. A later promotion that selects a new person links that person and changes nothing else.",[1936,2974,2975,2977],{},[946,2976,2324],{}," is written last. A partial attempt keeps the CRM rows and the earlier review state, and a retry completes the missing links.",[1936,2979,2980,2981,2984],{},"The route passes ",[815,2982,2983],{},"no policy checkpoint",", because a checkpoint belongs to a run. A policy rule on promotion applies on the tool path.",[1153,2986,2988],{"id":2987},"every-outcome-has-one-status","Every outcome has one status",[827,2990,2991,3004],{},[830,2992,2993],{},[833,2994,2995,2997,3001],{},[836,2996,1667],{},[836,2998,3000],{"align":2999},"right","Status",[836,3002,3003],{},"Meaning",[846,3005,3006,3018,3030,3042,3054,3066,3079],{},[833,3007,3008,3011,3015],{},[851,3009,3010],{},"unreadable cursor",[851,3012,3013],{"align":2999},[946,3014,1083],{},[851,3016,3017],{},"the cursor is empty, malformed or for another order",[833,3019,3020,3023,3027],{},[851,3021,3022],{},"unusable company or list",[851,3024,3025],{"align":2999},[946,3026,1083],{},[851,3028,3029],{},"the company has no name, or the list is not static and does not accept a company",[833,3031,3032,3035,3039],{},[851,3033,3034],{},"not found",[851,3036,3037],{"align":2999},[946,3038,1028],{},[851,3040,3041],{},"the prospect, parent prospect, selected person or list is missing or belongs to another organization",[833,3043,3044,3047,3051],{},[851,3045,3046],{},"already promoted",[851,3048,3049],{"align":2999},[946,3050,1103],{},[851,3052,3053],{},"watch or dismiss cannot overwrite the promotion result",[833,3055,3056,3059,3063],{},[851,3057,3058],{},"person cannot resolve",[851,3060,3061],{"align":2999},[946,3062,1103],{},[851,3064,3065],{},"a selected person names more than one CRM row, or already holds a different CRM company link",[833,3067,3068,3071,3076],{},[851,3069,3070],{},"invalid id, state or limit",[851,3072,3073],{"align":2999},[946,3074,3075],{},"422",[851,3077,3078],{},"request validation refused the input",[833,3080,3081,3084,3089],{},[851,3082,3083],{},"storage fault",[851,3085,3086],{"align":2999},[946,3087,3088],{},"500",[851,3090,3091],{},"the error decorator recorded an unexpected repository failure",[806,3093,3094,3095,3097],{},"Every success returns ",[946,3096,1099],{},". A page may return no items. A repeated watch, dismiss or promote returns the current result.",[968,3099,885],{"id":257},[806,3101,3102,3103,3105,3106,3108,3109,3112],{},"A saved search is one repeatable Signals Search brief. It is product state in\n",[946,3104,1044],{},", so its repository uses ",[946,3107,1048],{},". It is not the\nlegacy ",[946,3110,3111],{},"workflow_presets"," table.",[1158,3114,3117],{"className":3115,"code":3116,"language":1163,"meta":1164},[1161],"GET    \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches?cursor=&limit=\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\u002F{id}\nPATCH  \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\u002F{id}\nDELETE \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\u002F{id}\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\u002F{id}\u002Fruns       Idempotency-Key\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fsaved-searches\u002F{id}\u002Fdiff?cursor=&limit=\n",[946,3118,3116],{"__ignoreMap":1164},[1153,3120,3122],{"id":3121},"the-stored-brief","The stored brief",[806,3124,3125,3126,1574,3129,3132,3133,3136],{},"Create takes ",[946,3127,3128],{},"name",[946,3130,3131],{},"brief",". Patch takes ",[946,3134,3135],{},"expected_updated_at"," and at least\none of those two fields. The API trims the name and accepts 1 through 200\ncharacters. The database keeps the unique name rule inside one organization.",[806,3138,3139,3140,3142,3143,3146,3147,3150,3151,949,3154,949,3157,959,3160,3163],{},"The brief is an object. It must contain a non-empty ",[946,3141,61],{}," string or a non-empty\n",[946,3144,3145],{},"company_criteria"," array. It must also contain a ",[946,3148,3149],{},"persona"," object with at least\none value in ",[946,3152,3153],{},"titles",[946,3155,3156],{},"departments",[946,3158,3159],{},"seniority",[946,3161,3162],{},"country_codes",".\nThe API preserves every other JSON field.",[806,3165,3166],{},"Create validates and normalizes the supplied brief. A patch that changes the\nbrief validates and normalizes the resulting brief. Normalization trims the\nsupported ICP, company criteria and persona strings before it applies their\nlength bounds. It preserves unknown top-level fields unchanged. The stored\nbrief and the response contain this normalized copy. A name-only patch does not\nvalidate or normalize an unchanged brief. Start normalizes legacy stored input\nbefore it validates and freezes the Run input. Thus, a legacy row without a\nusable persona stays readable and can be renamed or deleted, but it cannot\nstart or change its brief until a patch supplies a usable brief.",[806,3168,3169],{},"The API measures the complete saved-search Run input before create or patch\nstores a brief. It refuses a brief that makes this input exceed the platform's\n32 KiB Run-input limit. A brief accepted by create or patch is startable.",[806,3171,3172,2833,3175,949,3177,949,3179,949,3182,3185,3186,1574,3188,3190,3191,3194,3195,3197],{},[946,3173,3174],{},"SavedSearchSummary",[946,3176,1520],{},[946,3178,3128],{},[946,3180,3181],{},"last_run_id",[946,3183,3184],{},"last_run_at",",\n",[946,3187,2094],{},[946,3189,1889],{},". The two Run fields can be null.\n",[946,3192,3193],{},"SavedSearchDetail"," adds ",[946,3196,3131],{},". A list never returns every brief.",[806,3199,3200,3201,2346,3203,3205,3206,1574,3208,2224,3211,3213],{},"The list orders by ",[946,3202,2094],{},[946,3204,1520],{}," descending. Both values\nare immutable. It returns ",[946,3207,2399],{},[946,3209,3210],{},"next_cursor",[946,3212,2405],{}," defaults to 50 and\naccepts 1 through 100.",[806,3215,3216,3217,3219,3220,1009],{},"Patch uses ",[946,3218,1889],{}," as an opaque write token. The client echoes the exact\nstring. An empty patch writes nothing, so it cannot take another writer's\ntoken. Delete carries no token and returns ",[946,3221,3222],{},"204",[1153,3224,3226],{"id":3225},"start-one-saved-search","Start one saved search",[1158,3228,3230],{"className":1291,"code":3229,"language":1293,"meta":1164,"style":1164},"{\n  \"contract_version\": 1\n}\n",[946,3231,3232,3237,3247],{"__ignoreMap":1164},[1297,3233,3234],{"class":1299,"line":22},[1297,3235,3236],{"class":1302},"{\n",[1297,3238,3239,3242,3244],{"class":1299,"line":32},[1297,3240,3241],{"class":1306},"  \"contract_version\"",[1297,3243,1310],{"class":1302},[1297,3245,3246],{"class":1306},"1\n",[1297,3248,3249],{"class":1299,"line":233},[1297,3250,1390],{"class":1302},[806,3252,3253],{},"The start route reads the visible saved search and builds this Run input:",[1158,3255,3257],{"className":1291,"code":3256,"language":1293,"meta":1164,"style":1164},"{\n  \"source\": \"saved_search\",\n  \"saved_search_id\": \"UUID\",\n  \"baseline_run_id\": \"UUID | null\",\n  \"brief\": {}\n}\n",[946,3258,3259,3263,3275,3287,3299,3307],{"__ignoreMap":1164},[1297,3260,3261],{"class":1299,"line":22},[1297,3262,3236],{"class":1302},[1297,3264,3265,3268,3270,3273],{"class":1299,"line":32},[1297,3266,3267],{"class":1306},"  \"source\"",[1297,3269,1310],{"class":1302},[1297,3271,3272],{"class":1313},"\"saved_search\"",[1297,3274,3185],{"class":1302},[1297,3276,3277,3280,3282,3285],{"class":1299,"line":233},[1297,3278,3279],{"class":1306},"  \"saved_search_id\"",[1297,3281,1310],{"class":1302},[1297,3283,3284],{"class":1313},"\"UUID\"",[1297,3286,3185],{"class":1302},[1297,3288,3289,3292,3294,3297],{"class":1299,"line":244},[1297,3290,3291],{"class":1306},"  \"baseline_run_id\"",[1297,3293,1310],{"class":1302},[1297,3295,3296],{"class":1313},"\"UUID | null\"",[1297,3298,3185],{"class":1302},[1297,3300,3301,3304],{"class":1299,"line":264},[1297,3302,3303],{"class":1306},"  \"brief\"",[1297,3305,3306],{"class":1302},": {}\n",[1297,3308,3309],{"class":1299,"line":222},[1297,3310,1390],{"class":1302},[806,3312,3313,3314,3317,3318,3321,3322,1009],{},"The caller cannot replace the stored brief or name an executor. Initial Run\nadmission calls the shared capability start service for ",[946,3315,3316],{},"signals.search"," with\nthe supplied contract version, one frozen input and the caller-scoped key. It\nuses capability validation and records the normal capability request digest.\nIt returns the same Run detail. A stale version returns\n",[946,3319,3320],{},"409 contract_version_conflict",", and an unavailable binding returns\n",[946,3323,3324],{},"409 capability_unavailable",[806,3326,3327],{},"The client key contains 1 to 255 characters. Before any mutable saved-search\nread, the route uses the shared atomic idempotency plane. The exact claim\nidentity is:",[1158,3329,3332],{"className":3330,"code":3331,"language":1163,"meta":1164},[1161],"organization_id = actor.organization_id\nscope           = surface.saved_search.start\nkey             = \"user:\" + actor.user_id + \":\" + client key\nrequest_hash    = SHA-256(canonical JSON {saved_search_id, contract_version})\n",[946,3333,3331],{"__ignoreMap":1164},[806,3335,3336,3337,3340],{},"The stable capability delivery key uses the actor user ID and client key only.\nBefore the route reads or claims anything mutable, it looks up that Run key. A\nRun whose capability, version and frozen saved-search ID match returns as a\nduplicate. Any mismatch returns ",[946,3338,3339],{},"409 idempotency_conflict",". Search ID and\nversion validate the claimed Run; they do not select another Run key.",[806,3342,3343],{},"The claim's stored response is the normalized brief, saved-search ID and\nbaseline Run ID, not an HTTP response. The winner reads current state, composes\nthe input and completes the claim with that immutable snapshot before\ncapability admission. Only a successful fenced completion may continue to\nadmission. A worker that loses its lease stops.",[806,3345,3346,3347,3350,3351,3354],{},"This preparation scope uses a 30-second lease and 24-hour retention. The lease\ncovers tenant reads, normalization and the snapshot write. A concurrent loser\nnever reads mutable saved-search state. It uses the completed snapshot, or\nreturns retryable ",[946,3348,3349],{},"409 start_in_progress"," with ",[946,3352,3353],{},"Retry-After: 1"," while the lease\nis live. After the lease expires, exactly one caller reclaims it. The retention\nmatches the browser recovery window. After snapshot retention, an existing Run\nstill returns through its stable Run key for the complete 396-day Run retention\nwindow. If neither the snapshot nor the Run remains, a delivery is a new\nrequest and reads current state.",[806,3356,3357],{},"A failed winner releases a claim only when it froze no snapshot and started no\nwork. The capability delivery key does not depend on the claim ID or mutable\ninput. Thus, every delivery of the endpoint identity reaches one Run key.",[806,3359,3360,3361,3363],{},"During the 24-hour snapshot window, a later delivery sends the same input\nthrough normal capability digest replay. After that window, the stable Run key\nreturns an admitted Run directly. Neither path recomposes an admitted Run's\nrequest from mutable state. After the Run retention window, the delivery reads\ncurrent state as a new request. Reusing the key with another search or contract\nversion returns ",[946,3362,3339],{}," while the snapshot claim or Run remains.",[806,3365,3366,3367,3370,3371,3374],{},"The CLI command is\n",[946,3368,3369],{},"ac agentic saved-searches start SEARCH_ID --contract-version VERSION --idempotency-key KEY",".\nIt does not accept ",[946,3372,3373],{},"--definition",". Any CLI plugin recipe that starts a saved\nsearch uses the same stable arguments.",[806,3376,3377,3378,3381,3382,3384],{},"The input is a snapshot. ",[946,3379,3380],{},"baseline_run_id"," is the saved search's current\n",[946,3383,3181],{}," when the Run starts. A later saved-search edit or completed Run\ncannot change either snapshot value. Deleting the saved search does not cancel\nthat Run. The Run still holds its input, but a later product write can find that\nthe search no longer exists.",[806,3386,3387],{},"The route creates no Trigger, schedule, queue or product runtime. Phase 5 can\nbind a Trigger to the same published workflow.",[806,3389,3390,3391,3394,3395,3398,3399,959,3402,3405],{},"This input requires the normalized Signals Search source step. The published\nworkflow must start with ",[946,3392,3393],{},"signals.compile_search_scope",", and later nodes must\nread its frozen ",[946,3396,3397],{},"as_of",", brief and persona. A definition that still reads\n",[946,3400,3401],{},"input.as_of",[946,3403,3404],{},"input.persona"," cannot run a saved search and must not enable\nSmart Feed publication.",[1153,3407,3409],{"id":3408},"the-latest-smart-feed-diff","The latest Smart Feed diff",[806,3411,3412,3413,3416,3417,3420,3421,3423],{},"The diff route reads ",[946,3414,3415],{},"saved_searches.last_run_id",". It returns the diff of that\nlatest published successful Run. If no Run has published, it returns\n",[946,3418,3419],{},"run_id: null",", an empty ",[946,3422,2399],{}," array and no cursor. A published Run with no\nmaterial change returns its Run id and the same empty page.",[806,3425,3426,3427,3430,3431,3434,3435,3438,3439,3441,3442,3444],{},"Only a Run whose durable status is ",[946,3428,3429],{},"succeeded"," can replace this pointer. The\nlast workflow node is ",[946,3432,3433],{},"compile-smart-feed-observations",". It reads all named\nprospects, signal grades and attached people in one database statement. The\nsame snapshot checks that every prospect still names this Run in\n",[946,3436,3437],{},"last_seen_run_id",". A mismatch returns ",[946,3440,473],{},". A ready result holds one\ndigest envelope per prospect and the scope step's frozen ",[946,3443,3397],{}," observation\ntime. The node moves no pointer and writes no publication row.",[806,3446,3447,3448,3451,3452,3455,3456,3459,3460,3463,3464,3467,3468,3471,3472,3475],{},"The workflow selects the node output under ",[946,3449,3450],{},"RunResult.output.smart_feed",". It\ndoes not store the eleven earlier node outputs in the completed Run result. A\nlater Run can change mutable prospect state, but it cannot change this Run\nresult. After ",[946,3453,3454],{},"run.execute"," finalizes the Run, its ",[946,3457,3458],{},"publish.succeeded"," step\nsends ",[946,3461,3462],{},"agent\u002Frun.succeeded"," with only the organization and Run ids. The stable\nevent id is ",[946,3465,3466],{},"run.succeeded:\u003Crun_id>",". The consumer reads the Run again. It\nrequires a saved-search input, no ",[946,3469,3470],{},"partial_reason"," and a schema-valid, ready\n",[946,3473,3474],{},"smart_feed"," output. A missing or dropped projection publishes nothing.",[806,3477,3478,3479,3482,3483,3485,3486,3488,3489,3492],{},"The consumer does not reject the global ",[946,3480,3481],{},"RunResult.truncated"," flag alone.\n",[946,3484,1791],{}," bounds the selected output before it bounds resource refs, so that\nflag can mean that only the diagnostic refs were shortened. The ",[946,3487,3474],{},"\nvalue is one top-level item: it is either intact or replaced by a ",[946,3490,3491],{},"Dropped","\nmarker. Strict projection validation distinguishes those cases.",[806,3494,3495,3496,3499,3500,3503],{},"The writer first records immutable rows in ",[946,3497,3498],{},"saved_search_run_prospects",". Each\nrow names the Run, prospect, observed digest, change reasons and first and last\nseen times. It records every prospect the Run observed. An unchanged prospect\nhas an empty ",[946,3501,3502],{},"change_kinds"," array. Thus, the next Run can compare with the\ncomplete published result instead of its diff page.",[806,3505,3506,3507,949,3509,949,3512,3185,3514,949,3516,949,3519,949,3521,3185,3523,949,3525,1574,3527,3529,3530,3533],{},"The table carries ",[946,3508,1040],{},[946,3510,3511],{},"saved_search_id",[946,3513,1724],{},[946,3515,2949],{},[946,3517,3518],{},"observed_digest",[946,3520,3502],{},[946,3522,2596],{},[946,3524,2610],{},[946,3526,2094],{},[946,3528,1520],{},". Composite foreign keys keep the search\nand prospect in the same organization. Organization-scoped RLS applies. A\npartial index on ",[946,3531,3532],{},"(organization_id, saved_search_id, run_id, created_at DESC, id DESC)"," covers rows whose change array is not empty.",[806,3535,3536,3537,3540],{},"The rows are append-only. The unique key is\n",[946,3538,3539],{},"(saved_search_id, run_id, prospect_id)",". A retry uses insert-on-conflict-do-\nnothing and then verifies that an existing row has the same immutable values.\nIt never updates or deletes an observation.",[806,3542,3543,3545,3546,3548,3549,949,3552,949,3555,949,3558,949,3561,3185,3564,1574,3567,3570],{},[946,3544,3518],{}," is UTF-8 canonical JSON text. Serialization uses sorted keys,\ncomma and colon separators with no added whitespace, unescaped Unicode, JSON\n",[946,3547,2392],{}," for null values and lowercase UUID strings. It refuses non-finite\nnumbers. Text is stored text and gets no second trim. The complete version 1\nshape is ",[946,3550,3551],{},"v",[946,3553,3554],{},"score",[946,3556,3557],{},"signal_count",[946,3559,3560],{},"signals_sha256",[946,3562,3563],{},"people_count",[946,3565,3566],{},"people_sha256",[946,3568,3569],{},"evidence_sha256",". Each SHA-256 value is lowercase hex.",[806,3572,3573],{},"The signal hash reads tuples of Intelligence signal id, signal score and\nsignal reason, ordered by signal id. The people hash reads tuples of\nIntelligence person id, persona-fit score, persona-fit reason and contact\nstate, ordered by person id. The evidence hash reads company name and domain,\nopportunity reason, recommended action, people state and people-state reason.\nEach hash input uses the same canonical JSON rule. An unknown digest version\nstops publication instead of guessing.",[806,3575,3576,3577,3579,3580,3583,3584,3587,3588,3591,3592,3595,3596,3598,3599,949,3601,949,3603,3185,3605,949,3607,3609],{},"The first observation absent from the baseline gets only ",[946,3578,2340],{},". For an\nexisting baseline, any score change gets ",[946,3581,3582],{},"score_changed",". A larger signal\ncount gets ",[946,3585,3586],{},"new_signals",", and this reason explains the signal hash change. A\nlarger people count gets ",[946,3589,3590],{},"new_people",", and this reason explains the people hash\nchange. A same-or-smaller count with a changed collection hash gets\n",[946,3593,3594],{},"evidence_changed",". A changed evidence hash also gets ",[946,3597,3594],{},".\nReasons use this fixed order: ",[946,3600,2340],{},[946,3602,3586],{},[946,3604,3590],{},[946,3606,3582],{},[946,3608,3594],{},". Any exact normalized value change is\nmaterial. There is no score threshold.",[806,3611,3612,3613,3615,3616,3618,3619,3622],{},"The Run input freezes the comparison baseline. Publication compares\n",[946,3614,3415],{}," with that ",[946,3617,3380],{},". If they still match,\none compare-and-set writes the Run id and ",[946,3620,3621],{},"ended_at",". If they do not match,\nanother Run already published and this Run is stale. A stale Run remains in Run\nExplorer but never replaces the Smart Feed. Thus, overlapping Runs cannot\nreplay one baseline's changes.",[806,3624,3625],{},"Publication is a durable, idempotent job after Run success. It retries until\nthe compare-and-set publishes the Run or proves it stale. A retry that finds\nthe pointer already on its Run still finishes membership. A failed, cancelled,\nmissing-projection or pre-promotion partial Run publishes nothing. A Run whose\nonly truncation is in resource refs can still publish its intact projection.",[806,3627,3628],{},"A later Run can start after the pointer moves but before membership finishes.\nBefore it writes, it reads membership for the union of baseline and current\nprospects. If a baseline prospect has no membership, it repairs that row from\nthe immutable baseline observation before it writes its own immutable rows.\nThe baseline Run id supplies both Run fields. The observation supplies the\nfirst and last seen times. This repair also covers a baseline prospect that the\nlater Run omits and a future Run returns. Thus, no terminal consumer failure\ncan block later Runs or freeze incomplete first-seen values.",[806,3630,3631],{},"There is no periodic reconciler. The event send is an Inngest step, so Inngest\nretries it. If the producer still exhausts its retries, the search keeps its\nold pointer. Its next Run freezes the same baseline and publishes the current\ncumulative change. If the consumer exhausts retries after the pointer moves,\nthe next Run repairs missing membership from that immutable published result.\nA product cron and an index on generic Run input would add a second recovery\npath without a stronger product guarantee.",[806,3633,3634,3637,3638,3640,3641,3643,3644,1009],{},[946,3635,3636],{},"saved_search_prospects"," keeps only search-wide membership: the first and most\nrecent successful observation of one prospect. A new immutable observation\nuses the membership ",[946,3639,2596],{},", or its own observation time for new\nmembership. Its ",[946,3642,2610],{}," is the frozen observation time. It never copies\nthe organization-wide times from ",[946,3645,256],{},[806,3647,3648,3649,3651,3652,1009],{},"An idempotent publisher updates membership after the pointer. A newer\n",[946,3650,2610],{}," wins. An equal time must name the same Run and values. An older\nobservation writes nothing.\nIt does not hold a digest, Run diff marker or change reasons. Those values are\nper-Run facts and belong only to ",[946,3653,3498],{},[806,3655,3656,3657,949,3659,949,3661,3663,3664,2778,3666,3669],{},"Each item contains ",[946,3658,3502],{},[946,3660,2596],{},[946,3662,2610],{}," and one\ncurrent ",[946,3665,2411],{},[946,3667,3668],{},"reported_digest",". The writer decides\nmaterial change and records the reasons. The reader never recomputes them.",[806,3671,3672,3673,3675,3676,3678,3679,1574,3681,3683,3684,3686,3687,3689],{},"The page reads rows for ",[946,3674,3181],{}," whose ",[946,3677,3502],{}," is not empty. It\norders them by immutable ",[946,3680,2094],{},[946,3682,1520],{},", newest first. Its cursor also\nbinds the page to ",[946,3685,3181],{},". If another Run becomes latest between pages,\nthe next request returns ",[946,3688,1103],{}," and tells the client to restart. It never\ncombines two Run diffs.",[806,3691,3692],{},"This is not a historical diff API. The route accepts no Run id. Immutable rows\nexist so a failed or overlapping Run cannot damage the published diff; their\nretention is an internal storage rule, not a product promise. Run Explorer is\nthe product Run-history surface.",[1153,3694,2988],{"id":3695},"every-outcome-has-one-status-1",[827,3697,3698,3708],{},[830,3699,3700],{},[833,3701,3702,3704,3706],{},[836,3703,1667],{},[836,3705,3000],{"align":2999},[836,3707,3003],{},[846,3709,3710,3721,3733,3744,3756,3770,3782,3797,3810,3821],{},[833,3711,3712,3714,3718],{},[851,3713,3010],{},[851,3715,3716],{"align":2999},[946,3717,1083],{},[851,3719,3720],{},"the cursor is empty, malformed or for another page",[833,3722,3723,3726,3730],{},[851,3724,3725],{},"unusable brief or empty patch",[851,3727,3728],{"align":2999},[946,3729,1083],{},[851,3731,3732],{},"the stored search could not start, or the patch names no field",[833,3734,3735,3737,3741],{},[851,3736,3034],{},[851,3738,3739],{"align":2999},[946,3740,1028],{},[851,3742,3743],{},"the saved search is missing or belongs to another organization",[833,3745,3746,3749,3753],{},[851,3747,3748],{},"duplicate name",[851,3750,3751],{"align":2999},[946,3752,1103],{},[851,3754,3755],{},"this organization already has the exact trimmed name",[833,3757,3758,3761,3765],{},[851,3759,3760],{},"stale update",[851,3762,3763],{"align":2999},[946,3764,1103],{},[851,3766,3767,3769],{},[946,3768,3135],{}," no longer matches",[833,3771,3772,3775,3779],{},[851,3773,3774],{},"diff changed",[851,3776,3777],{"align":2999},[946,3778,1103],{},[851,3780,3781],{},"a newer Run became the latest while the client paged",[833,3783,3784,3787,3791],{},[851,3785,3786],{},"start in progress",[851,3788,3789],{"align":2999},[946,3790,1103],{},[851,3792,3793,3794],{},"another delivery of this key is freezing the input; retry with the same key after ",[946,3795,3796],{},"Retry-After",[833,3798,3799,3802,3807],{},[851,3800,3801],{},"input too large",[851,3803,3804],{"align":2999},[946,3805,3806],{},"413",[851,3808,3809],{},"the composed saved-search Run input is over 32 KiB",[833,3811,3812,3815,3819],{},[851,3813,3814],{},"invalid id, name or limit",[851,3816,3817],{"align":2999},[946,3818,3075],{},[851,3820,3078],{},[833,3822,3823,3825,3829],{},[851,3824,3083],{},[851,3826,3827],{"align":2999},[946,3828,3088],{},[851,3830,3091],{},[806,3832,3833,3834,3837,3838,3840,3841,3843],{},"Create returns ",[946,3835,3836],{},"201",". Read, list, patch, start and diff return ",[946,3839,1099],{},". Delete\nreturns ",[946,3842,3222],{},". Start also uses every Run start outcome in the Run Explorer\ntable; this section does not copy or change that closed set.",[968,3845,3847],{"id":3846},"product-capability-surfaces","Product capability surfaces",[806,3849,3850,3851,3854],{},"Phase 7 adds five feature-gated product routes. Their shared ",[1005,3852,3853],{"href":315},"capability contracts"," supply field definitions and limits.",[827,3856,3857,3870],{},[830,3858,3859],{},[833,3860,3861,3864,3867],{},[836,3862,3863],{},"Route",[836,3865,3866],{},"Capability",[836,3868,3869],{},"Result handoff",[846,3871,3872,3887,3902,3917,3932],{},[833,3873,3874,3879,3884],{},[851,3875,3876],{},[946,3877,3878],{},"\u002Fcompanies\u002Fsearch",[851,3880,3881],{},[946,3882,3883],{},"company.search",[851,3885,3886],{},"Select company refs for Enrich or Find People",[833,3888,3889,3894,3899],{},[851,3890,3891],{},[946,3892,3893],{},"\u002Fcompanies\u002Fenrich",[851,3895,3896],{},[946,3897,3898],{},"company.enrich",[851,3900,3901],{},"Read frozen company results and canonical refs",[833,3903,3904,3909,3914],{},[851,3905,3906],{},[946,3907,3908],{},"\u002Fpeople\u002Fsearch",[851,3910,3911],{},[946,3912,3913],{},"people.search",[851,3915,3916],{},"Select person refs for Enrich",[833,3918,3919,3924,3929],{},[851,3920,3921],{},[946,3922,3923],{},"\u002Fpeople\u002Fenrich",[851,3925,3926],{},[946,3927,3928],{},"people.enrich",[851,3930,3931],{},"Read profile results and requested email state",[833,3933,3934,3939,3943],{},[851,3935,3936],{},[946,3937,3938],{},"\u002Fsignals\u002Fsearch",[851,3940,3941],{},[946,3942,3316],{},[851,3944,3945],{},"Open the existing saved-search or prospect review context",[806,3947,3948,3949,949,3952,1574,3955,3958],{},"Companies and People each present Search and Enrich segments. Signals remains the opportunity-discovery entry point.\nKeep ",[946,3950,3951],{},"\u002Fcrm\u002Fcompanies",[946,3953,3954],{},"\u002Fcrm\u002Fpeople",[946,3956,3957],{},"\u002Fprospects"," available. Phase 7 does not rebuild those pages or perform legacy cutover.\nThe shell owns typed API access, server validation mapping, start keys, Run status and selection handoff.\nThe server validates the published JSON Schema. The shell does not add a second schema engine.\nProduct views own their forms, result rows and diagnostics.",[1153,3960,3962],{"id":3961},"capability-api-and-cli","Capability API and CLI",[806,3964,3965,3966,3969],{},"All paths below use the ",[946,3967,3968],{},"\u002Fapi\u002Fv1"," prefix. All three ship on the runtime baseline.",[827,3971,3972,3985],{},[830,3973,3974],{},[833,3975,3976,3979,3982],{},[836,3977,3978],{},"Method and path",[836,3980,3981],{},"CLI command",[836,3983,3984],{},"Contract",[846,3986,3987,4006,4021],{},[833,3988,3989,3994,3999],{},[851,3990,3991],{},[946,3992,3993],{},"GET \u002Fagentic\u002Fcapabilities",[851,3995,3996],{},[946,3997,3998],{},"ac agentic capabilities list",[851,4000,4001,4002,4005],{},"The whole authorized catalogue; no cursor and no limit; ",[946,4003,4004],{},"available_only"," defaults to true",[833,4007,4008,4013,4018],{},[851,4009,4010],{},[946,4011,4012],{},"GET \u002Fagentic\u002Fcapabilities\u002F{capability_id}",[851,4014,4015],{},[946,4016,4017],{},"ac agentic capabilities get",[851,4019,4020],{},"One authorized product contract, including unavailable state",[833,4022,4023,4028,4033],{},[851,4024,4025],{},[946,4026,4027],{},"POST \u002Fagentic\u002Fcapabilities\u002F{capability_id}\u002Fruns",[851,4029,4030],{},[946,4031,4032],{},"ac agentic capabilities start",[851,4034,4035,4036,4038],{},"Versioned input and required ",[946,4037,1079],{},"; delegate to RunManager",[806,4040,4041,4044,4045,4048,4049,4052,4053,4056,4057,2267,4059,4062,4063,4066,4067,4070,4071,4073,4074,4076,4077,4080,4081,4084,4085,4087,4088,4091],{},[815,4042,4043],{},"The list is not paginated, and it is the second route on this surface that is not.","\nThe vocabulary holds five IDs and a deploy fixes them, so a page can never fill. ",[946,4046,4047],{},"GET \u002Fagentic\u002Ftools"," answers a build-time-fixed catalogue the same way, and the shared cursor encodes a ",[946,4050,4051],{},"(timestamptz, uuid)"," pair that a capability ID is not.\nA list therefore returns ",[946,4054,4055],{},"{items}"," in stable capability-ID order, and it takes no ",[946,4058,2405],{},[946,4060,4061],{},"cursor",".\nRead replies omit executor UUIDs, configuration and provider Tool definitions. The public five-ID vocabulary reveals no tenant bindings.\nAn available record carries ",[946,4064,4065],{},"required_scopes",". The registry answers a contract only to a caller that holds every scope in that set, so the field states rights the caller already holds.\nThe server checks ",[946,4068,4069],{},"run.start"," before the binding lookup, and the published required scopes after it.\nThe list returns 200 for every authenticated member, including one that holds no ",[946,4072,4069],{},".\n",[946,4075,4004],{}," filters every unavailable record, ",[946,4078,4079],{},"unauthorized"," included, so that actor reads zero items under the default.\n",[946,4082,4083],{},"available_only=false"," returns the five records it would have hidden, each with its own reason.\nOn the single-ID endpoint an unknown ID returns 404, and the ",[946,4086,4079],{}," reason returns 403.\nThe three remaining reasons return 200 and carry the reason, so an authorized uninstalled, disabled or misbound product stays readable with ",[946,4089,4090],{},"availability: unavailable",".\nThe two routes therefore split one reason and share the other three. A batch read cannot answer one status per member, and 403 is the answer a single read owes a caller that may not start it.\nAn unavailable start returns 409. The registry never silently chooses another executor.",[806,4093,4094,4095,4098,4099,4102,4103,4106,4107,4111,4112,4115,4116,4119,4120,4123,4124,949,4127,949,4130,1574,4133,4136,4137,1009],{},"The start body is ",[946,4096,4097],{},"{contract_version: 1, input: {...}}",".\nA schema error returns 422 with a field path. Oversized input uses the existing 413 response.\nA stale contract version returns 409 ",[946,4100,4101],{},"contract_version_conflict","; a changed request under the same key returns 409 ",[946,4104,4105],{},"idempotency_conflict",".\nThe shared capability start service uses the ",[1005,4108,4110],{"href":4109},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-contracts#capability-start-identity","start identity contract",".\nRunManager records the request digest in the frozen snapshot with its atomic Run insert.\nChat, API and CLI use this service; clients do not implement a separate version or replay rule.\nCaller identity namespaces the start key. A capability-start prefix separates it from generic definition starts.\nUnknown, unavailable and unauthorized starts return 404 ",[946,4113,4114],{},"capability_not_found",", 409 ",[946,4117,4118],{},"capability_unavailable"," and 403 ",[946,4121,4122],{},"capability_unauthorized",", respectively.\nThe CLI requires ",[946,4125,4126],{},"CAPABILITY_ID",[946,4128,4129],{},"--contract-version",[946,4131,4132],{},"--input",[946,4134,4135],{},"--idempotency-key",". It supports ",[946,4138,4139],{},"--json",[806,4141,4142,4143,959,4146,4149],{},"Successful admission and replay return the existing Run detail with HTTP 200 and ",[946,4144,4145],{},"outcome: started",[946,4147,4148],{},"duplicate",".\nCapability identity and contract version are fields on that Run. The normal Run endpoints own status, spans, cancel and streaming.\nPolicy denial can produce a failed Run and approval can produce a waiting Run. Read its status, not just HTTP 200.\nBudget refusal retains the existing 429 and Retry-After behavior.\nGeneric custom-definition starts keep their current endpoint and replay behavior; the stricter digest rule belongs to capability starts.",[1153,4151,4153],{"id":4152},"shared-launch-shell","Shared launch shell",[806,4155,4156,4157,4160],{},"ENG-2284 owns the common client, state and shell. Product tickets supply forms, result rows and canonical entity reads.\nEnable the five routes and their navigation with ",[946,4158,4159],{},"VITE_ENABLE_CAPABILITY_PRODUCTS=true",". The default is off.\nThe same gate controls route registration and navigation. Existing authentication guards still apply.\nThe gate controls rollout. The API checks permission on every request.\nWith the gate off, direct product URLs resolve to the existing not-found page. Legacy routes keep their current behavior.\nThe initial route shell shows product navigation and availability. It has no generic JSON editor or automatic start.",[806,4162,4163,4164,4166,4167,4170,4171,4173],{},"Read the catalogue with ",[946,4165,4083],{}," to retain unavailable reasons. Do not request a cursor or a limit.\nUse ",[946,4168,4169],{},"capability_id",", not the internal registry field ",[946,4172,1520],{},". An unavailable record has only the ID, availability and reason.\nRender loading, failed reads and empty replies as different states. A read failure is not an unavailable capability.\nMap server schema paths and envelope validation paths to field errors. Do not show rejected input values.\nA version conflict requires a fresh contract read and an explicit start with the accepted version.\nA permission, availability or budget refusal retains the draft. A retry never refreshes the key by itself.",[806,4175,4176],{},"Keep one unresolved attempt per capability in same-tab session storage. Scope it to both organization and actor.\nFreeze the capability ID, version, input and key before POST. Reject input that is not JSON or exceeds 32 KiB.\nPersist before POST. If storage fails, refuse the launch so a reload cannot lose the recovery key.\nA double click sends one POST. A timeout, connection fault or 5xx leaves the attempt unresolved.\nAn explicit retry sends the same frozen request and key. A reload restores the attempt but sends no POST.\nDo not replace an unresolved attempt with edited input. Resolve it first so an earlier Run cannot be hidden.\nA definitive refusal of the first request ends that attempt. A later explicit start may use a new key.\nAfter an uncertain response, a later refusal does not prove that the first request failed.\nKeep its key even after a rate-limit or authorization refusal. Middleware can refuse a retry before the replay lookup.\nA response-body failure after HTTP 200 is also uncertain. Retry that attempt with its original key.\nAn admitted Run keeps its ID across reloads. Reopening it reads the Run and never starts another one.",[806,4178,4179],{},"Subscribe before the initial Run read. Stream frames are hints; the durable Run owns the displayed status and output.\nRe-read on a stream gap, reconnect, root terminal event or explicit refresh.\nUse a single bounded polling interval while following a nonterminal Run. This also covers quiet streams and failed connections.\nSerialize refreshes and coalesce repeated hints. An older response cannot replace newer state.\nA child terminal event does not end the root. A root terminal state never returns to a nonterminal state.\nStop streams, timers and reads when the view leaves or the actor changes. Ignore all late responses from the old scope.\nClear visible state and selections on identity changes. Keep recovery records under their original actor and organization until the tab closes.\nReturning to that scope can restore its attempt. Another actor cannot load it through the shell.\nRun Explorer owns the span tree and approval actions. Product adapters own canonical reads and result diagnostics.\nThe shell exposes durable Run output and canonical refs without copying product result tables or inferring email verification.",[1153,4181,4183],{"id":4182},"company-search-journey","Company Search journey",[806,4185,4186,4187,4189],{},"ENG-2290 supplies the form and result table at ",[946,4188,3878],{},".\nReuse the shared shell and actor-scoped selection store. Do not add a second launcher, schema engine or selection store.\nThe form supports the published V1 sources: supplied companies, Explorium, or both.\nStart with supplied companies selected. Never enable a paid source without an explicit user choice.\nAccept one domain or organization LinkedIn URL per line. Canonical and saved-row refs remain typed handoff inputs.\nDo not parse CSV, infer companies from names, or rewrite identity values in the browser.",[806,4191,4192,4193,4196,4197,4200],{},"Read source choices, country codes, employee bands, revenue bands and count limits from the published input schema.\nUse local schema references for field metadata. Missing required metadata or an unsupported contract version disables new starts.\nThe current V1 generator puts a non-standard ",[946,4194,4195],{},"maxLength"," annotation on some array schemas, not on their string items.\nRead standard ",[946,4198,4199],{},"maxItems"," first and this annotation as a compatibility fallback. Standard JSON Schema validators ignore that array annotation.\nThe executor still enforces array bounds.\nUse the published target default, which is 50 in V1. Do not clamp an invalid entered count silently.\nIndustry names use the provider vocabulary. Accept names as text; do not reuse the CRM industry picker or duplicate the provider list.\nThe executor validates provider industry names. A runtime input refusal can therefore produce a failed Run.\nThe shared start endpoint validates JSON Schema, which does not encode every executor cross-field rule.",[806,4202,4203],{},"Omit blank filter lists. Require one supported filter when Explorium is selected.\nRequire 1 to 100 company refs with the supplied source; omit companies for provider-only requests.\nUse OR within a filter and AND across filters. Unknown facts can remain in results; explicit contradictions are excluded.\nKeep the form draft after refusal and availability refresh. Restore the frozen request when reopening a saved attempt.\nClear the visible draft on actor, organization or impersonation changes.\nAn uncertain attempt locks edits until retry resolves it. A new Run requires an explicit start after the current Run ends.",[806,4205,4206,4207,4210,4211,4214,4215,4218],{},"Read the typed envelope from ",[946,4208,4209],{},"RunResult.output.company_search",", not from ",[946,4212,4213],{},"output.items"," or top-level ",[946,4216,4217],{},"output.outcome",".\nCheck the capability ID, contract version and row identities before enabling selection.\nKeep Run status separate from result outcome. A failed or cancelled Run never appears as an empty successful search.\nA partial result can contain zero rows. Missing or invalid output after success is a result error, not an empty search.\nShow the funnel, requested source states, usage, diagnostic reasons and omitted diagnostic count.\nLabel absent public facts as unknown. Never infer enrichment or CRM membership from a canonical Intelligence ID.\nRetain a stable form frame and minimum result area during loading. Allow result-table scrolling within the page on mobile.",[806,4220,4221,4222,4225],{},"Store selected rows as ",[946,4223,4224],{},"{kind: \"search_result\", run_id, result_id}"," refs, in selection order, with at most 100 entries.\nThe frozen row retains its normalized identity and optional canonical ID. Do not put either display data or refs in the URL.\nRestore selection only for rows in the currently displayed successful Run. Duplicate status reads do not change selection.\nA newly admitted search clears the prior company selection. A refused or uncertain start retains the prior selection.\nA failed selection write shows an error and retains the previous saved selection.\nENG-2288 owns the Enrich handoff action. ENG-2292 owns Find People. Neither action starts work in ENG-2290.",[1153,4227,4229],{"id":4228},"company-enrich-journey","Company Enrich journey",[806,4231,4232,4233,4235],{},"ENG-2288 supplies the form and frozen result details at ",[946,4234,3893],{},".\nReuse the shared launcher and company selection store. Add no API, second launcher or provider path.\nSearch offers an explicit Enrich selected action only when valid rows are selected.\nNavigation carries no entity list or display data in the URL and starts no work.",[806,4237,4238],{},"A fresh form prefills the shared selection. A saved Enrich attempt takes precedence over a new Search selection.\nRestore its frozen input without a POST. Lock edits while that attempt is unresolved or its Run is active.\nAfter it ends, offer Use search selection as an explicit replacement. Do not append new refs to the restored request.\nKeep selection changes local to the Enrich draft. Do not change the saved Search selection from this form.\nIf no selection exists, show direct entry and a link back to Search.\nDirect entry accepts domains and canonical company UUIDs in separate text fields, one value per line.\nThe form sends domains first, then canonical refs, in line order. It does not infer identity from company names.\nKeep restored LinkedIn and search-result refs typed. Never reconstruct them from display text.",[806,4240,4241],{},"Read the subject bound, preset choices, field sets and refresh defaults from the published V1 schema.\nUse Basic and stale defaults. Omitted fields mean the whole preset; an explicit subset must not be empty.\nChanging a preset selects its full field set visibly. The user can then choose a smaller subset.\nExplain missing-only refresh: it keeps existing stale values without verification.\nRequire 1 to 100 subjects and a separate explicit start. Preserve duplicate input indexes through server resolution.\nExplain the separate output byte limit. Never split, truncate or retry a batch automatically.",[806,4243,4244,4245,4248],{},"Read ",[946,4246,4247],{},"RunResult.output.company_enrich",". Validate its capability, version, rows and indexes against the frozen Run input.\nKeep Run status, product outcome and field states separate. A retained value does not prove a successful refresh.\nShow every selected value and its reported field state, including retained, not found, pending, failed and cancelled.\nShow the reported cache decision and tier times. A not-read cache is not a cache miss.\nShow bounded diagnostics and lineage omission counts. Do not invent per-field provenance.\nShow frozen estimated or settled usage once. If output is absent, show the available root meter without inventing a settlement state.\nAn unavailable meter is not zero cost. Payload failure can follow completed writes and charges; state that limit.\nKeep the result frame stable and all details reachable on desktop, mobile and keyboard.",[806,4250,4251,4252,4256],{},"This slice displays frozen company details and canonical refs with the existing Run Explorer link.\nIt does not read current canonical facts or link an Intelligence UUID to a CRM company.\nThe current Intelligence drawer requires superadmin access. Do not reuse it for member access or relax its guard.\nLive member-facing canonical details need a separate read contract and are deferred.\nThe ",[1005,4253,4255],{"href":4254},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-scenarios#company-enrich-journey-stress-cases","journey cases EJ01-EJ42"," define validation.",[1153,4258,4260],{"id":4259},"people-search-journey","People Search journey",[806,4262,4263,4264,4266],{},"ENG-2292 supplies the form and frozen results at ",[946,4265,3908],{},", behind the shared capability gate.\nReuse the launch shell, company brief controls and actor-scoped selection store.\nThe form supports supplied people, Explorium, or both. Start with supplied people only.\nProvider discovery requires an explicit source choice. Show its provider-credit cost warning.\nAccept person LinkedIn URLs and canonical person UUIDs in separate fields, one value per line.\nKeep saved search-row refs typed. Do not accept CRM person IDs or infer identities from names.",[806,4268,4269],{},"Offer no company scope for supplied-only requests, known companies, or a structured company brief.\nKnown scope accepts 1 to 20 CompanyRefs. Keep company selections ordered and require explicit replacement of an existing draft.\nFind People on Company Search accepts 1 to 20 selected rows. Larger selections show a limit message; never truncate them.\nA fresh People Search form prefills that selection. A saved People Search request takes precedence.\nAfter the saved attempt ends, Use company selection explicitly replaces the scope. It does not change the saved company selection.\nNavigation and scope replacement never select a paid source or start work.\nA company brief uses the full published Company Search input. It is not a free-text prompt.\nReuse the Company Search fields for supplied companies, Explorium filters and target count.\nThe server resolves the brief, bounds it to 20 companies and reports truncation. Show these diagnostics without repeating discovery.",[806,4271,4272],{},"Read source choices, count bounds, text lengths and country codes from the published V1 schema.\nResolve local schema references and union branches for metadata only. The server remains the schema authority.\nOmit empty optional lists and scope fields. Require at least one persona criterion.\nPersona uses titles, departments, seniority and person country codes. Values within a list use OR; fields use AND.\nDepartments and seniority are text fields because the schema supports supplied-only free text.\nExplorium checks its narrower vocabulary at execution. Explain this limit; do not copy its vocabulary into the frontend.\nUse the published target-per-company default and bounds. Supplied-only requests do not use that target to cap their supplied rows.\nUnsupported metadata disables new starts. A refusal retains the draft; unresolved or active attempts lock edits.\nRestore a frozen request without a POST. Clear visible drafts on actor, organization or impersonation changes.",[806,4274,4244,4275,4278,4279,4282,4283,4285],{},[946,4276,4277],{},"RunResult.output.people_search",". Validate capability, version, identity, bounded rows and per-company diagnostics before selection.\nKeep Run state separate from result outcome. A succeeded partial search can have zero rows.\nMissing, dropped or invalid product output is a result error. Failed and cancelled Runs cannot provide selectable rows.\nA valid partial envelope with ",[946,4280,4281],{},"output_truncated"," retains selectable identities after optional facts are trimmed.\nThe global ",[946,4284,3481],{}," flag alone does not reject an intact envelope; resource refs can also set that flag.\nShow name, title, employer, person country, LinkedIn identity, evidence sources and optional canonical ID.\nAbsent facts remain unknown. Unknown persona fit is not verified fit. A canonical ID does not prove CRM membership.\nShow the people funnel, per-source states, per-company counts and reasons, usage and omitted diagnostic count.\nDo not turn evidence URLs into links without an HTTP or HTTPS check. Render external text as text.\nKeep the form and result frame stable. Contain horizontal table scrolling on mobile and retain keyboard selection focus.",[806,4287,4288,4289,4291,4292,4256],{},"Save up to 100 ordered ",[946,4290,4224],{}," person refs in the existing store.\nRestore only refs present in the displayed successful Run. Repeated reads preserve selection order.\nA newly admitted People Search clears prior person selection. Refused or uncertain starts retain it.\nA storage failure keeps the prior saved selection and shows an error.\nThe People Enrich segment preserves these refs without starting work. ENG-2289 owns its form and Enrich selected action.\nThe ",[1005,4293,4295],{"href":4294},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-scenarios#people-search-journey-stress-cases","journey cases PJ01-PJ40",[1153,4297,4299],{"id":4298},"people-enrich-journey","People Enrich journey",[806,4301,4302,4303,4305],{},"ENG-2289 supplies the form, profile preview and frozen results at ",[946,4304,3923],{},".\nReuse the shared launcher, person selection store and Company Enrich form patterns.\nAdd no endpoint, schema engine, provider poller or second selection store.\nPeople Search offers Enrich selected only when valid rows are selected.\nNavigation starts no work and carries no person data in the URL.",[806,4307,4308],{},"A fresh form uses the shared Search selection. A saved People Enrich attempt takes precedence.\nRestore its frozen input without a POST. Lock edits while the attempt is unresolved or the root Run is active.\nAfter the Run ends, let the user replace the draft with the saved Search selection.\nDo not change the Search selection from the Enrich form.\nDirect entry accepts person LinkedIn URLs, canonical person UUIDs and CRM person UUIDs in separate fields.\nThe form sends LinkedIn refs, then canonical refs, then CRM refs. It preserves line order within each field.\nKeep restored and selected refs typed. Do not copy CRM facts, infer names or rebuild search refs from display data.",[806,4310,4311,4312,4314,4315,959,4318,4321],{},"Read subject bounds, presets, fields and refresh defaults from the published V1 schema.\nUse Basic and stale as defaults. Omitted fields mean the complete preset.\nChanging the preset selects its complete field set. The user can then select a nonempty subset.\nRequire ",[946,4313,336],{}," when ",[946,4316,4317],{},"email_source",[946,4319,4320],{},"email_score"," is selected.\nExplain that Standard can start asynchronous work-email discovery.\nRequire 1 to 100 subjects and a separate explicit start.\nDo not split, truncate or retry a batch automatically.",[806,4323,4324,4325,4328],{},"Read the root Run as the lifecycle truth. Stream frames only request another durable read.\nWhile the root is live, read its one native email child through the Run API.\nThe child must be the root's only direct workflow child and must carry only ",[946,4326,4327],{},"input.profile",".\nValidate that profile envelope against the root's frozen request before display.\nShow it as a profile preview, not as the final result.\nDo not read span payloads, poll provider jobs or call the superadmin Intelligence API.",[806,4330,4331,4332,4335,4336,4338],{},"Read the root's final ",[946,4333,4334],{},"RunResult.output.people_enrich"," when it is valid.\nRequire a ",[946,4337,3928],{}," V1 root and frozen input, plus the same identity in the output.\nReject truncated output. Validate all row refs, values, states, diagnostics, counts and bounds against V1.\nEach row must contain every selected field state and no unselected value or state.\nCurrent, retained and refreshed states require a value.\nInput indexes must be nonempty per row and cover every input subject exactly once, without gaps or duplicates.\nUse only these checked indexes to resolve CRM links.\nIt replaces the profile preview and remains frozen after later Intelligence changes.\nReject a final result that still marks a selected field as pending.\nIf the root ends without final output, keep a valid preview and derive unfinished email states from the root status.\nMap pending email to cancelled for a cancelled root and failed for any other terminal root.\nThis display rule does not change the stored snapshot. A reload can read the same saved child.\nDo not claim that a final result exists. If no valid preview exists, show the terminal root state only.\nKeep root status, product outcome, profile state, email operation state and verification status separate.",[806,4340,4341,4342,4345],{},"Show every selected field and its reported state.\nPending, failed and cancelled describe the current operation even when a retained value exists.\nShow email verification only for the exact email named by the metadata.\nUse ",[946,4343,4344],{},"unknown"," when evidence is absent. Reject malformed or mismatched evidence in a final result or preview.\nNever infer verification from a score or Run success.\nShow the reported cache decision, tier times, diagnostics, lineage omissions and frozen usage.\nDo not add root and child usage totals because both describe one Run tree.",[806,4347,4348,4349,4352,4353,4256],{},"Link an explicit ",[946,4350,4351],{},"crm_person"," input ref to its CRM detail route through the result's input indexes.\nShow canonical UUIDs as references only. A canonical UUID does not prove CRM membership.\nAllow safe HTTP or HTTPS profile links. Render all other external values as text.\nThe existing Run Explorer link remains available for every admitted Run.\nKeep the result frame stable and all controls reachable on desktop, mobile and keyboard.\nThe ",[1005,4354,4356],{"href":4355},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-scenarios#people-enrich-journey-stress-cases","journey cases PEJ01-PEJ40",[1153,4358,4360],{"id":4359},"signals-search-journey","Signals Search journey",[806,4362,4363,4364,4366],{},"ENG-2294 supplies the direct launch and saved-search workspace at\n",[946,4365,3938],{},". Reuse the shared launch shell, Prospect Review and saved\nsearch API. Add no generic JSON editor, second Run client, provider client or\nprospect table.",[806,4368,4369],{},"Direct launch has two source modes: discovery and company set. Saved monitoring\nuses the saved-search start route and is not a third editable direct mode.\nThe discovery mode sends no company refs. The company-set mode accepts 1 to 10\ndomains, organization LinkedIn URLs, canonical company UUIDs or prospect UUIDs\nin separate fields. Preserve type and order. Do not accept a company name as an\nidentity. Switching modes omits every hidden source field.",[806,4371,4372,4373,4375,4376,949,4378,949,4380,4382,4383,4385],{},"Both direct modes use one brief editor. Label ",[946,4374,61],{}," as the target-company and\nsignal thesis because the current workflow reads both purposes from this text.\nThe editor also supports up to ten company criteria. Each criterion contains\n1 to 200 characters and a required flag. Require an ICP or one criterion.\nThe persona editor uses ",[946,4377,3153],{},[946,4379,3156],{},[946,4381,3159],{}," and\n",[946,4384,3162],{},". Require at least one value. Each supplied list contains 1 to\n20 unique values and uses the shared field limits. Use the existing country\noptions. Preserve every rejected draft. Do not add separate signal-theme or\nresult-limit fields until the Run contract supports them.",[806,4387,4388],{},"Keep the unsaved draft unchanged while validation or a request is pending.\nBuild a normalized request copy by trimming the supported brief and persona\nstrings before applying bounds. After a saved create or brief patch succeeds,\nreplace the draft with the normalized detail returned by the server. Direct\nstart and saved-search storage therefore apply the same character boundaries.",[806,4390,4391],{},"Read the supported version and schema from the capability record. The product\nadapter may render only the documented V1 fields. A missing field, different\nversion or incompatible constraint disables start. The server remains the\nvalidation authority. Use its bounded field paths in the form.",[806,4393,4394,4395,4398],{},"After a direct Run is admitted, open ",[946,4396,4397],{},"\u002Fprospects?run={run_id}",". Do this only for\nthe current explicit start response. A restored, replayed or already admitted\nattempt stays in the shell until the user opens Prospect Review. A refusal or\nuncertain response stays on the form. Prospect Review owns live status, empty,\npartial, failed and cancelled outcomes. Signals does not interpret the final\nSmart Feed output as a second result table.",[806,4400,4401,4402,4404],{},"The saved-search workspace lists summaries and reads one selected detail. Put\nonly the saved-search ID in the query string. Create and edit use the same brief\neditor. Preserve unknown stored brief fields when the user changes documented\nfields. A name-only edit omits ",[946,4403,3131],{},", so an incompatible legacy persona can\nstill be renamed. A brief edit must correct the complete persona. Never drop or\nguess a legacy criterion.",[806,4406,4407,4408,4410],{},"Patch sends the exact ",[946,4409,1889],{}," token. A stale update keeps the draft and\noffers an explicit reload. Delete requires confirmation and never cancels an\nadmitted Run. A saved-search start freezes its ID, capability version and key\nbefore POST. It uses the same uncertain-response rules as the shared shell.\nStay in the saved-search workspace after admission and show the durable Run\nstatus with an explicit Prospect Review link.",[806,4412,4413,4414,4416,4417,4420],{},"Show the latest diff separately from the saved-search detail. Distinguish no\npublished Run from a published Run with no material change. Bind every diff\npage to its returned Run ID. On ",[946,4415,1103],{},", discard the old page cursor and offer an\nexplicit reload. A diff item opens ",[946,4418,4419],{},"\u002Fprospects\u002F{prospect_id}?run={run_id}",".\nKeep watch, dismiss, promotion and the existing prospect drawer unchanged.",[806,4422,4423,4424,4428],{},"Clear drafts, selected saved-search state, diff pages and visible Runs when the\nactor or organization changes. Ignore all late responses from the old scope.\nKeep controls reachable by keyboard and on a narrow viewport. The\n",[1005,4425,4427],{"href":4426},"\u002Fengineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fcapability-scenarios#signals-search-journey-stress-cases","journey cases SJ01-SJ48","\ndefine validation.",[1153,4430,4432],{"id":4431},"selection-and-progress","Selection and progress",[806,4434,4435],{},"Carry an ordered set of at most 100 stable refs between Search and Enrich. Never put provider payloads, private fields or entity lists in query strings.\nUse tenant-scoped session state for same-tab handoff. Retain source Run\u002Fresult IDs so a reload can resolve the selected rows.\nIf the state is absent or a ref is stale, ask for selection again. Do not launch an empty or guessed request.\nClear selection state on organization change and ignore late responses for the previous organization.",[806,4437,4438],{},"The Enrich form shows the selection and preset before a separate explicit start.\nFind People carries the selected company refs into the known-company scope form; it never starts automatically.\nPreserve a start key while a request is unresolved. A retry or stream reconnect must not issue a new start key.\nAfter a user edits the input or accepts a new contract version, an explicit new start uses a new key.",[806,4440,4441,4442,4445],{},"Render running, empty, partial, failed and cancelled states from durable Run and result data.\nUse an existing detail view only when it accepts that ref and the caller can access it.\nCompany Enrich shows frozen details in this slice; live canonical details are deferred.\nSearch success does not mean enrichment or CRM promotion occurred.\nThe email view distinguishes profile ready, email pending, not found, verification unknown, unverified, verified, failed and cancelled.\nRender the result's ",[946,4443,4444],{},"email_verification"," metadata; never infer verification from a confidence score or a successful Run.\nKeep the root live while its email child waits. Read the profile preview from the child's immutable input.\nOn reconnect, refetch durable root and child Run data. Duplicate reads must not add duplicate rows.\nKeep the final frozen Run output after canonical Intelligence changes.",[806,4447,4448,4449,4452],{},"Backend exits prove API\u002FCLI parity. Front Door exits prove selection and refusal.\nBrowser exits prove desktop\u002Fmobile layout, selection continuity, failure recovery and async state.\nThe ",[1005,4450,4451],{"href":324},"scenario matrix"," names each owner.",[968,4454,895],{"id":4455},"agent-builder",[806,4457,4458,4459,1009],{},"Agents, Workflows and Skills share one lifecycle, one service, one repository and one validator. They therefore share ",[815,4460,4461],{},"one API resource",[810,4463,4464],{},[806,4465,4466],{},[815,4467,4468],{},"draft -> validate -> current published configuration",[806,4470,4471],{},"There is no definition revision or history subsystem in V1.",[1158,4473,4476],{"className":4474,"code":4475,"language":1163,"meta":1164},[1161],"GET    \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions?kind=&origin=&state=&cursor=\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\nDELETE \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}                a draft only\nPATCH  \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Fdraft          expected_updated_at\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Fvalidate\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Fpublish        expected_updated_at\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Fdisable\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Fenable\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fdefinitions\u002F{id}\u002Ffork\n\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Ftools                           no cursor\n",[946,4477,4475],{"__ignoreMap":1164},[806,4479,4480,4489],{},[815,4481,4482,4484,4485,4488],{},[946,4483,1068],{}," answers the picker an author selects ",[946,4486,4487],{},"tool_ids"," from."," It is author facing. The full catalogue never reaches a model. A row carries the dotted platform name, and the tool adapter derives the model facing name from it.",[806,4491,4492,4493,949,4495,949,4498,949,4501,1574,4504,4507,4508,4511,4512,1574,4515,4518],{},"A row carries five fields: ",[946,4494,3128],{},[946,4496,4497],{},"description",[946,4499,4500],{},"input_schema",[946,4502,4503],{},"output_schema",[946,4505,4506],{},"side_effects",". It drops ",[946,4509,4510],{},"binding",", which names the handler key. It drops ",[946,4513,4514],{},"timeout_s",[946,4516,4517],{},"item_kind",", which the invoker reads and an author does not.",[806,4520,4521,4531],{},[815,4522,4523,4526,4527,4530],{},[946,4524,4525],{},"build_platform()"," is the one builder, and ",[946,4528,4529],{},"AgenticPlatform.registry"," publishes what it built."," Each process holds its own object, because the two run as separate dynos. They therefore answer one catalogue while they run one release. A rolling deploy that changes a declaration is the window where they disagree.",[806,4533,4534,4539],{},[815,4535,4536,4538],{},[946,4537,1068],{}," is not paginated."," The registry is a process-local set that only a deploy changes, and a picker filters the whole of it. The rows are ordered by name, because the registry is keyed by name and holds no order of its own.",[806,4541,4542,4551,4552,4554,4555,4557,4558,4561],{},[815,4543,4544,1574,4547,4550],{},[946,4545,4546],{},"disable",[946,4548,4549],{},"enable"," are a pair."," The table below tells an admin to use ",[946,4553,4546],{}," to prevent new Runs, which reads as reversible, so the route that reverses it must exist. ",[946,4556,4549],{}," revalidates before it returns the row to ",[946,4559,4560],{},"active",", because a definition it references may have been disabled while it was off.",[806,4563,4564,4570,4571,4574],{},[815,4565,4566,4569],{},[946,4567,4568],{},"DELETE"," accepts a draft only."," A published definition is disabled, never deleted: ",[946,4572,4573],{},"runs_definition_fk"," restricts, and a Run's audit trail must not be deletable from under it. Without this route an abandoned draft has nothing that clears it, and conversational authoring creates one per attempt.",[827,4576,4577,4588],{},[830,4578,4579],{},[833,4580,4581,4583,4585],{},[836,4582,1667],{},[836,4584,3000],{},[836,4586,4587],{},"Means",[846,4589,4590,4606,4620,4634,4648,4662,4676,4693],{},[833,4591,4592,4597,4601],{},[851,4593,4594],{},[946,4595,4596],{},"stale",[851,4598,4599],{},[946,4600,1103],{},[851,4602,4603,4605],{},[946,4604,3135],{}," did not match. Reload and retry",[833,4607,4608,4613,4617],{},[851,4609,4610],{},[946,4611,4612],{},"invalid",[851,4614,4615],{},[946,4616,3075],{},[851,4618,4619],{},"validation failed. The body names every error",[833,4621,4622,4627,4631],{},[851,4623,4624],{},[946,4625,4626],{},"definition_in_use",[851,4628,4629],{},[946,4630,1103],{},[851,4632,4633],{},"disable refused; an active definition references this one",[833,4635,4636,4641,4645],{},[851,4637,4638],{},[946,4639,4640],{},"referrer_limit",[851,4642,4643],{},[946,4644,1103],{},[851,4646,4647],{},"publish refused; it would exceed the direct referrer cap",[833,4649,4650,4655,4659],{},[851,4651,4652],{},[946,4653,4654],{},"not_a_draft",[851,4656,4657],{},[946,4658,1103],{},[851,4660,4661],{},"delete refused; disable it instead",[833,4663,4664,4669,4673],{},[851,4665,4666],{},[946,4667,4668],{},"not_published",[851,4670,4671],{},[946,4672,1103],{},[851,4674,4675],{},"disable or enable refused; the definition has never published",[833,4677,4678,4683,4687],{},[851,4679,4680],{},[946,4681,4682],{},"forbidden",[851,4684,4685],{},[946,4686,1032],{},[851,4688,4689,4690],{},"the actor lacks ",[946,4691,4692],{},"definition.publish",[833,4694,4695,4700,4704],{},[851,4696,4697],{},[946,4698,4699],{},"not_found",[851,4701,4702],{},[946,4703,1028],{},[851,4705,4706],{},"no such definition for this organization",[806,4708,4709,4712,4713,949,4715,959,4717,2224,4720,4712,4722,959,4725,1009],{},[946,4710,4711],{},"kind"," is ",[946,4714,1036],{},[946,4716,34],{},[946,4718,4719],{},"skill",[946,4721,600],{},[946,4723,4724],{},"platform",[946,4726,4727],{},"custom",[1153,4729,1931],{"id":4730},"rules-1",[1933,4732,4733,4736,4739,4746,4749,4760,4767,4770,4775,4784,4790,4805,4820,4842,4851,4862,4882],{},[1936,4734,4735],{},"Organization admins author custom definitions.",[1936,4737,4738],{},"The list returns platform templates too, because a fork starts from one.",[1936,4740,4741,4742,4745],{},"A platform template is read-only. ",[946,4743,4744],{},"fork"," copies it into the organization as a draft.",[1936,4747,4748],{},"A draft may be incomplete. Publish runs full deterministic validation.",[1936,4750,4751,1574,4754,4756,4757,4759],{},[946,4752,4753],{},"PATCH ...\u002Fdraft",[946,4755,1131],{}," both carry ",[946,4758,3135],{},". A stale writer reloads. It never overwrites another admin.",[1936,4761,4762,4763,4766],{},"A draft patch ",[815,4764,4765],{},"replaces"," each named field. It never merges into one, because a merge cannot remove a tool.",[1936,4768,4769],{},"A new Run uses the current published configuration. An existing Run keeps its frozen snapshot.",[1936,4771,4772,4773,1009],{},"A definition cannot be disabled while an active definition references it. The API returns ",[946,4774,4626],{},[1936,4776,4777,1574,4779,4781,4782,2892],{},[946,4778,4546],{},[946,4780,4549],{}," are idempotent. A second press answers ",[946,4783,1099],{},[1936,4785,4786,4787,4789],{},"A custom definition references definitions of its own organization only. ",[946,4788,4744],{}," a platform template before referencing it.",[1936,4791,4792,4794,4795,4798,4799,4802,4803,1009],{},[946,4793,3135],{}," is an ",[815,4796,4797],{},"opaque string",". A client echoes it back unparsed. A JavaScript ",[946,4800,4801],{},"Date"," round trip truncates it to milliseconds and every write then answers ",[946,4804,4596],{},[1936,4806,4807,4810,4811,4813,4814,4816,4817,4819],{},[815,4808,4809],{},"It travels in the request body",", on both routes that carry it. ",[946,4812,4753],{}," already has a body, and ",[946,4815,1131],{}," takes a body of that one field. A header would need a name of its own, a second ",[946,4818,1083],{},", and a second place a client looks for it.",[1936,4821,4822,4830,4831,4834,4835,4837,4838,4841],{},[815,4823,4824,4825,4827,4828,1009],{},"An empty ",[946,4826,4753],{}," body is ",[946,4829,1083],{}," A ",[946,4832,4833],{},"BEFORE UPDATE"," trigger moves ",[946,4836,1889],{}," on a write that changes no value, so a patch of ",[946,4839,4840],{},"{}"," would take every other admin's token and change nothing.",[1936,4843,4844,4850],{},[815,4845,4846,1923,4848,1009],{},[946,4847,4568],{},[946,4849,3222],{}," The row is gone, so there is nothing to read back.",[1936,4852,4853,4861],{},[815,4854,4855,4857,4858,4860],{},[946,4856,4744],{}," carries no ",[946,4859,1079],{},", and two calls mint two copies."," The shared rule asks for a key on a route that starts a Run, and this route writes drafts. A draft cannot run and it deletes cleanly, so a repeated fork leaves rows an admin removes rather than work that ran twice.",[1936,4863,4864,4867,4868,4870,4871,4874,4875,4878,4879,4881],{},[815,4865,4866],{},"The list is a union, and the two halves filter differently."," It returns the caller's own definitions in every state, plus the platform templates that are ",[946,4869,4560],{},". RLS does not run on this path, so the read applies ",[946,4872,4873],{},"state = 'active'"," to the platform half itself. It pages on ",[946,4876,4877],{},"(created_at, id)",", so it is newest first and not alphabetical: ",[946,4880,3128],{}," is not unique, and a cursor on it needs an encoder of its own.",[1936,4883,4884,4892,4893,4895,4896,4898],{},[815,4885,4886,1923,4889,4891],{},[946,4887,4888],{},"GET ...\u002Fdefinitions\u002F{id}",[946,4890,1028],{}," for a platform template."," A template is visible through the list and through ",[946,4894,4744],{},", and the list row carries the id, the kind, the name, the origin, the state and ",[946,4897,1889],{},". An admin who wants the configuration forks it, and a fork deletes.",[1153,4900,4902],{"id":4901},"disable-and-in-flight-work","Disable and in-flight work",[806,4904,4905],{},"Disable is not a stop button. Be precise about this in the UI.",[827,4907,4908,4918],{},[830,4909,4910],{},[833,4911,4912,4915],{},[836,4913,4914],{},"To do this",[836,4916,4917],{},"Use this",[846,4919,4920,4933,4943],{},[833,4921,4922,4925],{},[851,4923,4924],{},"Prevent new Runs",[851,4926,4927,4929,4930,4932],{},[946,4928,4546],{}," the definition. ",[946,4931,4549],{}," reverses it",[833,4934,4935,4938],{},[851,4936,4937],{},"Stop a Run that is already executing",[851,4939,4940],{},[946,4941,4942],{},"POST \u002Fruns\u002F{id}\u002Fcancel",[833,4944,4945,4948],{},[851,4946,4947],{},"Stop one action everywhere, immediately",[851,4949,4950],{},"The Tool kill switch, which is read live",[806,4952,4953,4955,4956,4959],{},[946,4954,4546],{}," refuses a ",[815,4957,4958],{},"new Run tree",". A workflow already running still starts its child Runs, including one that begins after a three day wait. That is deliberate, and it is why the emergency lever is the tool kill switch and not this. An admin who must stop one Run cancels it.",[968,4961,905],{"id":4962},"run-explorer",[1158,4964,4967],{"className":4965,"code":4966,"language":1163,"meta":1164},[1161],"POST   \u002Fapi\u002Fv1\u002Fagentic\u002Fruns                            Idempotency-Key\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fruns?root_only=true&parent_run_id=&definition_id=&status=&cursor=\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\u002F{id}\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\u002F{id}\u002Fspans?since=&cursor=\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\u002F{id}\u002Fcancel\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\u002F{id}\u002Fstream\n",[946,4968,4966],{"__ignoreMap":1164},[806,4970,4971,4974,4975,4978,4979,4982],{},[946,4972,4973],{},"POST \u002Fapi\u002Fv1\u002Fagentic\u002Fruns"," starts a custom Agent or Workflow by ",[946,4976,4977],{},"definition_id",".\nThe capability endpoint resolves a stable product ID before building the same ",[946,4980,4981],{},"StartRunCommand",".\nBoth endpoints, the Front Door and Triggers share one RunManager boundary.",[806,4984,4985,1550,4992,4995,4996,4999],{},[815,4986,4987,4988,4991],{},"The body carries no ",[946,4989,4990],{},"parent_run_id",", and the route refuses one.",[946,4993,4994],{},"start()"," sends the dispatch event only for a top level Run, so a child accepted here would be created and never claimed, and the reaper would fail it two minutes later. A child Run is invoked by its parent node and reaches no API. ",[946,4997,4998],{},"parent_cancelled"," is therefore unreachable from this route.",[806,5001,5002],{},"Run detail returns the product status, what it waits on, the result or error, the pending approval, and the summarized usage. Usage comes from the canonical meter. V1 needs no separate per-Run usage endpoint.",[806,5004,5005,5008,5009,5012,5013,5016],{},[815,5006,5007],{},"Run detail embeds no child list."," A workflow parent has as many children as its widest node, and this page's own case is five hundred. The children are a page of the list route, ",[946,5010,5011],{},"GET \u002Fruns?parent_run_id={id}&cursor=",", so the pagination rule holds and no route is added. Detail carries ",[946,5014,5015],{},"child_count"," only.",[806,5018,5019,1550,5022,5025,5026,5029,5030,5033,5034,5037,5038,1009],{},[815,5020,5021],{},"Usage is read from one indexed column, never from a list of span references.",[946,5023,5024],{},"agent.spans.usage_id"," points at ",[946,5027,5028],{},"ai_usage_log"," one row at a time, so summing a tree through it sends every span's ",[946,5031,5032],{},"usage_id"," back as a filter. ",[946,5035,5036],{},"UsageMeter"," therefore stamps the root Run on each usage row, and the summary is one aggregate over that column. See ",[1005,5039,5040],{"href":277},"observability and operations",[806,5042,5043,5049,5050,5053,5054,5057,5058,5060],{},[815,5044,5045,5048],{},[946,5046,5047],{},"RunDetail.usage"," is the total of the whole tree, and a child reports the same total as its parent."," The usage row carries ",[946,5051,5052],{},"agent_root_run_id",", so the meter can answer a tree and it cannot answer one Run inside one. Detail therefore reads ",[946,5055,5056],{},"run_usage(root_run_id)"," for every Run. The alternative is a second stamped column on ",[946,5059,5028],{},", which V1 does not need: a person opens a child Run to read what it did, and the cost of the work belongs to the tree that asked for it.",[806,5062,5063,5064,5066],{},"Cancel is idempotent. Cancelling a finished Run is still a success. It answers ",[946,5065,1099],{}," with the Run detail, so the caller reads the status it produced.",[806,5068,5069,5072],{},[946,5070,5071],{},"definition_name"," on a Run is the definition's current name, not the name frozen in the snapshot. A rename therefore moves every Run of that definition, which is what a person reading the list expects. The snapshot keeps the frozen name for an audit.",[1153,5074,2988],{"id":5075},"every-outcome-has-one-status-2",[806,5077,5078,5081],{},[946,5079,5080],{},"RunManager.start()"," answers a closed set, and the route maps it. Nothing else on this route returns a status of its own.",[827,5083,5084,5098],{},[830,5085,5086],{},[833,5087,5088,5093,5095],{},[836,5089,5090],{},[946,5091,5092],{},"StartRunResult.outcome",[836,5094,3000],{},[836,5096,5097],{},"Body",[846,5099,5100,5116,5132,5146,5160,5174,5192,5209],{},[833,5101,5102,5107,5111],{},[851,5103,5104],{},[946,5105,5106],{},"started",[851,5108,5109],{},[946,5110,1099],{},[851,5112,5113,5114],{},"Run detail, ",[946,5115,4145],{},[833,5117,5118,5122,5126],{},[851,5119,5120],{},[946,5121,4148],{},[851,5123,5124],{},[946,5125,1099],{},[851,5127,5128,5129],{},"the Run the first call created, ",[946,5130,5131],{},"outcome: duplicate",[833,5133,5134,5139,5143],{},[851,5135,5136],{},[946,5137,5138],{},"definition_not_found",[851,5140,5141],{},[946,5142,1028],{},[851,5144,5145],{},"missing, or another organization's",[833,5147,5148,5153,5157],{},[851,5149,5150],{},[946,5151,5152],{},"definition_not_published",[851,5154,5155],{},[946,5156,1103],{},[851,5158,5159],{},"your own draft or disabled definition, with its state; or a skill, which never runs",[833,5161,5162,5167,5171],{},[851,5163,5164],{},[946,5165,5166],{},"snapshot_too_large",[851,5168,5169],{},[946,5170,1103],{},[851,5172,5173],{},"the definition is published and cannot be frozen; republish it",[833,5175,5176,5181,5185],{},[851,5177,5178],{},[946,5179,5180],{},"input_too_large",[851,5182,5183],{},[946,5184,3806],{},[851,5186,5187,5188,5191],{},"the body is over 32 KB; pass a ",[946,5189,5190],{},"ResourceRef"," instead",[833,5193,5194,5199,5204],{},[851,5195,5196],{},[946,5197,5198],{},"organization_budget_exhausted",[851,5200,5201],{},[946,5202,5203],{},"429",[851,5205,5206,5208],{},[946,5207,3796],{}," in seconds",[833,5210,5211,5215,5218],{},[851,5212,5213],{},[946,5214,4998],{},[851,5216,5217],{},"unreachable",[851,5219,5220,5221],{},"the route refuses ",[946,5222,4990],{},[806,5224,5225,5232,5233,5235],{},[815,5226,5227,1574,5229,5231],{},[946,5228,1028],{},[946,5230,1103],{}," are two answers on purpose."," Another organization's definition must not be distinguishable from one that does not exist. Your own draft is the opposite case: the Builder shows it on the next tab, so ",[946,5234,1028],{}," reads as data loss.",[806,5237,5238,5241,5242,5244,5245,5248,5249,5251],{},[815,5239,5240],{},"A start and a duplicate share one status, and the body separates them."," Two concurrent POSTs on one key are a race: one inserts and one reads. If the two answered different codes, an identical pair of calls would return different codes on different days, and a client that branched on the code would be nondeterministic. So both answer ",[946,5243,1099],{}," and carry ",[946,5246,5247],{},"outcome",", which is the same closed value ",[946,5250,1791],{}," returned.",[806,5253,5254,5260,5261,5263,5264,949,5267,5270,5271,5273,5274,5276],{},[815,5255,5256,5259],{},[946,5257,5258],{},"200 started"," does not mean the Run will execute."," Policy admission runs after the Run row exists, so a ",[946,5262,5106],{}," Run can be ",[946,5265,5266],{},"queued",[946,5268,5269],{},"waiting"," on an admission approval, or already ",[946,5272,1795],{}," on a policy denial. The client reads ",[946,5275,1138],{},", never the HTTP code.",[806,5278,5279,5284],{},[815,5280,5281,5282,1009],{},"On the generic definition-start endpoint, a repeated key with different content is still ",[946,5283,4148],{}," The start key carries no request hash, because the Run row is the claim. The route namespaces the key per caller, so one person cannot read another person's Run out of a shared key, and a caller that reuses its own key gets its own first Run back.",[806,5286,5287],{},"Steering is deferred to V2, on the API and in chat. A person who wants to correct a live Run cancels it, then starts it again.",[1153,5289,5291],{"id":5290},"the-run-tree","The Run tree",[806,5293,5294,5295,5297,5298,5301],{},"A Workflow creates a child Run for an ",[946,5296,1036],{}," step and for a ",[946,5299,5300],{},"subworkflow"," step.",[1933,5303,5304,5315,5336,5345,5359,5388],{},[1936,5305,5306,5307,5310,5311,5314],{},"The list returns root Runs by default. ",[946,5308,5309],{},"root_only=false"," returns the children too, and ",[946,5312,5313],{},"parent_run_id="," returns the children of one Run.",[1936,5316,5317,1574,5320,5322,5323,5325,5326,5329,5330,5332,5333,5335],{},[946,5318,5319],{},"root_only=true",[946,5321,5313],{}," contradict each other, and the route answers ",[946,5324,1083],{}," rather than choosing one. Silently dropping either filter returns a page that answers a question the caller did not ask. ",[946,5327,5328],{},"root_only"," is unset by default rather than true, so ",[946,5331,5313],{}," alone reads the children: a caller that follows ",[946,5334,5015],{}," from a detail does not have to unset a default it never set.",[1936,5337,5338,5339,1029,5341,5344],{},"The spans route reads ",[946,5340,1724],{},[946,5342,5343],{},"root_run_id",". Both are indexed, so the wrong one silently returns a whole tree instead of one Run. The UI loads a child tree when the user opens it.",[1936,5346,5347,5348,5351,5352,5355,5356,5358],{},"Spans are paginated. The cursor is ",[946,5349,5350],{},"(started_at, span_id)",", because ",[946,5353,5354],{},"started_at"," is not unique and a bare timestamp cursor drops a row on a page boundary. The run list cursor is ",[946,5357,4877],{}," for the same reason.",[1936,5360,5361,5364,5365,5368,5369,5372,5373,5375,5376,5378,5379,5381,5382,5384,5385,5387],{},[946,5362,5363],{},"since="," changes the order of the spans page to ",[946,5366,5367],{},"(updated_at, span_id)",", which is the index the reconnect reads. One route therefore holds two orders, so ",[815,5370,5371],{},"the cursor names the order it was written for"," and a mismatched pair answers ",[946,5374,1083],{},". Untagged, a cursor from a ",[946,5377,5363],{}," page replayed without ",[946,5380,5363],{}," pages ",[946,5383,5354],{}," from an ",[946,5386,1889],{}," value, reads the wrong rows, and reports no error.",[1936,5389,5390,5391,5394,5395,949,5398,1574,5401,5404,5405,5407,5408,5411],{},"A span payload is not on this route. ",[946,5392,5393],{},"agent.spans"," withholds ",[946,5396,5397],{},"input",[946,5399,5400],{},"output",[946,5402,5403],{},"error"," from ",[946,5406,2857],{},", because they hold tool arguments and results. ",[946,5409,5410],{},"SpanNode.error"," therefore carries the error code and a redacted message, never the raw payload.",[1153,5413,5415],{"id":5414},"live-progress","Live progress",[806,5417,5418,5419,5422],{},"One subscription is enough for a whole tree. Each event goes to the channel of the Run that produced it, and the publisher mirrors it to the ",[815,5420,5421],{},"root"," Run channel. A workflow therefore shows the work its children do.",[806,5424,1182,5425,5435,5436,5438,5439,5441],{},[815,5426,5427,5430,5431,5434],{},[946,5428,5429],{},"GET \u002Fruns\u002F{id}\u002Fstream"," reads the channel of ",[946,5432,5433],{},"{id}",", and it resolves no root."," A reader that rewrote the id to the root would make every child channel unreachable, and the 20 child rule that moves a wide tree's detail onto the child channels would have no caller. Authority is still one ",[946,5437,1028],{}," check on ",[946,5440,5433],{}," before the subscribe.",[806,5443,5444,5445,5448,5449,5452,5453,5456,5457,5459,5460,5463],{},"The stream carries a hint and never a record, so three rules bound the connection. It writes ",[946,5446,5447],{},"retry:"," and never ",[946,5450,5451],{},"id:",": Redis Pub\u002FSub keeps no backlog, so a ",[946,5454,5455],{},"Last-Event-ID"," a browser sent back would promise a replay that cannot happen. It closes on a terminal event for ",[946,5458,5433],{},", and a stream opened on a Run that already ended answers the terminal frame that Run would have published, off the row. An empty body is not a close: an ",[946,5461,5462],{},"EventSource"," re-opens on a clean close exactly as it does on a fault, so a stream that yielded nothing would be re-opened on the client's backoff for ever, at one authority read per attempt per viewer. The same frame answers a terminal event that was lost, which the status poll finds.",[806,5465,1182,5466,5469,5470,5473,5474,5477,5478,5481,5482,5484,5485,5488],{},[815,5467,5468],{},"A Run reaches a terminal status before its spans do, and the reader owns that gap."," Three writers end a Run and close no span: ",[946,5471,5472],{},"RunManager.cancel",", the retries exhausted handler, and the reaper before its own ",[946,5475,5476],{},"close_orphans",". The reaper publishes after that close; the other two do not, so the sweep ends their spans up to a minute later. The reaper leaves a window of its own: ",[946,5479,5480],{},"_reap_page"," writes ",[946,5483,1795],{}," one Run at a time and closes that page's spans only after the last of them, so a Run failed early in a thousand-Run page reads terminal for tens of seconds before its spans close. The client closes its stream on the terminal event, so a client that refetches then reads those spans as ",[946,5486,5487],{},"running"," and nothing corrects it.",[806,5490,5491,5494,5495,5497,5498,5503],{},[815,5492,5493],{},"The stream does not close this, and the attempts to make it are recorded so they are not repeated."," Withholding the frame keeps every viewer of a cancelled Run watching it read ",[946,5496,5487],{}," for the whole reaper period. Gating only the frames the endpoint synthesizes leaves the live event ungated, which is the path a cancel takes. Gating a root on its whole tree blocks on the spans of children that are still alive, which nothing in a reaper pass closes, so the frame waits the full bound every time — and no partial index covers that read. ",[815,5499,5500,5501,1009],{},"A client therefore re-reads a Run it holds as terminal while any of its spans still reads ",[946,5502,5487],{}," The durable fix is a recorder that refuses to open a span under an ended Run, so the two settle together; that is not this phase.",[1153,5505,5507],{"id":5506},"reconnect","Reconnect",[806,5509,5510,5511,5513],{},"Subscribe ",[815,5512,1626],{}," you read. The reverse order drops every event that occurs between the read and the subscription, and V1 has no replay log.",[1158,5515,5518],{"className":5516,"code":5517,"language":1163,"meta":1164},[1161],"1. subscribe to SSE, and buffer what arrives\n2. fetch the Run, then fetch the durable spans\n3. apply the buffer, and drop anything older than the fetched state\n",[946,5519,5517],{"__ignoreMap":1164},[806,5521,5522,5523,5526,5527,5529,5530,5533],{},"Application is idempotent. The client upserts a span by ",[946,5524,5525],{},"span_id",", and it keeps the newer ",[946,5528,1889],{},". The durable record therefore always wins over a lost or duplicated event, so ",[946,5531,5532],{},"SpanNode"," returns the column.",[806,5535,5536,5537,5540,5541,5544,5545,5547],{},"A result item follows the same rule with different fields. The client upserts a prospect by ",[946,5538,5539],{},"item.id"," and keeps the newer ",[946,5542,5543],{},"item.updated_at",". If equal revisions carry different bodies, or the stream reports ",[946,5546,1874],{},", the client refetches the durable prospect state. The durable row decides the result.",[806,5549,5550],{},"The client also refetches the prospect list after the subscribed Run reaches a terminal status. This read covers a lost item event, a projection fault and child detail that the width gate did not mirror.",[806,5552,5553,5556,5557,5559,5560,5563,5564,959,5567,5569,5570,5573,5574,1009],{},[815,5554,5555],{},"A live event needs no timestamp to lose that comparison."," Span status is monotonic: every write to ",[946,5558,5393],{}," is an insert, or an update filtered on ",[946,5561,5562],{},"status = 'running'",", so a span makes exactly one transition. A client that holds a span as ",[946,5565,5566],{},"ok",[946,5568,5403],{}," therefore ignores any later event about it, and applies a ",[946,5571,5572],{},"span.completed"," only over a span it holds as ",[946,5575,5487],{},[806,5577,5578,5588,5589,5591],{},[815,5579,5580,5582,5583,5585,5586,1009],{},[946,5581,5363],{}," filters ",[946,5584,1889],{},", and never ",[946,5587,5354],{}," A tool span that opened before the gap and closed inside it keeps its older ",[946,5590,5354],{},", so a filter on that column never reports the close and the client draws the span as running for the life of the page.",[806,5593,5594,1550,5600,5481,5603,5606,5607,5610,5611,5614,5615,5618,5619,5621],{},[815,5595,5596,5597,1009],{},"The filter is ",[946,5598,5599],{},"updated_at >= since",[946,5601,5602],{},"public.trigger_set_updated_at()",[946,5604,5605],{},"now()",", which is the transaction start time, so the orphan closer stamps every span of one ",[946,5608,5609],{},"UPDATE"," identically. A bare ",[946,5612,5613],{},">"," on the newest value the client holds therefore drops each sibling written in that same transaction. ",[946,5616,5617],{},">="," re-answers the boundary on each poll, and the client upserts by ",[946,5620,5525],{},", so the repeat costs no correctness.",[806,5623,5624,5630,5631,5633,5634,5636,5637,5639,5640,5642],{},[815,5625,5626,5627,1009],{},"The client pages the cursor to exhaustion before it moves ",[946,5628,5629],{},"since"," The boundary is not one row. ",[946,5632,5476],{}," ends every running span of a batch in one statement, and a five hundred way parallel node is this page's own case, so a client that only re-sends the newest ",[946,5635,1889],{}," it holds re-reads that whole group forever and never reaches the span after it. A drain always ends, and two facts prove it. The cursor is ",[946,5638,5367],{},", which is unique and moves forward. And every update of a span filters on ",[946,5641,5562],{}," and writes a terminal status, so a row jumps the cursor at most one time. Cursor order alone would not be enough: a row that could move forward again and again would outrun any scan.",[806,5644,5645,1550,5648,5651,5652,5654,5655,5657,5658,5661,5662,1009],{},[815,5646,5647],{},"A wide reaped Run is briefly half closed, and no lock fixes it.",[946,5649,5650],{},"RunSpanRepository.close_orphans"," pages past 200 ids, one transaction per page, so a ",[946,5653,5363],{}," between two pages reads some spans closed and some still running. That state is correct and momentary. The reaper publishes one ",[946,5656,1600],{}," after its last page, and the client refetches the spans of a Run on every terminal Run event, so the last read is the whole answer. It is one event and not two: ",[946,5659,5660],{},"RunManager.fail"," publishes nothing for the reaper, because a client closes its stream on the first terminal event it reads and an earlier one would land while those spans still read ",[946,5663,5487],{},[968,5665,5667],{"id":5666},"the-three-admin-surfaces","The three admin surfaces",[806,5669,5670],{},"These three configure the platform. They are ordinary product CRUD, they start no Run, and V1 cannot ship without them: a scheduled Signals Search and an approval rule are both unreachable otherwise.",[806,5672,5673],{},"They are not conversational, so they speak the schema and call the domain API, exactly like the Approval Inbox and the Agent Builder. They add no service layer of their own.",[827,5675,5676,5688],{},[830,5677,5678],{},[833,5679,5680,5682,5685],{},[836,5681,838],{},[836,5683,5684],{},"Owns",[836,5686,5687],{},"Calls",[846,5689,5690,5705,5726],{},[833,5691,5692,5694,5700],{},[851,5693,916],{},[851,5695,5696,5699],{},[946,5697,5698],{},"agent.triggers",": pattern, filter, target, input builder, scopes, schedule",[851,5701,5702],{},[946,5703,5704],{},"TriggerRepository",[833,5706,5707,5709,5718],{},[851,5708,926],{},[851,5710,5711,1574,5714,5717],{},[946,5712,5713],{},"agent.policies",[946,5715,5716],{},"agent.cost_ceilings"," rows",[851,5719,5720,949,5723],{},[946,5721,5722],{},"PolicyRepository",[946,5724,5725],{},"CostCeilingRepository",[833,5727,5728,5730,5733],{},[851,5729,936],{},[851,5731,5732],{},"Provider credentials needed by the next product slice. Channel installs and MCP connections are deferred",[851,5734,5735],{},[946,5736,5737],{},"ConnectionResolver",[1158,5739,5742],{"className":5740,"code":5741,"language":1163,"meta":1164},[1161],"GET    \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers?enabled=&cursor=\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers\nPATCH  \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers\u002F{id}                expected_updated_at\nDELETE \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers\u002F{id}\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers\u002F{id}\u002Fenable\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Ftriggers\u002F{id}\u002Fdisable\n\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fpolicies?action=&cursor=\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fpolicies\nPATCH  \u002Fapi\u002Fv1\u002Fagentic\u002Fpolicies\u002F{id}                expected_updated_at\nDELETE \u002Fapi\u002Fv1\u002Fagentic\u002Fpolicies\u002F{id}\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Flimits\nPUT    \u002Fapi\u002Fv1\u002Fagentic\u002Flimits\n\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fconnections?kind=&cursor=\nGET    \u002Fapi\u002Fv1\u002Fagentic\u002Fconnections\u002F{id}\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fconnections\u002F{id}\u002Freconnect\nPOST   \u002Fapi\u002Fv1\u002Fagentic\u002Fconnections\u002F{id}\u002Frevoke\n",[946,5743,5741],{"__ignoreMap":1164},[1153,5745,1931],{"id":5746},"rules-2",[1933,5748,5749,5752,5766,5769,5775,5780,5783,5786],{},[1936,5750,5751],{},"Organization admins only. These three rows change what runs without a person.",[1936,5753,5754,5755,1871,5758,5761,5762,5765],{},"A trigger carries its own scopes. The save validates them against the authoring admin's rights ",[815,5756,5757],{},"at that moment",[946,5759,5760],{},"PrincipalFactory"," meets them again with that admin's live rights ",[815,5763,5764],{},"at every mint",". The save-time check stops an admin writing a scope they do not hold. The mint-time meet is what makes a trigger never outlive the authority that created it. Both run, and neither replaces the other.",[1936,5767,5768],{},"A trigger row that authored no scope is refused at save. Admission already refuses it, and 02:00 is the wrong hour to learn that a row was never filled in.",[1936,5770,5771,5772,5774],{},"A trigger has no service layer. The router calls ",[946,5773,5704],{},", exactly as the policy and saved-search routers call theirs.",[1936,5776,5777,5778,1009],{},"Saving a policy validates every target fact it names against the action it binds to. An undeclared fact is refused at save time, not at run time. See ",[1005,5779,2242],{"href":287},[1936,5781,5782],{},"A connection never returns a credential. The row holds a vault reference, and the response holds the status only.",[1936,5784,5785],{},"Deleting a trigger does not stop a Run it already started. Cancel the Run.",[1936,5787,5788,5789,1009],{},"These routes start no Run, so they need no ",[946,5790,1079],{},[968,5792,5794],{"id":5793},"core-code","Core code",[806,5796,5797],{},"Each surface is a router over a domain component. A surface can add a bounded read model. The prospect capability also adds the shared product repository that its routes and later prospect tools use.",[1158,5799,5802],{"className":5800,"code":5801,"language":1163,"meta":1164},[1161],"src\u002Fagentic\u002Fsurfaces\u002F\n  conversations\u002F  router.py  schemas.py  stream.py    -> ConversationService, FrontDoorService\n  approvals\u002F      router.py  read_models.py           -> ApprovalService\n  prospects\u002F      router.py  read_models.py           -> services\u002Fprospects\u002FProspectRepository\n  saved_searches\u002F router.py  schemas.py               -> SavedSearchService\n  definitions\u002F    router.py  schemas.py  tools.py     -> DefinitionService, ToolRegistry\n  runs\u002F           router.py  read_models.py  stream.py -> RunManager, RunSpanRepository\n  triggers\u002F       router.py  schemas.py               -> triggers\u002FTriggerRepository\n  policies\u002F       router.py  schemas.py               -> PolicyRepository, CostCeilingRepository\n  connections\u002F    router.py  schemas.py               -> ConnectionResolver\n  _shared\u002F        org_scope.py  pagination.py  sse.py\n\nsrc\u002Fagentic\u002Fservices\u002F\n  prospects\u002F      repository.py\n\nsrc\u002Fagentic\u002Fentry_control\u002F\n  conversations\u002F  models.py  repository.py  service.py  events.py\n  channels\u002F       adapters\u002Fweb.py  models.py\n  front_door\u002F     service.py  protocol.py  agno.py  policy.py  capabilities.py\n  inngest\u002F        turn.py\n",[946,5803,5801],{"__ignoreMap":1164},[806,5805,5806,5818,5819,5822,5823,5826,5827,5829,5830,5832,5833,1574,5836,5838],{},[815,5807,5808,5810,5811,5814,5815,1009],{},[946,5809,1581],{}," lives in ",[946,5812,5813],{},"entry_control\u002Fconversations\u002Fevents.py",", and it\ndoes not live beside ",[946,5816,5817],{},"RUN_EVENTS"," It is one more ",[946,5820,5821],{},"ChannelScheme",", on the\nprefix ",[946,5824,5825],{},"agentic:conversation",", and it is a conversation concern. Put it beside\n",[946,5828,5817],{}," for the symmetry, and ",[946,5831,149],{}," holds a type that only\n",[946,5834,5835],{},"entry_control",[946,5837,253],{}," read.",[806,5840,5841,5851,5852,5854,5855,5858],{},[815,5842,5843,5844,1832,5847,5850],{},"The turn function lives in ",[946,5845,5846],{},"entry_control\u002Finngest\u002Fturn.py",[946,5848,5849],{},"inngest_functions\u002F__init__.py"," imports it to register it."," That is the shape\n",[946,5853,3454],{}," already has: the function sits in ",[946,5856,5857],{},"runtime\u002Finngest\u002Fexecute.py",",\nand the registry imports it. The registry is a list, and a list of functions has\nto reach the packages that hold them.",[806,5860,5861,5863,5864,5866,5867,5869,5870,5872,5873,5875],{},[946,5862,5835],{}," holds the conversational path. It sits above ",[946,5865,149],{},", because it calls ",[946,5868,1791],{},", and below ",[946,5871,253],{},", because the conversation router calls it. ",[1005,5874,242],{"href":241}," § Core code components owns its import contracts.",[806,5877,5878,1550,5883,5885,5886,1574,5889,5891,5892,5894,5895,5898,5899,949,5901,949,5903,949,5905,949,5908,949,5910,949,5912,1574,5915,5918,5919,1009],{},[815,5879,5880,5882],{},[946,5881,253],{}," is the top layer of the platform, and one more contract says so.",[946,5884,149],{}," is already forbidden to ",[946,5887,5888],{},"services",[946,5890,294],{},". A new package that none of those contracts name may be imported by any of them, so ",[946,5893,149],{}," could read a read model and ",[946,5896,5897],{},"lint-imports"," would stay green. The contract therefore forbids ",[946,5900,5888],{},[946,5902,294],{},[946,5904,149],{},[946,5906,5907],{},"shared",[946,5909,320],{},[946,5911,5835],{},[946,5913,5914],{},"inngest_app",[946,5916,5917],{},"inngest_functions"," to import ",[946,5920,253],{},[806,5922,1182,5923,5930,5931,5933,5934,5936],{},[815,5924,5925,5926,5929],{},"Every package under ",[946,5927,5928],{},"src.agentic"," is a source of that contract, and the list is written by hand."," import-linter refuses a contract whose source and forbidden modules share a descendant, so ",[946,5932,5928],{}," itself cannot be the source. A new top-level package joins the list on the day it lands, and ",[946,5935,5835],{}," is the next one.",[806,5938,1182,5939,1550,5942,5945,5946,5948,5949,5951,5952,5954],{},[815,5940,5941],{},"The direction is downwards, and a router reads every layer under it.",[946,5943,5944],{},"surfaces\u002Fruns\u002Fdependencies.py"," reads ",[946,5947,4525],{}," in ",[946,5950,149],{}," and the usage meter in ",[946,5953,294],{},". A contract that forbade those imports would refuse the code this page describes.",[806,5956,5957,5963,5964,5966,5967,5969,5970,5973,5974,5977,5978,5980],{},[815,5958,5959,5960,5962],{},"Every surface module sits under ",[946,5961,5928],{},", and that is the enforced path."," The import-linter contract from ",[1005,5965,1008],{"href":1007}," binds ",[946,5968,5928],{}," as its source module. A router under ",[946,5971,5972],{},"src\u002Fdomains\u002F"," would be outside it, so it could import ",[946,5975,5976],{},"src.domains.workflows"," while ",[946,5979,5897],{}," stayed green, which is the one thing the contract exists to stop.",[806,5982,5983,5986,5987,5989,5990,5992,5993,5996],{},[946,5984,5985],{},"org_scope.py"," holds the HTTP identity seam for ",[946,5988,1036],{}," schema resources. Their repositories write the organization filter because the service role bypasses RLS.\nA public product repository uses ",[946,5991,1048],{}," instead. ",[946,5994,5995],{},"ProspectRepository"," is the first capability repository with that shape.",[806,5998,5999],{},"A read model exists so that a router never returns a domain entity.",[1158,6001,6005],{"className":6002,"code":6003,"language":6004,"meta":1164,"style":1164},"language-python shiki shiki-themes github-dark","@dataclass(frozen=True)\nclass RunSummary:\n    id: UUID\n    kind: RunKind\n    definition_id: UUID\n    definition_name: str\n    status: RunStatus\n    waiting_on: WaitingOn | None\n    started_at: datetime\n    ended_at: datetime | None\n    root_run_id: UUID | None\n    source: RunSource\n\n@dataclass(frozen=True)\nclass RunDetail(RunSummary):\n    input: dict                         # refused above 32 KB at start; never trimmed\n    result: dict | None                 # already bounded at 32 KB by bound()\n    error: ErrorSummary | None\n    usage: UsageSummary | None          # null when the meter answered no row\n    child_count: int                    # the children are a page of the list route\n    pending_approval_id: UUID | None    # agent.runs.waiting_ref_id, on an approval wait\n\n@dataclass(frozen=True)\nclass SpanNode:\n    span_id: UUID\n    parent_span_id: UUID | None\n    kind: SpanKind\n    name: str\n    status: Literal['ok', 'error', 'running']\n    started_at: datetime\n    updated_at: datetime                # the reconnect keeps the newer of two copies\n    duration_ms: int | None\n    usage_id: UUID | None\n    error: ErrorSummary | None\n","python",[946,6006,6007,6012,6017,6022,6027,6032,6037,6042,6047,6052,6057,6062,6067,6073,6077,6083,6089,6095,6101,6107,6112,6117,6121,6126,6132,6138,6144,6150,6156,6162,6166,6172,6178,6184],{"__ignoreMap":1164},[1297,6008,6009],{"class":1299,"line":22},[1297,6010,6011],{},"@dataclass(frozen=True)\n",[1297,6013,6014],{"class":1299,"line":32},[1297,6015,6016],{},"class RunSummary:\n",[1297,6018,6019],{"class":1299,"line":233},[1297,6020,6021],{},"    id: UUID\n",[1297,6023,6024],{"class":1299,"line":244},[1297,6025,6026],{},"    kind: RunKind\n",[1297,6028,6029],{"class":1299,"line":264},[1297,6030,6031],{},"    definition_id: UUID\n",[1297,6033,6034],{"class":1299,"line":222},[1297,6035,6036],{},"    definition_name: str\n",[1297,6038,6039],{"class":1299,"line":360},[1297,6040,6041],{},"    status: RunStatus\n",[1297,6043,6044],{"class":1299,"line":368},[1297,6045,6046],{},"    waiting_on: WaitingOn | None\n",[1297,6048,6049],{"class":1299,"line":375},[1297,6050,6051],{},"    started_at: datetime\n",[1297,6053,6054],{"class":1299,"line":157},[1297,6055,6056],{},"    ended_at: datetime | None\n",[1297,6058,6059],{"class":1299,"line":182},[1297,6060,6061],{},"    root_run_id: UUID | None\n",[1297,6063,6064],{"class":1299,"line":290},[1297,6065,6066],{},"    source: RunSource\n",[1297,6068,6069],{"class":1299,"line":280},[1297,6070,6072],{"emptyLinePlaceholder":6071},true,"\n",[1297,6074,6075],{"class":1299,"line":272},[1297,6076,6011],{},[1297,6078,6080],{"class":1299,"line":6079},15,[1297,6081,6082],{},"class RunDetail(RunSummary):\n",[1297,6084,6086],{"class":1299,"line":6085},16,[1297,6087,6088],{},"    input: dict                         # refused above 32 KB at start; never trimmed\n",[1297,6090,6092],{"class":1299,"line":6091},17,[1297,6093,6094],{},"    result: dict | None                 # already bounded at 32 KB by bound()\n",[1297,6096,6098],{"class":1299,"line":6097},18,[1297,6099,6100],{},"    error: ErrorSummary | None\n",[1297,6102,6104],{"class":1299,"line":6103},19,[1297,6105,6106],{},"    usage: UsageSummary | None          # null when the meter answered no row\n",[1297,6108,6109],{"class":1299,"line":520},[1297,6110,6111],{},"    child_count: int                    # the children are a page of the list route\n",[1297,6113,6114],{"class":1299,"line":318},[1297,6115,6116],{},"    pending_approval_id: UUID | None    # agent.runs.waiting_ref_id, on an approval wait\n",[1297,6118,6119],{"class":1299,"line":327},[1297,6120,6072],{"emptyLinePlaceholder":6071},[1297,6122,6124],{"class":1299,"line":6123},23,[1297,6125,6011],{},[1297,6127,6129],{"class":1299,"line":6128},24,[1297,6130,6131],{},"class SpanNode:\n",[1297,6133,6135],{"class":1299,"line":6134},25,[1297,6136,6137],{},"    span_id: UUID\n",[1297,6139,6141],{"class":1299,"line":6140},26,[1297,6142,6143],{},"    parent_span_id: UUID | None\n",[1297,6145,6147],{"class":1299,"line":6146},27,[1297,6148,6149],{},"    kind: SpanKind\n",[1297,6151,6153],{"class":1299,"line":6152},28,[1297,6154,6155],{},"    name: str\n",[1297,6157,6159],{"class":1299,"line":6158},29,[1297,6160,6161],{},"    status: Literal['ok', 'error', 'running']\n",[1297,6163,6164],{"class":1299,"line":481},[1297,6165,6051],{},[1297,6167,6169],{"class":1299,"line":6168},31,[1297,6170,6171],{},"    updated_at: datetime                # the reconnect keeps the newer of two copies\n",[1297,6173,6175],{"class":1299,"line":6174},32,[1297,6176,6177],{},"    duration_ms: int | None\n",[1297,6179,6181],{"class":1299,"line":6180},33,[1297,6182,6183],{},"    usage_id: UUID | None\n",[1297,6185,6187],{"class":1299,"line":6186},34,[1297,6188,6100],{},[806,6190,6191,6192,6195],{},"One helper answers visibility for every router. It is named for what it does, because a name like ",[946,6193,6194],{},"SurfaceAuthz"," invites the reader to mistake it for a second policy engine.",[1158,6197,6199],{"className":6002,"code":6198,"language":6004,"meta":1164,"style":1164},"class SurfaceVisibility:\n    \"\"\"Visibility only. Policy decides every action below this.\"\"\"\n\n    async def run(self, principal: Principal, run_id: UUID) -> Run: ...\n    async def approval(self, principal: Principal, approval_id: UUID) -> tuple[Approval, bool]:\n        \"\"\"Return the row, and whether this principal may resolve it.\"\"\"\n    async def definition(self, principal: Principal, definition_id: UUID, *, write: bool) -> Definition: ...\n",[946,6200,6201,6206,6211,6215,6220,6225,6230],{"__ignoreMap":1164},[1297,6202,6203],{"class":1299,"line":22},[1297,6204,6205],{},"class SurfaceVisibility:\n",[1297,6207,6208],{"class":1299,"line":32},[1297,6209,6210],{},"    \"\"\"Visibility only. Policy decides every action below this.\"\"\"\n",[1297,6212,6213],{"class":1299,"line":233},[1297,6214,6072],{"emptyLinePlaceholder":6071},[1297,6216,6217],{"class":1299,"line":244},[1297,6218,6219],{},"    async def run(self, principal: Principal, run_id: UUID) -> Run: ...\n",[1297,6221,6222],{"class":1299,"line":264},[1297,6223,6224],{},"    async def approval(self, principal: Principal, approval_id: UUID) -> tuple[Approval, bool]:\n",[1297,6226,6227],{"class":1299,"line":222},[1297,6228,6229],{},"        \"\"\"Return the row, and whether this principal may resolve it.\"\"\"\n",[1297,6231,6232],{"class":1299,"line":360},[1297,6233,6234],{},"    async def definition(self, principal: Principal, definition_id: UUID, *, write: bool) -> Definition: ...\n",[806,6236,6237],{},"The stream services share one shape. They subscribe, and they publish nothing.",[1158,6239,6241],{"className":6002,"code":6240,"language":6004,"meta":1164,"style":1164},"class RunStreamService:\n    async def subscribe(self, run_id: UUID, organization_id: UUID) -> AsyncGenerator[str]:\n        \"\"\"Prove visibility of this run, then read its own channel.\"\"\"\n\nclass ConversationStreamService:\n    async def subscribe(\n        self, conversation_id: UUID, actor: ActorIdentity\n    ) -> AsyncGenerator[str]: ...\n",[946,6242,6243,6248,6253,6258,6262,6267,6272,6277],{"__ignoreMap":1164},[1297,6244,6245],{"class":1299,"line":22},[1297,6246,6247],{},"class RunStreamService:\n",[1297,6249,6250],{"class":1299,"line":32},[1297,6251,6252],{},"    async def subscribe(self, run_id: UUID, organization_id: UUID) -> AsyncGenerator[str]:\n",[1297,6254,6255],{"class":1299,"line":233},[1297,6256,6257],{},"        \"\"\"Prove visibility of this run, then read its own channel.\"\"\"\n",[1297,6259,6260],{"class":1299,"line":244},[1297,6261,6072],{"emptyLinePlaceholder":6071},[1297,6263,6264],{"class":1299,"line":264},[1297,6265,6266],{},"class ConversationStreamService:\n",[1297,6268,6269],{"class":1299,"line":222},[1297,6270,6271],{},"    async def subscribe(\n",[1297,6273,6274],{"class":1299,"line":360},[1297,6275,6276],{},"        self, conversation_id: UUID, actor: ActorIdentity\n",[1297,6278,6279],{"class":1299,"line":368},[1297,6280,6281],{},"    ) -> AsyncGenerator[str]: ...\n",[806,6283,1182,6284,6287,6288,6291,6292,6295],{},[815,6285,6286],{},"A stream service yields SSE frames, and it yields no event object."," The\nendpoint hands what this generator answers straight to ",[946,6289,6290],{},"StreamingResponse",", so\nthe frame is the unit. ",[946,6293,6294],{},"RunStreamService"," builds two frames off the run row that\nno publisher wrote, and a typed event in the middle would have to be built and\nthen unbuilt.",[806,6297,1182,6298,6304],{},[815,6299,6300,6301,1009],{},"Neither takes a ",[946,6302,6303],{},"Principal"," A principal is the frozen authority of one\nrun, and a reader has none.",[806,6306,6307,6308,6310,6311,6314,6315,6318,6319,6322,6323,6326,6327,1009],{},"The two differ in what they take instead, and the reason is visibility.\n",[946,6309,6294],{}," takes the tenant, because ",[946,6312,6313],{},"require_run"," filters on it alone.\nA conversation is read by the person who created it, so the tenant does not\nanswer visibility on its own: ",[946,6316,6317],{},"ConversationStreamService"," takes the whole\n",[946,6320,6321],{},"ActorIdentity"," and hands it to ",[946,6324,6325],{},"require_conversation",", which owns both filters\nand the ",[946,6328,1028],{},[1153,6330,6331],{"id":612},"Data model",[806,6333,6334,6335,6338],{},"Two tables join the ",[1005,6336,6337],{"href":203},"platform data model"," for web chat.",[827,6340,6341,6351],{},[830,6342,6343],{},[833,6344,6345,6348],{},[836,6346,6347],{},"Table",[836,6349,6350],{},"Holds",[846,6352,6353,6363],{},[833,6354,6355,6360],{},[851,6356,6357],{},[946,6358,6359],{},"agent.conversations",[851,6361,6362],{},"Organization, creator, title, summary, last activity",[833,6364,6365,6370],{},[851,6366,6367],{},[946,6368,6369],{},"agent.conversation_messages",[851,6371,6372,6373,6375],{},"Role, sender, text, attachment count, originating ",[946,6374,1724],{}," when one exists",[806,6377,6378,6381,6382,6384,6385,1009],{},[946,6379,6380],{},"agent.channel_sessions"," is deferred with remote interactive channels. When Slack lands, it maps an external thread onto a ",[946,6383,6359],{}," row without replacing the conversation. A shared Slack thread then holds one conversation and more than one sender. See the ",[1005,6386,6387],{"href":230},"channel gateway",[968,6389,6390],{"id":159},"Migration",[806,6392,6393,6394,6397],{},"Two start routes exist today, and ",[1005,6395,6396],{"href":219},"agent access"," records them.",[1158,6399,6402],{"className":6400,"code":6401,"language":1163,"meta":1164},[1161],"POST \u002Fapi\u002Fv1\u002Fagents\u002Fruns                  ->  POST \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\nPOST \u002Fapi\u002Fv1\u002Fworkflows\u002F{id}\u002Fruns          ->  POST \u002Fapi\u002Fv1\u002Fagentic\u002Fruns\n",[946,6403,6401],{"__ignoreMap":1164},[806,6405,6406,6407,6410],{},"The ",[946,6408,6409],{},"ac"," CLI drives both, so the change needs a CLI update in the same branch.",[806,6412,6413,6416,6417,6420],{},[815,6414,6415],{},"Cutover is Phase 9."," Phase 7 delivers Company, People and Signals capabilities. Phase 8 delivers email sequences.\nThe new runtime develops on the four repositories' ",[946,6418,6419],{},"agentic-platform"," branches. Production fallback remains separate until cutover.\nTargeted legacy chat removal already exists on trunk; do not infer trunk file presence from production coexistence.\nPhase 9 owns the remaining route migration and legacy removal. Phase 7 preserves the CRM and prospect routes named above.",[806,6422,6423,6424,6427,6428,6431,6432,6434,6435,6437,6438,6441],{},"One consequence binds the parity audit. ",[946,6425,6426],{},"audit_endpoints.py"," compares the CLI against a live ",[946,6429,6430],{},"\u002Fopenapi.json",", so a CLI that knows ",[946,6433,998],{}," reports every one of those paths as CLI-ONLY against a staging API that does not serve them yet. The CLI change therefore stays on the ",[946,6436,6419],{}," branch of ",[946,6439,6440],{},"ac-cli",", and the audit runs against a branch API.",[968,6443,1931],{"id":6444},"rules-3",[1933,6446,6447,6450,6453,6456,6459,6464,6467,6470,6473],{},[1936,6448,6449],{},"Web chat has no private path around Channel Gateway.",[1936,6451,6452],{},"A schema-native surface does not go through Channel Gateway.",[1936,6454,6455],{},"Admins author. Authorized users run.",[1936,6457,6458],{},"A surface never duplicates definition validation, approval resolution or span logic.",[1936,6460,6461,6462,1009],{},"A surface never writes Run state. It calls ",[946,6463,1791],{},[1936,6465,6466],{},"A surface returns no unbounded list. A related set is a page of a list route, never an array inside a detail response.",[1936,6468,6469],{},"Every refusal a domain component returns maps to one status, in one table on the page that owns the route.",[1936,6471,6472],{},"The durable record is authoritative. A live event is a hint.",[1936,6474,6475],{},"Surface rendering may degrade. The decision and the state below it do not.",[968,6477,6479],{"id":6478},"open-decisions","Open decisions",[6481,6482,6483,6489],"ol",{},[1936,6484,6485,6488],{},[815,6486,6487],{},"Self-approval."," V1 permits it, because a small organization has one admin. A later policy condition can require a second person.",[1936,6490,6491,6497],{},[815,6492,6493,6494,1009],{},"Redaction depth for ",[946,6495,6496],{},"proposed_content"," The Tool result rules apply today. Confirm they are enough for an email body.",[968,6499,6501],{"id":6500},"minimum-contract-tests","Minimum contract tests",[1933,6503,6504,6510,6519,6526,6532,6535,6540,6545,6551,6556,6562,6565,6568,6571,6574,6577,6580,6586,6593,6600,6605,6611,6614,6617,6620,6626,6629,6636,6639,6642,6645,6650,6658,6666,6669,6672,6675,6678,6681,6689,6694,6700,6706,6711,6714,6717,6720,6725,6728,6733,6738,6744,6747,6750,6753,6756,6759,6762,6768,6773,6778,6783,6786,6789,6792,6795,6798,6801,6804,6807,6810,6813,6816,6819,6822,6825,6828,6831,6834,6837,6840,6843,6846,6849,6852,6855,6858,6861,6867,6870,6876,6879,6885,6895,6901,6907,6917,6930,6933,6936,6939,6942,6945,6950,6953,6958,6969,6977,6980,6983,6988,6991,6994,6997,7000],{},[1936,6505,6506,6507,6509],{},"A web chat POST returns ",[946,6508,1194],{}," before any model call.",[1936,6511,6512,6513,6515,6516,6518],{},"The message row is committed before the ",[946,6514,1194],{},", so an immediate ",[946,6517,1515],{}," reads it.",[1936,6520,1920,6521,1923,6523,6525],{},[946,6522,1079],{},[946,6524,1099],{}," with the first message, and one turn runs.",[1936,6527,6528,6529,6531],{},"A message over the text limit answers ",[946,6530,1083],{},", and writes no row.",[1936,6533,6534],{},"A Front Door answer arrives on the conversation stream, and it creates no Run.",[1936,6536,6537,6538,1009],{},"A conversation in another organization and a missing conversation return the same ",[946,6539,1028],{},[1936,6541,6542,6543,1009],{},"A colleague's conversation and a missing conversation return the same ",[946,6544,1028],{},[1936,6546,6547,6548,6550],{},"An empty message and a whitespace-only message both answer ",[946,6549,1083],{},", and write no row.",[1936,6552,6553,6554,1009],{},"An impersonated session reads a conversation, and a write answers ",[946,6555,1032],{},[1936,6557,6558,6559,6561],{},"One ",[946,6560,1079],{}," reused in two conversations writes two messages.",[1936,6563,6564],{},"A duplicate POST sends the durable event again, and one turn runs.",[1936,6566,6567],{},"The message list and the conversation list both read newest first.",[1936,6569,6570],{},"Two messages posted 200 ms apart in one conversation produce two turns that do not interleave, and they run in order when neither retried.",[1936,6572,6573],{},"A turn over the organization day ceiling writes an assistant message and makes no model call.",[1936,6575,6576],{},"A retried turn writes one assistant message, and starts at most one Run.",[1936,6578,6579],{},"A retried turn makes one model call, and writes one usage row.",[1936,6581,6582,6583,6585],{},"A failed turn writes one ",[946,6584,1780],{}," message, and a second retry writes no copy of it.",[1936,6587,6588,6589,1716,6591,1009],{},"A delegation writes one assistant message carrying ",[946,6590,1724],{},[946,6592,1589],{},[1936,6594,6595,6596,2267,6598,1009],{},"A policy denial carries ",[946,6597,1686],{},[946,6599,1589],{},[1936,6601,6602,6603,1009],{},"Every frame of one turn carries the same ",[946,6604,1178],{},[1936,6606,6607,6608,6610],{},"A turn publishes ",[946,6609,1648],{}," only after Inngest spends every attempt.",[1936,6612,6613],{},"Two messages in different conversations run at the same time.",[1936,6615,6616],{},"The conversation stream carries no terminal event, and the client closes it itself.",[1936,6618,6619],{},"The client subscribes before it sends, and it reconciles after each resubscribe.",[1936,6621,6622,6623,6625],{},"A ",[946,6624,1686],{}," lost to a gap is read back by the reconcile, and the message stops pending.",[1936,6627,6628],{},"A page read merges into the held messages, so a frame that arrived first survives it.",[1936,6630,6631,1574,6633,6635],{},[946,6632,1648],{},[946,6634,1874],{}," both refetch, and neither renders text of its own.",[1936,6637,6638],{},"A reconnect that subscribes before it reads loses no span.",[1936,6640,6641],{},"A workflow parent stream carries the span events of its children.",[1936,6643,6644],{},"A child Run stream carries that child's events, and the reader resolves no root.",[1936,6646,6647,6649],{},[946,6648,5363],{}," answers a span that closed inside the gap.",[1936,6651,6652,6654,6655,6657],{},[946,6653,5363],{}," set to the exact ",[946,6656,1889],{}," of a closed span still answers it.",[1936,6659,6660,6661,6663,6664,1009],{},"A spans cursor written under ",[946,6662,5363],{}," and replayed without it answers ",[946,6665,1083],{},[1936,6667,6668],{},"A stream opened on a Run that already ended answers a terminal frame, and the client closes on it.",[1936,6670,6671],{},"A stream whose Run row vanishes still answers a terminal frame.",[1936,6673,6674],{},"The runs list excludes child Runs by default.",[1936,6676,6677],{},"A run detail response carries no child array, whatever the width of the tree.",[1936,6679,6680],{},"The runs list and the spans list both page correctly when two rows share a timestamp.",[1936,6682,6683,6686,6687,1009],{},[946,6684,6685],{},"POST \u002Fruns"," refuses a body carrying ",[946,6688,4990],{},[1936,6690,6691,6692,1009],{},"A start against another organization's definition and a start against a missing one return the same ",[946,6693,1028],{},[1936,6695,6696,6697,6699],{},"A prospect in another organization and a missing prospect return the same ",[946,6698,1028],{}," on detail, child pages and curation.",[1936,6701,6702,6703,6705],{},"Empty prospect, people and signals pages return an empty ",[946,6704,2399],{}," array and no next cursor.",[1936,6707,6708,6709,1009],{},"The three prospect pages do not drop or repeat rows when two rows share one ",[946,6710,2094],{},[1936,6712,6713],{},"A score update between two prospect pages does not move that prospect across the cursor.",[1936,6715,6716],{},"Prospect pages preserve null opportunity scores and null signal scores.",[1936,6718,6719],{},"Prospect detail, people and signals responses preserve every documented nullable field without inventing a value.",[1936,6721,6722,6723,1009],{},"A repeated watch or dismiss writes nothing and returns ",[946,6724,1099],{},[1936,6726,6727],{},"Concurrent watch and dismiss requests both succeed, and a refetch returns the last accepted database state.",[1936,6729,6730,6731,1009],{},"A watch or dismiss racing with promotion never overwrites ",[946,6732,2324],{},[1936,6734,6735,6736,1009],{},"A promotion racing with a second promotion writes one company, one row for each person, and one ",[946,6737,2324],{},[1936,6739,6740,6741,6743],{},"Prospect responses carry no related array, ",[946,6742,2781],{}," array or raw provider payload.",[1936,6745,6746],{},"A saved search create and read preserve every unknown brief field and return normalized supported fields.",[1936,6748,6749],{},"A saved search list returns no brief.",[1936,6751,6752],{},"A brief with no ICP and no company criteria is refused.",[1936,6754,6755],{},"A brief with no usable persona is refused on create, patch and start.",[1936,6757,6758],{},"A legacy brief with no persona remains readable and deletable. A name-only patch can rename it; a brief patch must repair the persona.",[1936,6760,6761],{},"A brief whose composed Run input exceeds 32 KiB is refused before storage.",[1936,6763,6764,6765,6767],{},"Missing and foreign saved searches return the same ",[946,6766,1028],{}," on every route.",[1936,6769,6770,6771,1009],{},"Two saved searches with the same trimmed name in one organization return ",[946,6772,1103],{},[1936,6774,6775,6776,1009],{},"An empty patch writes nothing, and a stale update token returns ",[946,6777,1103],{},[1936,6779,6780,6781,1009],{},"The saved-search list pages correctly when two rows share one ",[946,6782,2094],{},[1936,6784,6785],{},"Starting a saved search freezes its brief and calls the normal Run lifecycle.",[1936,6787,6788],{},"Starting a saved search freezes the current published Run as its comparison baseline.",[1936,6790,6791],{},"Two saved-search starts with one idempotency key create one Run.",[1936,6793,6794],{},"Concurrent same-key starts cannot freeze different briefs or baselines; a preparation loser retries the same key.",[1936,6796,6797],{},"Starting a saved search creates no Trigger or schedule.",[1936,6799,6800],{},"A saved search with no published Run returns an empty diff and a null Run id.",[1936,6802,6803],{},"A published Run with no material change returns an empty diff and its Run id.",[1936,6805,6806],{},"A failed or cancelled Run does not replace the latest Smart Feed pointer.",[1936,6808,6809],{},"A cancellation after workflow execution but before success or dispatch does not publish.",[1936,6811,6812],{},"A missing, dropped or schema-invalid Smart Feed projection does not publish.",[1936,6814,6815],{},"A partial Run does not publish. Ref-only truncation does not block an intact projection.",[1936,6817,6818],{},"A result without a ready final observation node does not publish.",[1936,6820,6821],{},"A final observation with 100 signals or people for one prospect, or an oversized answer, returns a non-ready result and publishes nothing; it never prunes prospects or components.",[1936,6823,6824],{},"Of two overlapping successful Runs with one baseline, only the first publication compare-and-set replaces the pointer.",[1936,6826,6827],{},"A successful Run whose baseline is stale remains in Run Explorer and does not replay an earlier diff.",[1936,6829,6830],{},"The final observation node and another Run can race without mixing their prospect, signal or people state.",[1936,6832,6833],{},"A prospect last written by another Run before the final snapshot makes the older result superseded.",[1936,6835,6836],{},"A durable publish dispatch retries, and the next Run keeps the baseline when dispatch never completes.",[1936,6838,6839],{},"A partial failed write cannot alter a prior successful Run's diff.",[1936,6841,6842],{},"Deleting the saved search before the immutable insert, pointer move or membership write makes that boundary a clean no-op.",[1936,6844,6845],{},"A publish event with a forged or mismatched organization and Run id writes nothing.",[1936,6847,6848],{},"Every observed prospect has one immutable Run row; an unchanged row has no change reason.",[1936,6850,6851],{},"A retry cannot overwrite an immutable observation with different values.",[1936,6853,6854],{},"A retry after pointer movement finishes an interrupted membership update.",[1936,6856,6857],{},"A later Run repairs missing baseline membership after terminal consumer failure.",[1936,6859,6860],{},"Each change kind follows the versioned digest rule, and an unknown version publishes nothing.",[1936,6862,6863,6864,6866],{},"A diff returns only immutable Run rows whose ",[946,6865,1724],{}," matches the latest Run and whose change reasons are not empty.",[1936,6868,6869],{},"A diff response carries no digest and does not recompute change reasons.",[1936,6871,6872,6873,6875],{},"A newly published Run between diff pages returns ",[946,6874,1103],{},"; it never combines two Runs.",[1936,6877,6878],{},"No saved-search route accepts a historical Run id.",[1936,6880,6881,6882,6884],{},"A start against the caller's own draft or disabled definition returns ",[946,6883,1103],{},", and names the state.",[1936,6886,6622,6887,6889,6890,6892,6893,1009],{},[946,6888,6685],{}," with no ",[946,6891,1079],{},", or one over 255 characters, returns ",[946,6894,1083],{},[1936,6896,6897,6898,6900],{},"Two people in one organization sending the same ",[946,6899,1079],{}," create two Runs.",[1936,6902,6903,6904,6906],{},"A stale ",[946,6905,3135],{}," fails cleanly, on a draft save and on a publish.",[1936,6908,6909,6910,6913,6914,6916],{},"Two POSTs to ",[946,6911,6912],{},"\u002Fruns"," with one ",[946,6915,1079],{}," create one Run.",[1936,6918,6919,6920,6922,6923,6925,6926,2746,6928,1009],{},"Two concurrent POSTs with one ",[946,6921,1079],{}," both answer ",[946,6924,1099],{},", both name the same Run, and one carries ",[946,6927,4145],{},[946,6929,5131],{},[1936,6931,6932],{},"Cancel is idempotent on a running, a waiting and a finished Run.",[1936,6934,6935],{},"A disabled definition blocks a new Run, and it does not stop an in-flight Run.",[1936,6937,6938],{},"The Builder cannot publish an invalid Agent, Workflow or Skill.",[1936,6940,6941],{},"The Builder cannot disable an active definition that another active definition references.",[1936,6943,6944],{},"A platform template cannot be edited, and a fork of it can.",[1936,6946,6947,6948,1009],{},"A platform template appears in the definitions list, and its detail route answers ",[946,6949,1028],{},[1936,6951,6952],{},"A disabled platform template appears in no list, and no fork.",[1936,6954,6955,6956,1009],{},"The definitions list pages correctly when two rows share a ",[946,6957,2094],{},[1936,6959,4824,6960,6962,6963,6965,6966,6968],{},[946,6961,4753],{}," body answers ",[946,6964,1083],{},", and the row's ",[946,6967,1889],{}," does not move.",[1936,6970,6622,6971,6974,6975,6531],{},[946,6972,6973],{},"POST ...\u002Fdefinitions"," by a non-admin answers ",[946,6976,1032],{},[1936,6978,6979],{},"Two concurrent approval decisions produce one state transition.",[1936,6981,6982],{},"An expired approval and a stale-hash approval do not execute.",[1936,6984,6985,6986,6767],{},"A principal from another organization receives ",[946,6987,1028],{},[1936,6989,6990],{},"A non-admin cannot write a trigger, a policy, a limit or a connection.",[1936,6992,6993],{},"A trigger cannot be saved with a scope its author does not hold.",[1936,6995,6996],{},"A policy naming an undeclared target fact is refused when it is saved.",[1936,6998,6999],{},"A connection response never carries a credential.",[1936,7001,7002],{},"Run Explorer rebuilds its state from the Run and the spans, with no event log.",[7004,7005,7006],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s6RL2, html code.shiki .s6RL2{--shiki-default:#FDAEB7;--shiki-default-font-style:italic}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":1164,"searchDepth":32,"depth":233,"links":7008},[7009,7010,7017,7023,7030,7036,7046,7050,7056,7059,7062,7063,7064,7065],{"id":970,"depth":32,"text":971},{"id":1141,"depth":32,"text":853,"children":7011},[7012,7013,7014,7015,7016],{"id":1155,"depth":233,"text":1156},{"id":1207,"depth":233,"text":1208},{"id":1268,"depth":233,"text":1269},{"id":1843,"depth":233,"text":1844},{"id":1930,"depth":233,"text":1931},{"id":2015,"depth":32,"text":864,"children":7018},[7019,7020,7021,7022],{"id":2044,"depth":233,"text":2045},{"id":2135,"depth":233,"text":2136},{"id":2163,"depth":233,"text":2164},{"id":2245,"depth":233,"text":2246},{"id":2294,"depth":32,"text":875,"children":7024},[7025,7026,7027,7028,7029],{"id":2327,"depth":233,"text":2328},{"id":2375,"depth":233,"text":2376},{"id":2865,"depth":233,"text":2866},{"id":2909,"depth":233,"text":2910},{"id":2987,"depth":233,"text":2988},{"id":257,"depth":32,"text":885,"children":7031},[7032,7033,7034,7035],{"id":3121,"depth":233,"text":3122},{"id":3225,"depth":233,"text":3226},{"id":3408,"depth":233,"text":3409},{"id":3695,"depth":233,"text":2988},{"id":3846,"depth":32,"text":3847,"children":7037},[7038,7039,7040,7041,7042,7043,7044,7045],{"id":3961,"depth":233,"text":3962},{"id":4152,"depth":233,"text":4153},{"id":4182,"depth":233,"text":4183},{"id":4228,"depth":233,"text":4229},{"id":4259,"depth":233,"text":4260},{"id":4298,"depth":233,"text":4299},{"id":4359,"depth":233,"text":4360},{"id":4431,"depth":233,"text":4432},{"id":4455,"depth":32,"text":895,"children":7047},[7048,7049],{"id":4730,"depth":233,"text":1931},{"id":4901,"depth":233,"text":4902},{"id":4962,"depth":32,"text":905,"children":7051},[7052,7053,7054,7055],{"id":5075,"depth":233,"text":2988},{"id":5290,"depth":233,"text":5291},{"id":5414,"depth":233,"text":5415},{"id":5506,"depth":233,"text":5507},{"id":5666,"depth":32,"text":5667,"children":7057},[7058],{"id":5746,"depth":233,"text":1931},{"id":5793,"depth":32,"text":5794,"children":7060},[7061],{"id":612,"depth":233,"text":6331},{"id":159,"depth":32,"text":6390},{"id":6444,"depth":32,"text":1931},{"id":6478,"depth":32,"text":6479},{"id":6500,"depth":32,"text":6501},"md",{},[7069,7070,7071,7072,7073,7074,7075],"engineering\u002Fsystem-design\u002Fagentic-platform","engineering\u002Fsystem-design\u002Fagentic-platform\u002Fcontract","engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fchannel-gateway","engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Ffront-door","engineering\u002Fsystem-design\u002Fagentic-platform\u002Fruntime\u002Fdefinitions","engineering\u002Fsystem-design\u002Fagentic-platform\u002Fruntime\u002Fexecution","engineering\u002Fsystem-design\u002Fagentic-platform\u002Fproducts\u002Fhuman-review",{"title":250,"description":251},"engineering\u002Fsystem-design\u002Fagentic-platform\u002Finterfaces\u002Fsurfaces",[253,254,24,255,256,257,258,217,64],"XLe1GAbNh37byxhU3rhsIZ2kbCP5rb41sjRub14mwP4",1788650190256]