Skip to content

Design decisions ​

The architecture decision records: one page per decision that shaped Switchboard, with the context it was made in, what was decided, what that costs, the alternatives that were rejected, and the named pattern the decision instantiates so a newcomer can map the code to a concept they already know in one lookup.

A record is written once and never edited in place; after acceptance it may only grow by a dated ## Amended section that re-evaluates the decision. When a decision stops holding, a new record supersedes it and the old one keeps its reasoning with status: superseded and a pointer forward. That is checked, not hoped for: npm run decisions:check (part of verify) fails the build when a record lacks a status, a superseded record names nothing, or an accepted record's body differs from the copy on main other than by such an appended amendment. The table below is generated from the records' own frontmatter by npm run docs:gen.

Statuses: proposed (written, not yet agreed), accepted (agreed, being built), implemented (in the code the record describes), superseded (replaced by the record it names).

Retire native work after a run ends extends the existing isolation controls to close leftover work after a run ends and preserve unknown outcomes.

Cancel deployment checks before upload is allowed binds a deployment check to its original fence and keeps cancellation separate from sealed upload readiness.

Retain original native timeout ending proof keeps the original process ending before journal removal so an interrupted check can retire without running again.

#DecisionPatternStatusDate
0001Every boundary is an interface with at least two implementationsPorts & Adaptersimplemented2026-09-08
0002The dispatcher is the only place an agent run startsRegistry with orchestration outside itimplemented2026-09-08
0003Slack is reached over Socket Mode, outbound only, and the gaps it leaves are recovered on reconnectDurable record outside the processimplemented2026-09-08
0004Every identity is a platform-namespaced idNamespaced principal identifiersimplemented2026-09-08
0005Model and effort resolve through the same configuration layersLayered configuration resolutionimplemented2026-09-08
0006A run has two lives, a live registry and then a history record, behind one read serviceTwo stores, one facadeimplemented2026-09-08
0007Authorization is one policy table over a closed condition vocabulary, asked once per requestRules tableimplemented2026-09-08
0008A command is defined once and every surface is derived from itRegistryimplemented2026-09-08
0009Residents hold their own GitHub credential; the bot never holds a repo-write tokenTrust boundary per planeimplemented2026-09-08
0010Every model output is a typed contract, normalized once at the answer boundaryStrategy per output typeimplemented2026-09-08
0011One live run per thread; a follow-up steers the run or is refused, never queued as a second runAdmission controlimplemented2026-09-08
0012Slack itself is the record of what was handled; every reconnect replays what it never sawExternal system as durable recordimplemented2026-09-08
0013A live run page is opened by an unguessable per-run token; finished runs are read by the policy tableCapability-based securityimplemented2026-09-08
0014The dashboard runs under a Content Security Policy that executes no inline scriptDefense in depthimplemented2026-09-08
0015Releases deploy from CI in one fixed Worker order, and a deploy is done only when the new code is liveDerived, not declaredimplemented2026-09-08
0016One long-lived bot process plus Durable Objects for what must survive it, not a serverless runtimeState outside the processimplemented2026-09-08
0017Memory is off by default, byte-identical to absent when off, advisory when on, and gated on the way inNull Objectimplemented2026-09-08
0018Capabilities are computed once at startup and every off-state is a Null ObjectFeature toggles resolved once + Null Objectimplemented2026-09-08
0019A run outlives the container through a leased, fenced ledger with a write-ahead step recordLease with a fencing tokenimplemented2026-09-08
0020A span is the one measurement primitive; every duration a user sees falls out of itExecute Around Methodimplemented2026-09-08
0021Records are immutable and specs are checked; documentation drift is prevented by CI, not disciplineTwo kinds of documentimplemented2026-09-08
0022The public tree carries no imprint of the company that grew it, and a ratchet holds that lineRatchetaccepted2026-09-08
0023Cloudflare is the one supported production target; docker compose is the local loopOne way to do itaccepted2026-09-09
0024The dispatcher is a pipeline of stages, split one file move at a timePipeline (Pipes and Filters) with Interface Segregation, by Tidy Firstsuperseded → 0025-dispatch-pipeline-as-built.md2026-09-09
0025The dispatch pipeline as built, and the 800-line cap that keeps it that shapePipeline (Pipes and Filters) with Interface Segregation — the as-built recordaccepted2026-09-09
0026A run is a capability profile over three axes, named agents become presets, and a routing stage picks the profileStrategy as data + a routing filter in the pipeline; the policy table keeps one question, the config layers gain an intersecting setting, the authorize stage gains a gateaccepted2026-09-09
0027Published images are copied into each installation's Cloudflare registry once per version, never pulled at runtimeArtifact promotionaccepted2026-09-09
0028What a container image installs is a manifest per image class, rendered into the committed Dockerfile and extended by an operator overlay that deploy images buildsGenerated artifact (gen + check) over a declarative manifest; artifact promotion for the operator overlayproposed2026-09-10
0029Durable Objects are the store and never the scheduler, Cloudflare Workflows schedules the multi-step lifecycles, and the agent loop stays in a containerDurable execution for the lifecycles around the loop, never for the loop; one order for four converging tracksaccepted2026-09-10
0030deploy all copies the images it needs into the account registry itself, over HTTPS, and the operator's whole deploy is one commandArtifact promotion, folded into the one deploy commandaccepted2026-09-10
0031The coordinator runs a plan, not a pull request; a child is handed a contract and hands back a deviation; verification cannot be weakened without its specPlanner over workers with one policy table per child; specs as the contract both ways; deterministic guards on the review gateaccepted2026-09-11
0032pi is the harness for every preset, and the native turn loop retires in a replacement series that never leaves two harnesses in steady stateStrangler fig over a language-server shape; the loop is a separate process driven over a line protocol, the editor keeps the policy, the record and the UIproposed2026-09-13
0033Run artifacts move by reference through one private R2 bucket — containers and the bot's Worker move the bytes over URLs the bot signs, in both directions, and the run record keeps the keyPresigned-URL upload (a browser writing to S3 while the app server only signs), applied to both directions of a run, with the run record as catalogue and access listimplemented2026-09-14
0034One agent does a unit of work; a run continues its thread's transcript; a child run is a reader; only a state machine sits between agentsSingle-threaded agent per unit of work, the session as the unit of context; coordinator as a state machine, never a model; fan-out only to readerssuperseded → 0068-one-agent-per-unit-a-run-continues-its-threads-transcript-and-the-unit-is-the-reading-unit.md2026-09-14
0035A session's log outlives its runs in one searchable object; a run seeds from its notepad and the log's tail and recalls the rest; compaction is a pointer, never a lossAppend-only log with a search index as the store of record; a working-notes document beside it; the context window as a cache over bothaccepted2026-09-14
0036The router offers every command and the ship loop through one front door; coding leaves the table; a repository's auto-merge setting no longer refuses shipOne menu derived from two registries (the router is an MCP client of the command registry); structural opt-outs on both registries instead of prompt rules; adoption over refusal at ship's entryaccepted2026-09-15
0037A linked thread is quoted, not joined — the bot reads what it was invited to, the policy table decides who may point at it, and the text never enters the conversation the parsers readConfused-deputy avoidance by grant-on-the-resource (Teams resource-specific consent, Anthropic's Slack app's bot-membership gate) plus an untrusted-data fence (the runs tool's existing wrapper) instead of per-user permission mirroringimplemented2026-09-15
0038The harness is a six-clause contract the bot holds a process to; pi is its one implementation; a second harness enters as a conformance row before it enters an imagePorts and Adapters with one production adapter and a second adapter proven against a fake of its protocol and never shipped; a contract test (the conformance suite) runs against both; the container-runtime-interface shape, where the kubelet keeps the record and the runtime keeps its own stateaccepted2026-09-15
0039The front door writes nothing from prose and never routes twice; a routed write is handed back by its effect, a failed command asksFail-stop over fallback; a decision derived from a field the definition already carries, never a hand-kept listaccepted2026-09-15
0040The front door knows the data sources a run can reach; a question one of them answers routes to the least capable preset that receives itA third registry read as facts, never as tools; the rule in the cacheable half, the per-caller list in the per-message halfaccepted2026-09-15
0041The settings page is a surface over the command registry and configures the shared tiers; personal settings stay where the identity isAdapter over the registry (no second write path); the null tier for an identity the store cannot map; an allow-list projection instead of a filtered secretaccepted2026-09-16
0042A dashboard session is the person its email names; the link is identity, not authorityIdentity resolved once in the adapter, never authority; a set-valued self; promotion by re-issue, never by copying a secretaccepted2026-09-16
0043The home page is a chat; the browser is a channel adapter; a turn is a run; the conversation is the thread's records; delight is motion over surfaces that already existA fifth implementation behind the channel seam, never a special case; the store of record reused as the conversation store; one view model for the live and the finished; the surface's baseline decided by who the surface admitsaccepted2026-09-16
0044A routed write is confirmed in proportion to its blast radius; the definition declares it, the org floors it, the channel asks with one click, and a test run never asksA decision derived from a field the definition carries and fenced by the conformance suite; a boundary axis that intersects toward caution; one core object every channel adapter rendersaccepted2026-09-17
0045The costs snapshot archives the days it saw whole, so the page outlives the providers' retentionA write-behind archive keyed by the natural period, fed by the cache refresh that already runs; the writer decides finality, the store only upserts; the same pure builders over merged rows; coverage said, never inventedproposed2026-09-17
0046A budget is a lease carved from its parent, and one module proves the leases fitA single arithmetic module owns every wall clock as a lease carved from the parent's remainder minus the floors of every round that must follow; the lease covers everything a run does; one fit is asserted at verify, at config load and at the fork; a lease is sized by the infrastructure that must outlive it, a grant by the person; renewal opens a new segment from a recorded headaccepted2026-09-17
0047An outside fact finds the thread that owns the work and lands as its owner's turnOne inbox per conversation, keyed by the artifact it owns; every outside event is filed by reference and read under the owner's authority, never the sender'sproposed2026-09-17
0048A cold run's only GitHub credential is its run bearer; the bot's git door, on its own hostname, exchanges it per request and enforces the repository and the refThe model proxy's design applied to a second upstream; a per-run capability token exchanged at a policy-enforcing boundary for the real credential, which never crosses itproposed2026-09-17
0049A stored schedule is a turn the minute tick fires, claimed under a compare-and-swap, dispatched as its own actor, and refused when it would writeTimers are one tick plus arithmetic, never a scheduler per schedule; a scheduled message is an ordinary turn whose actor is the scheduleproposed2026-09-17
0050The PR body is a fixed-size map for the reader, and everything for agents sits below a foldThe human part of a PR description is a capped map (a TL;DR, the why, at most seven linked pointers, the feedback wanted, the risk, one line of verification) whose size is enforced by the schema and does not grow with the diff; pointers are inline links, never embedded code; decisions, proofs and notes for agents render collapsed below the map; anchors stay mechanical and render at the headaccepted2026-09-17
0051A thread has one owner for its life, a message is one event in a mode the sender chooses, and a pipeline idles instead of endingThread as session with a single durable owner (Anthropic Managed Agents' idle/running session, Capy's one agent per thread); the owner outlives its runs, a pipeline unit idles at its lease or its cap like a session at budget_reached, and a plain message steers the owner and a stop followed by a message interrupts it, uniform across agents and never a typed word; the router is a session-start concern onlyaccepted2026-09-17
0052A run resolves one model card, and every control is decided against it before the first callOne resolved capability record per run, layered operator over registry over wire defaults, written into every harness and onto the run's record, so a control is native, degraded by name, or refused before the first call, and a conformance matrix asserts the declaration against the wireproposed2026-09-17
0053Viewing as a person borrows their ceiling and keeps your name on the lineNarrowing by intersection (the existing on-behalf-of relation), a read-only door, identity never delegated — an admin sees what a person sees and writes nothing as themaccepted2026-09-17
0054A refusal the person caused is one question with a best guess; the button and the next message both answer it; every refusal has exactly one way to existOne typed outcome for every refusal, rendered in one place, fenced structurally; the cause decides whether the bot asks or reports; an answer re-dispatches a request, and only a button showing the line runs a writeaccepted2026-09-17
0055A unit has one thread, because isolation is already the session's; a round's verdict joins the review with the checks at its headThe thread is the surface a unit is read on; the session (thread and agent) already isolates the transcript and the worktree is released at every run's end, so nothing needs a second thread; a failed check is a finding of the round, never a refusal at the dooraccepted2026-09-17
0056The dashboard navigates in place; a page is one view, one seed, two encodingsContent negotiation on the existing resource (one view, two representations) instead of a second API; the router as the seed's loader; a keyed scope that keeps the pages unchangedaccepted2026-09-17
0057The operator is the one door; a model binds every chat input from the thread's session, and deterministic code authorizes, fences and executes but never interpretsOne interpreter per surface; the typed registry projected into a model's tools; the thread as the memory; determinism at the boundaries (authorization, class over the bound input, fence, execution), judgement in the middle; a replay row as the release gate for every prompt changeproposed2026-09-18
0058A thread reply is read before it is answered; a cheap gate ahead of the door decides whether the bot was addressed, and that verdict survives a restartA two-tier gate (a cheap decision ahead of the expensive door, the shouldReply pattern) with silence as a legal outcome; the verdict claimed once per process and written first-writer-wins beside the run ledger so a replay reads the decision instead of re-deciding it; the mention as the always-on overrideimplemented2026-09-18
0059A deploy drains the resident fleet — admission closes to new runs, the runs in flight end, the swap lands, admission reopensDrain-then-swap, the bot's own SIGTERM drain moved one level up to the fleet; one record with an end in the registry every attach already passes; the run in flight is told from the new one by the registration it already holds; the deployer waits on the count it already reads; every wait names its endproposed2026-09-18
0060A ship pipeline is a live run for its whole life, so every channel that can open a thread runs it and every surface is a projection of one recordOne store, many projections (the run record is the truth; Slack's card and the web's turn render it); a hosted run with no process, kept alive by the heartbeat every run has and claimed under a key of its own so it occupies no thread; capability over name at the channel seamimplemented2026-09-18
0061Memory keeps lessons, not status; a deterministic gate on every fact, a repository's newest lessons in every run, and a restated lesson merges instead of multiplyingOne rule at two sites (a gate on the way in, a sweep over what is already there); upsert by meaning (a restatement bumps the record it restates); a recency window in place of a search for the scope whose lessons are all relevantproposed2026-09-18
0062The requester authors the commits, the bot commits and opens them, and a run keeps the requester who started itGit's own author/committer split applied to agents (identity in the author field, mechanism in the committer); a trusted binding written by a named grant or by the provider's own link, never self-claimed; identities normalized by the mechanism at the boundary where it acts, over only the commits the run madeproposed2026-09-18
0063Every finished run writes one metrics point to Workers Analytics Engine, and a metrics page reads the trend from itObserver at the one commit point (the sink hangs off the write every final record already makes); a flat fact row with closed vocabularies (a star-schema fact, never text); a seam for the sink and one for the reader so a second store can join laterproposed2026-09-18
0064The plane owns every run's state and the bot is one of its clients; a refusal becomes a queue position, an ending is judged by the ledger that saw it, saturation is a checkpoint steer, and a release is a quiet window a person closesControl plane over a data plane (the bot executes, the plane decides); an event-sourced decider, a pure function from state and event to state and effects, inside the single-writer object that already holds the truth; a transactional outbox whose effects ride the calls the clients already make; a process manager whose only lever on live work is a message at a turn boundary; watches evaluated on edges, with levels re-stated at every boot so a lost edge heals without a resyncproposed2026-09-19
0065A hosted pipeline run is an orchestrator, so every surface reads its standing from one fold of its own events and never from a model run's signalsOne pure fold, many projections (the parent's ship events folded once into a standing that the index row, the run page and the card all render); null object for a model run's signals on a run that has none; progressive disclosure (the pipeline row first, its runs behind a caret)proposed2026-09-19
0066A user meets twelve nouns and no others; the vocabulary is a reference page bound to the code, and the consistency check fails a user surface that prints an internal wordUbiquitous language published as one glossary rendered as a generated region from a typed list that imports the code types carrying it; an anti-corruption layer at every user surface (internal words are translated at the boundary, never printed); a ratchet whose recorded baseline only shrinks, driven to zero as the acceptance gateaccepted2026-09-19
0067One seam for a structured answer — a model asked for one structured answer is forced onto its tool, the caller's pure parser accepts or names the violation, a violation is re-asked with the violation named, and after bounded retries the caller's declared floor holds, never a refusal shown to the personParse, don't validate (a pure parser per caller returns the value or names the violation); retry with feedback (the violation quoted back to the same model as a user turn), bounded by a named constant with every attempt on the record; a declared floor per caller as the null object — the seam degrades to it, never to an error a person readsproposed2026-09-19
0068One agent does a unit of work; a run continues its thread's transcript; a spawned child is a reader; the unit is the reading unitSingle-threaded agent per unit of work, the thread's transcript as the unit of context; a state machine between agents, never a model; fan-out only to readersaccepted2026-09-19
0069The one door has one execution path — after the operator decides, a registry command runs through the class ladder or is offered as one click, a preset runs the request through the route, every violation is re-asked at the seam, every disagreement floors to the route, and nothing on a chat surface is ever handed back as text to retypeOne decision table owned by one module (bind kind × surface → run / click / route / re-ask / refuse), asked by every caller and rendered by none of them; the seam as the only re-ask and the route as the only floor; a rendered line to retype made unrepresentable on a chat surface, so the defect class cannot recur site by siteaccepted2026-09-20
0070The control plane is where the maintainer works — the /plane page pins a chat column beside the panels, and an orchestrator thread the person keeps talking to answers questions about the fleet and acts on itChatOps over one command registry — the chat is a surface, never a second write path; one long-lived conversation per person as the working context; every read answered from one projection (the plane's tables), every write through the one fenced door; two deciders split by input kind — rules to the machine, a person's words to the threadaccepted2026-09-19
0071A ship unit owns its pull request until the pull request is merged — merge-ready waits on facts, a DIRTY head buys a rebase round, and anyone's merge ends the unitThree mechanisms in place of one always-on loop — every push is preceded by a rebase onto the fetched base; a sweep command a person runs rebases the pipeline's open pull requests with a two-rung resolver (git alone with the repository's own merge drivers and rerere, then one bounded model round reading the repository's AGENTS.md); and watch-until-merge is a per-repository setting, off by default, reacting to push-to-base webhooks under a rebase-in-flight cap and a spend limitaccepted2026-09-20
0072A run has one live state, owned by the server — a closed set from admitted to ended, one event the moment it changes, one projection and one wording function every surface readsState machine as the one source of truth — the server assigns each run's live state from a closed union, appends a state event to the run's log when it changes, projects {state, since, bound, detail} onto the run summary, and one wording function beside the ending words renders it everywhere; the seven rival derivations are deletedaccepted2026-09-20
0073The ship pipeline dissolves into the orchestrator — the unit machine is the deterministic atom, judgement composes units, and the pipeline graph retiresAgentic orchestration over deterministic unit workflows — coding → review → fix → checks → merge remains one leased, single-owner state machine; an LLM loop reads each durable ending and chooses the next bounded act, while DAG planning, recovery directives and person-managed workflow columns retiresuperseded → 0090-one-unit-lifetime-keeps-effects-and-settlement-under-existing-owners.md2026-09-21
0074A side effect crosses one typed seamCommand–query separation at the runner boundary — a child asks for an effect with a typed command; the runner validates current facts, performs the effect, and returns a typed receipt or refusalaccepted2026-09-21
0075One typed provider-failure cause decides every model call and every surface renders it onceClassify provider answers once at the provider boundary; consumers act on a closed cause and people see one cause-owned sentence, never provider proseaccepted2026-09-22
0076A verdict and its approval belong to the head they reviewedHead-bound review subscription — a posted verdict creates one durable watch on the pull request; a substantive head move invalidates the verdict and approval, emits one idempotent update per surface, and renews both through a review pinned to the new headproposed2026-09-22
0077A plan becomes a durable work record before code startsStore-canonical plan — one source-neutral front door pins a validated artifact; a provider URL addresses its human projection; execution composes plan, code and review from the pinned copyaccepted2026-09-22
0078Request context, operation target, publication destination, and evidence references are independent factsImmutable typed request envelope — request acceptance preserves context and initiator, operation semantics select each target and publication destination, and authorized evidence can inform but never retarget an effectproposed2026-09-23
0079A person outlives the surfaces they useVerified external identities resolve to a durable person; each request retains its actor, authority and surfaceproposed2026-09-25
0081Account linking proves both identities and revocation fences their useDual authentication with a one-time intent; transactional binding and revision-fenced authorizationsuperseded → 0104-account-operations-require-fresh-proof-and-preserve-native-ownership.md2026-09-26
0082An explicit PR batch may start exact Ship children—accepted2026-09-29
0083Context keeps its original sources across conversations and child workInformation-flow control and immutable snapshotsaccepted2026-10-01
0084Execution images follow their build inputs across Worker releasesContent-addressed deployment artifactaccepted2026-10-01
0085A cold publication controller is a fresh sandbox, not an inspectorOne-use isolated effect domainaccepted2026-10-02
0086A deploy fence separates execution from workspace retentionDurable run fence and exact owner readaccepted2026-10-02
0087An advertised run keeps one identity through setupOne run from reservation to finishaccepted2026-10-03
0088Review selects verified defects before applying severityEvidence filter followed by severity policyaccepted2026-10-03
0089Shipped and installed agents use one configuration languageDeclarative profiles with layered configurationaccepted2026-10-03
0090One unit lifetime keeps effects and settlement under existing ownersSingle-writer state, compare-and-swap reservation, transactional outbox and retained finalization obligationsproposed2026-10-04
0091Bound response validation off the gateway event loopBulkhead isolation and backpressuresuperseded → 0096-keep-the-proxy-thin-and-decode-once-in-the-harness.md2026-10-05
0092Consumers read configuration owned by their immutable image identityImmutable identity, explicit input witnesses and conditional publicationproposed2026-10-05
0093An inferred command needs a semantic fulfillment verdictOne bounded interpreter with a separate admissibility judgmentaccepted2026-10-07
0094Hard stop is a negative resolution independent of startup confirmationFenced cancellation and retained evidenceaccepted2026-10-08
0095Identity allocation is separate from active workSeparate identity and resource budgetsaccepted2026-10-08
0096Keep the proxy thin and decode once in the harnessThin streaming proxy and bounded full-response concurrencysuperseded → 0098-bound-stream-concurrency-separately-from-parsing.md2026-10-09
0097Every terminal run row carries its outcome and its workspace, and the review dashboard is grouped queries over themWide event (one fact row per terminal run, every metric a query over it) with closed vocabularies named after the OpenTelemetry semantic conventions; classification as a report-side mapping, never a stored judgementproposed2026-10-09
0098Bound stream concurrency separately from parsingBounded concurrency and FIFO backpressureproposed2026-10-09
0099One confirmation policy for every command surfaceOne command policy with thin consent adaptersaccepted2026-10-09
0100Ordinary MCP confirmation uses the client formOne consent policy with native host inputaccepted2026-10-10
0101Native operation recovery requires the original process receiptOriginal-owner journal and compare-and-swap settlementproposed2026-10-11
0102The browser cutover keeps the current execution engineReplace presentation and shared orchestration while preserving execution ownersaccepted2026-10-10
0103Context is a bounded view of source-bound workDurable source records with scoped working projections and acknowledged phase artifactsproposed2026-10-11
0104Account operations require fresh proof and preserve native ownershipOperation-bound dual authentication with independent native authorityaccepted2026-10-10
0105Rebuild modes and exact operation termination share existing recoveryExplicit destructive mode and original-runtime process capabilityproposed2026-10-11
0106Terminal execution and replay fencing commit togetherDurable journal and atomic completion barrierproposed2026-10-12
0107Retain exact review feedback with the publication ownerTransactional receipt and replaceable snapshotaccepted2026-10-14
0108Staging successors preserve consumed episodesCompare-and-swap episode pointer and immutable receiptsproposed2026-10-15
0109Follow-ups keep a continuation checkpointSnapshot and bounded log projectionproposed2026-10-16

The dated implementation plans under docs/plans/ are records of the same kind — proposals that were reviewed, built, and then frozen with their final status — and are held to the same check. They are working documents for the repository and are not published on this site.

Recorded check inspection proposal preserves original-operation identity for read-only inspection and bounded receipt-based retirement.

The resident replica placement proposal separates thread placement from per-run containment and records the migration boundary for several warm residents of one repository.

The proposed shared message handling design routes external agent messages through the orchestrator. Its implementation plan lists changes, effort and proof for the shared core, thin GitHub adapter and review-head continuation.

The proposed source input delivery contract separates the first orchestrator notice from an internal fold. It defines proof that a runtime accepted an input, current source access and retention of unresolved input before producers can be enabled.

The native launch and hard-stop design records per-run isolation, descendant termination and cancellation before VM startup. Its early setup amendment describes stop failures before a runtime is selected.