adr

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

README

Architecture Decision Records

This directory records the significant architectural decisions made on Atlas, using the MADR-influenced format described in template.md.

An ADR captures a decision, the context that forced it, the options considered, and the consequences accepted. ADRs are immutable once accepted: if a decision changes, a new ADR supersedes the old one rather than editing it.

A number belongs to exactly one decision, and it is assigned when the record lands on main — never on a branch. go test ./docs/adr enforces unique, gapless numbers, keeps the index below in step with the directory, and checks that every ADR-NNNN citation anywhere in the repository still resolves. The index is a table of contents: one row per record, carrying that record's own # ADR-NNNN: heading, so what the decision says belongs in the record, not in the cell.

Writing a record

Do not pick a number. A record in flight carries none:

  1. Copy template.md to docs/adr/draft-<slug>.md — a kebab-case slug, no number.
  2. Keep the heading as # ADR-DRAFT: Your title, and fill in the record.
  3. Add no row to the index below.
  4. Cite it as ADR-draft-<slug> from code comments and docs, or link draft-<slug>.md.

When the PR merges, a workflow on main runs make adr-number. That renames the file to NNNN-<slug>.md, rewrites the heading, appends the index row, and rewrites every citation of the draft to the number it just got. Nothing for you to remember; you can also run make adr-number by hand on main.

Why the ceremony: the number used to be taken when the record was written, which is the earliest possible moment and the one with the least information. Two open branches both saw the same "next free" number and both took it — that is how 0090, 0103 and 0105 came to be shared by unrelated ADRs (the later record of each pair now lives at 0139, 0140 and 0141). Once a test caught the collision, the cost became a renumber on every merge instead: one record walked 0164 → 0169 across six of them without a word of its content changing. Assigning the number where the question has one answer removes both. The full argument is in the record on merge-time numbering.

A number, once assigned, is never reassigned — that is what makes (ADR-0168) in a comment safe to write.

When the record rests on a question you could not answer

Some decisions are right while something we do not know stays unknown. ADR-0292 is the case that prompted this rule: it renders a MIMWAL table's cells by position because the meaning of the columns is established by no reference anyone could find, and it would be re-argued the day one turns up. Written like any other record, that reads a year later as settled — nothing in it separates "we decided this" from "we decided this for now, on a gap in what we know".

So say both, in the front matter:

- **Open question:** MIMWAL's grid column semantics are not established from its own
  source. The positional cells rest on that gap.
- **Question checked:** 2026-09

The two come as a pair, and go test ./docs/adr insists on it: a question with no date cannot go stale, and a date with no question says a thing was checked without saying what. The question may wrap onto indented lines. The date is the month somebody last looked at the question, not the month the record was written, and the guard fails once it has stood for a year — the same interval, for the same reason, as the Worker Type setup steps of ADR-0289.

The reasoning behind the pair — including why it states a month somebody looked rather than a deadline somebody set — is in ADR-0293.

When the check fires, the fix is never to bump the date. Go and look: if the question now has an answer, delete both lines and check whether the decision that rested on the gap still holds — that is the case this exists for. If it is still open, write down what you learned and date the month you looked.

Reading the older records: "connector"

Records written before ADR-0203 say connector where the rest of the documentation now says Worker Type (an execution capability), Worker (one configured target and identity of that type) or Worker Instance (a process leasing its jobs) — the word meant all three depending on where it stood, which is why it was replaced.

Those records are not rewritten: an ADR is immutable once accepted, and ADR-0203 says so explicitly — older connector wording is reconciled through links, not by editing history. So read ADR-0036, ADR-0041, ADR-0067, ADR-0154/0166/0172/0173/0201 and their neighbours in their own vocabulary; ADR-0203 carries the mapping. The persisted contracts kept the old spelling too, on purpose: the connector/ package paths, the connector="…" BPMN attribute, atlas worker --connector, the ATLAS_*_CONNECTORS variables and the /api/v1/connectors routes.

Index

ADR Title Status Implementation
0001 Event sourcing and log-structured state Accepted Landed
0002 Single-writer partition model Accepted Landed
0003 Pebble as embedded state store Accepted Landed
0004 Compile BPMN to an integer-indexed graph Accepted Landed
0005 Group commit and fsync strategy Accepted Landed
0006 Partition routing and cross-partition communication Accepted Partial
0007 Job worker protocol Accepted Landed
0008 FEEL expression compilation strategy Accepted Landed
0009 Record serialization format Accepted Landed
0010 Go as implementation language, no CGO Accepted Landed
0011 Single-binary distribution with an embedded web viewer and editor Accepted Landed
0012 A buildless, self-contained web UI app shell Accepted Landed
0013 Embed the bpmn-js modeler as a vendored asset Accepted Landed
0014 DMN business rule tasks via the temis engine Accepted Landed
0015 Reuse the external FEEL engine behind an expr boundary Accepted Landed
0016 Model Context Protocol server as a stdio adapter over the HTTP API Accepted Landed
0017 Retain finished process instances in a history index Accepted Landed
0018 Test-driven development as the default workflow Accepted Landed
0019 Durable deployments via an on-disk sidecar store Accepted Landed
0020 Message events and correlation Accepted Landed
0021 Diagram drafts, separate from deployments Accepted Landed
0022 Retain a per-element token-visit history for the Operations overlay Accepted Landed
0023 Collaborations and pools as multi-process deployments Accepted Landed
0024 Parallel gateway join synchronization Accepted Landed
0025 Extend the hand-written properties panel instead of vendoring bpmn-js-properties-panel Proposed Partial
0026 A Problems panel with validation targeted at an engine version Accepted Landed
0027 Element templates for pre-configured, reusable elements Proposed Partial
0028 User tasks, forms, and the Tasks app Accepted Landed
0029 Public process start via a published form link Accepted Landed
0030 Play mode — ephemeral in-Modeler process simulation Accepted Landed
0031 Diagram version history in the Modeler Proposed Not started
0032 In-Modeler AI copilot over the MCP/HTTP surface Proposed Partial
0033 Inclusive gateway join synchronization Accepted Landed
0034 Projects as containers for heterogeneous artifacts Accepted Landed
0035 Message start events and the processInstanceKey built-in Accepted Landed
0036 A clio connector — server-registered event-store integration Accepted Landed
0037 Structured JSON variables Accepted Landed
0038 Collaboration message-flow replay Accepted Landed
0039 Input/output variable mappings for business rule tasks Accepted Landed
0040 Boundary events — timer and message, interrupting and non-interrupting Accepted Landed
0041 Connector management and the secret store Accepted Landed
0042 User-task runtime assignment and claim/unclaim Accepted Landed
0043 An OpenAPI spec and an embedded API explorer for the HTTP API Accepted Landed
0044 User management and the authentication boundary Accepted Landed
0045 Binding user-task assignment to real identities Accepted Landed
0046 Single-process step-by-step replay Accepted Landed
0047 Polyglot script tasks (PowerShell, …) via job workers Accepted Landed
0048 Per-step variable snapshots in the single-process replay Accepted Landed
0049 Internal service authentication for the in-process MCP adapter Accepted Landed
0050 Central DMN decisions via a temis decision connector Accepted Landed
0051 Timer start events (duration, date, cycle) Accepted Landed
0052 Message end events Accepted Landed
0053 First-class data objects — typed, event-sourced state, and lineage Accepted Landed
0054 Date and cycle timers for catch and boundary events Accepted Landed
0055 FEEL-expression timer schedules for catch and boundary events Accepted Landed
0056 FEEL cycles, and FEEL on timer start events Accepted Landed
0057 First-class FEEL temporals for timer schedules Accepted Landed
0058 Data output associations — write a value and transition a data object's state Accepted Landed
0059 Data input associations — read a data object into an activity Accepted Landed
0060 Field-level data object writes — set one member of a structured object Accepted Landed
0061 Incident model — job-failure incidents, raise, resolve, resume Accepted Landed
0062 An embedded DMN editor (dmn-js) Accepted Landed
0063 DMN decision binding (latest vs deployment) Accepted Landed
0064 Timer FEEL-failure incidents — park and raise instead of firing immediately Accepted Landed
0065 Multi-token process replay and causal token lineage Accepted Landed
0066 Durable decision-evaluation records for debugging Accepted Landed
0067 A service-task connector catalog, and REST with a model-authored endpoint Accepted Landed
0068 Task input/output variable mappings with activity-local scopes Accepted Landed
0069 An engine-internal encrypted secret vault (ADR-0041 option A3) Accepted Landed
0070 The secret vault is on by default, with a generated key Accepted Landed
0071 Sharing scopes — private and shared access boundaries for design-time work Accepted Landed
0072 Multiple DMN models per process deployment Accepted Landed
0073 A principals directory for member and assignee pickers Accepted Landed
0074 Embedded subprocesses (scope lifecycle via child counters) Accepted Landed
0075 A clio inbound event bridge — at-least-once ingestion with engine-side idempotent delivery Accepted Landed
0076 Call activities (single-partition) Accepted Landed
0077 Multi-instance activities (parallel and sequential) Accepted (amended) Landed
0078 Design-view token simulation — a client-side control-flow walkthrough Accepted Landed
0079 An outbound mail connector (SMTP first) Accepted (amended) Landed
0080 Sublinear runtime views via maintained aggregate counters Accepted Landed
0081 A community marketplace for connectors, service tasks, and script tasks Proposed Partial
0082 Event subprocesses (message- and timer-triggered, interrupting and non-interrupting) Accepted Landed
0083 An O(1) instances summary — per-definition finished-count and last-activity counters Accepted Landed
0084 CSV batch validation — upload a file, validate every row against business rules, correct the failures Accepted Landed
0085 Process-instance TTL — self-cleaning via the due-timer index Accepted Landed
0086 Gateway conditions resolve over the scope chain Accepted Landed
0087 In-process CSV ingestion — upload in a user task, parse in the process Accepted Landed
0088 Signal events (broadcast throw/catch) Accepted Landed
0089 Error events (scoped propagation to the nearest handler) Accepted Landed
0090 Bulk-terminate running instances — an explicit selection and a filtered scope Accepted Landed
0091 User-task scheduling — priority and due date Accepted Landed
0092 One-click clio credential provisioning Accepted Landed
0093 Native Gmail and Microsoft Graph mail providers Accepted Landed
0094 Singleton message start — at most one live instance per correlation key Accepted Landed
0095 External variable modification on a running instance Accepted Landed
0096 Token simulation — event triggers, inclusive gateways, and an auto-decide mode Accepted Landed
0097 Token simulation — message starts, event-subprocess triggers, and multi-instance Accepted Landed
0098 Audit trail for external variable modifications Accepted Landed
0099 An ArchiMate 3.2 enterprise-architecture view Accepted Landed
0100 Token simulation — configurable multi-instance count, modelled cardinality wins Accepted Landed
0101 Token simulation — a thrown message/signal delivers to a waiting catch Accepted Landed
0102 Receive tasks Accepted Landed
0103 Compensation and compensation handlers Accepted Landed
0104 Token simulation — entering embedded subprocesses Accepted Landed
0105 Per-server call-activity target overrides Accepted Landed
0106 A BMC Remedy connector — server-registered ITSM entry creation Accepted Landed
0107 Backup and restore — a one-file download of the design-time data directory Accepted Landed
0108 BPMN transactions (cancel end event, cancel boundary, transactional compensation) Accepted Landed
0109 Whole-instance snapshot — a full backup that includes running instances Accepted Landed
0110 Event-based gateways (deferred choice) Accepted Landed
0111 Completing the incident model — retry backoff and timer-FEEL failure incidents Accepted Landed
0112 Send tasks Accepted Landed
0113 Org-wide UI brand theme Accepted Landed
0114 OpenSearch event exporter — a WAL-tailing sink, off the hot path Accepted Landed
0115 History retention — an export-gated, age-based hard delete of finished instances Accepted Landed
0116 Terminate end events Accepted Landed
0117 An AI agent task — an LLM agent as a managed connector on the job path Accepted Landed
0118 A web-scraping connector — model-authored URL + CSS selector extraction Accepted Landed
0119 Deactivating a deployed process Accepted Landed
0120 Mockup (engine-simulated) service tasks Accepted Landed
0121 BPMN lanes Accepted (Layer A) Landed
0122 A protected system project and bootstrap-deployed platform processes Accepted Landed
0123 A sanctioned automated user-provisioning path for system processes Accepted (amended) Landed
0124 Server-side BPMN diagram auto-layout in Go Accepted Landed
0125 Escalation events (non-interrupting, propagating throw/catch) Accepted Landed
0126 Self-service registration link on the login screen Accepted Landed
0127 A layered layout pipeline and executable layout invariants Accepted (amended) Landed
0128 Process applications — the project, elevated into a deployable, versioned, portable unit Accepted Landed
0129 Remote deployment targets — publish an application to another Atlas server Accepted Landed
0130 Deprecating a process version — a drain state distinct from pausing Proposed Not started
0131 Engine recovery checkpoints and WAL compaction Accepted Landed
0132 Link events (intra-scope goto — a compile-time synthetic flow) Accepted Landed
0133 Standard loop activities (the ↻ marker) Accepted (amended) Landed
0134 Git-backed applications — a repository as an application's source of truth Accepted Landed
0135 Retries as a property of every job-backed task Accepted Landed
0136 Terminated tokens in the step-by-step replay Accepted Landed
0137 Conditional events (data-triggered catch/boundary) Accepted Landed
0138 Ad-hoc subprocesses (on-demand, unordered contained activities) Accepted Landed
0139 A first-class "CSV to JSON" connector kind with model-authored layout Accepted Landed
0140 Live collaborative modeling sessions — real-time co-editing of drafts by people and AI agents Accepted Landed
0141 A SharePoint connector (create list item, via Microsoft Graph) Accepted Landed
0142 Operational metrics over a Prometheus endpoint Accepted Landed
0143 Process documentation export Accepted Landed
0144 Per-definition history TTL — retention the model declares Accepted Landed
0145 A Developer View for code-bearing fields Accepted Landed
0146 History expiry as a due-date index — retention that scales with what is due Accepted Landed
0147 Splitting the api Server object, without weakening the single writer Accepted Landed
0148 Org-wide brand logo Accepted Landed
0149 A bounded outbound-call budget for every connector Accepted (amended) Landed
0150 A preview mail provider, and incidents on the live diagram Accepted (amended) Landed
0151 Incidents beyond the live diagram — the replay, the lists, and one shared action Accepted Landed
0152 OAuth2 client-credentials for the REST connector Accepted Landed
0153 SCIM 2.0 provisioning connector Accepted Landed
0154 Generic LDAP connector Accepted Landed
0155 The Secrets panel says what a value has to be Accepted Landed
0156 In-process vs. out-of-process service tasks — where a step's work runs, and what we recommend Superseded by ADR-0164 Superseded
0157 Every side-effecting task on a worker process — atlas worker, optional supervision, and a Workers console Accepted Landed
0158 A connector reference that explains itself — and an incident you can actually resolve Accepted Landed
0159 Auditable manual task completion Accepted Landed
0160 Fix the connector from the incident Accepted Landed
0161 What an element was handed, on the diagram Accepted Landed
0162 Process instance migration Accepted Landed
0163 Deleting a connector deployed models still reference — and keeping a table inside its card Accepted Landed
0164 No in-process service tasks — the core loop must never be able to get stuck Accepted Landed
0165 SOAP / Web Services (WSDL) connector Accepted Landed
0166 Active Directory connector Accepted Landed
0167 A released connector ships in the marketplace Accepted Partial
0168 Moving a connector onto a worker — where the task detail travels, and where the credential lives Accepted Landed
0169 A form on the incident — repairing an instance with named fields instead of raw JSON Accepted Landed
0170 ADR numbers are assigned at merge, not on a branch Accepted Landed
0171 A directory-file connector — LDIF and DSML Accepted Landed
0172 A Microsoft Entra ID connector Accepted Landed
0173 Three SQL connectors, and the first kinds born on a worker Accepted Landed
0174 A connector task's input mappings are its outbound payload Accepted Landed
0175 Replicated partition cells for horizontal scale-out Proposed Not started
0176 Standards boundary and the Atlas runtime contract Accepted Landed
0177 Reload skips the deploy-time validation gate Accepted Landed
0178 Responsibility metadata — RACI on the element, with R derived from the assignment Proposed Not started
0179 A worker's job history lives in clio, not in Atlas Accepted Landed
0180 Groups as scope members Accepted Landed
0181 Mock mode for the Active Directory connector Accepted Landed
0182 Active Directory runs on a worker by default Accepted Landed
0183 The Modeler asks the server where an authored kind runs Accepted Landed
0184 Grant audit log Accepted Landed
0185 Live group membership Accepted Landed
0186 Embedding a public start form cross-origin (scoped CORS) Accepted Landed
0187 Database change events — captured in the database, read on a worker, deduplicated in the engine Proposed Not started
0188 A database is a Console entry, not a start parameter — and a worker is never a thing you create Accepted Landed
0189 Panorama architecture modeling and live operational overlays Accepted (amended) Landed
0190 Add explicit RSS and Atom extraction to the web-scraping connector Accepted Landed
0191 TLS 1.3 in the binary — an optional listener with operator-supplied certificates Accepted Landed
0192 BMC Remedy runs on a worker by default Accepted Landed
0193 The Active Directory mockup switch belongs in the Console Accepted Landed
0194 API tokens — a credential a machine can actually be given Accepted Landed
0195 Requiring a login is the default Accepted Landed
0196 The MCP transport is authenticated, and acts as its caller Accepted Landed
0197 A throttle on the login, and a security audit trail Accepted Landed
0198 The Prometheus exposition moves behind the boundary Accepted Landed
0199 Every mounted route declares its access class Accepted Landed
0200 Atlas as an OAuth resource server, so a hosted MCP client can connect Accepted Landed
0201 Atlas Jira connector Accepted Landed
0202 Atlas holds the AD mockup's starting entries Accepted Landed
0203 Worker execution model and integration terminology Accepted Landed
0204 Hosted apps — user HTML/JS served from an isolated origin Proposed Not started
0205 Who owns a connector, and who may use the events it brings in Accepted Landed
0206 Active Directory is a connector you configure, not one you write into a model Accepted Landed
0207 Package Worker Types as signed external runtime artifacts Accepted Landed
0208 Worker Type package contract, trust, and distribution Accepted Landed
0209 Roles per endpoint group Accepted Landed
0210 Federated authentication Accepted Landed
0211 Panorama's derived landscape mesh and notation projections Accepted Landed
0212 The element-template applier, and what a template binding means in Atlas Proposed Not started
0213 The mock Active Directory is visible in the Console Accepted Landed
0214 Jira as an inbound event source — a polled issue watch, deduplicated per issue Accepted Landed
0215 The Modeler Playground — batch simulation and analysis of a draft Accepted Landed
0216 Mockups are one view, not one per kind Accepted Landed
0217 A mock REST API served from an OpenAPI document Accepted Landed
0218 Jira runs on a worker by default Accepted Landed
0219 Variable write attribution Accepted Landed
0220 The Console may dial a database, and the engine still links no driver Accepted Landed
0221 A database task runs against seeded answers, not against a SQL engine Accepted Landed
0222 An artifact's id is its identity — renaming moves it, collisions are refused Accepted Landed
0223 Jira account lookup Accepted Landed
0224 A mockup run is visible, and it carries what the process bound Accepted Landed
0225 An inbound watch has an hourly budget Accepted Landed
0226 A start event is a trigger, and the one that fires is the one that starts Accepted Landed
0227 A Jira read is bounded and moves forward Accepted Landed
0228 User presence in the Console Accepted Landed
0229 The Modeler's editor bar carries two acts and a menu Accepted Landed
0230 The process information model — UML classes above BPMN's data objects Accepted Landed
0231 Structured HTML extraction, richer feed entries, and a fetch that survives the real web Accepted Landed
0232 Importing a UML class diagram — reading what somebody else drew Accepted Landed
0233 Finish ADR-0164 — in-process connector work becomes a finite list, then nothing Accepted Landed
0234 Google Sheets and Drive as inbound event sources — a polled row watch and a polled folder watch Accepted Landed
0235 Google Sheets as a Worker Type — a spreadsheet is a process data source Accepted Landed
0236 A non-interrupting message or signal boundary event stays armed Accepted Landed
0237 The class canvas on diagram-js Accepted (amended) Landed
0238 A reverse index from call activity to child instance Accepted Landed
0239 Read-only queries run off the run loop, on a consistent view Accepted Landed
0240 Variables stays on the Modeler's bar, as a pressed button with a shortcut Accepted Landed
0241 Finding an instance — a key lookup and a per-definition index Accepted Landed
0242 One route table describes the shell Proposed Not started
0243 The views are built from shared parts Proposed Not started
0244 Searchable variables — a declared value index Accepted Landed
0245 The call activity's "+" is the way into the process it calls Accepted Landed
0246 The Tasks app descends into a called process instead of navigating to it Accepted Landed
0247 An instance that is gone is still findable Accepted Landed
0248 A search term is literal, and widening is asked for Accepted Landed
0249 Cancelled tokens on the runtime overlay, and a deferred choice drawn once Accepted Landed
0250 Documentation prose is Markdown, rendered by one closed renderer Accepted Landed
0251 Adjusting a deployed definition's diagram without redeploying it Accepted Landed
0252 Runtime badges hang outside the shape, clear of its caption Accepted Landed
0253 Agent tool calls drive ad-hoc activation — the toolbox is the model Accepted Landed
0254 An agent round on a worker — the toolbox travels out, the tool calls travel back Accepted Landed
0255 An agent model is a Console Worker — the one field that is not a secret Accepted (amended) Landed
0256 The model is authored, the provider is configured — and one call is a task Accepted Landed
0257 What an agent may read is authored, the way its reach already is Accepted Landed
0258 Discord as a Worker Type — a process speaks in the channel the team already reads Accepted Landed
0259 The data object lifecycle — what the BPMN data state resolves against Accepted Landed
0260 A form is generated at design time, by the Worker an operator already configured Accepted Landed
0261 The diagram is the query — filtering instances by the element they sit on Accepted Landed
0262 Discord as an inbound event source — a channel is a log, and a snowflake is its sequence Accepted Landed
0263 The brand palette reaches the form runtime Accepted Landed
0264 A row watch's idempotency mark is its own, and the cursor is why that needs no migration Accepted Landed
0265 Signing in does not wait for the run loop Accepted Landed
0266 The runtime counts leave the run loop, and take the write paths with them Accepted Landed
0267 The console speaks German first, through a catalogue rather than a rewrite Accepted Landed
0268 Task folders are saved filters, stored as rules and generated into FEEL Accepted Landed
0269 The engine stays namespace-blind, and the deploy says so Accepted Landed
0270 A poll costs a page, not a backlog Accepted Landed
0271 A batch persists the work it still owes Accepted Landed
0272 One token may not hold the writer forever Accepted Landed
0273 A gateway that cannot route parks, it does not complete Accepted Landed
0274 The in-process runner claims what it works Accepted Landed
0275 Reading a running instance is an object question Accepted Landed
0276 A loop's size is checked before it is built Accepted Landed
0277 A join synchronizes within its own execution scope Accepted Landed
0278 Filing a deployment into a project is a write on that project Accepted Landed
0279 Topology is compiled, including the join's ancestors Accepted Landed
0280 Recovery proves its prefix or refuses to start Accepted Landed
0281 A role change takes effect on the next request, not the next login Accepted Landed
0282 One inventory of what is on disk, and what a backup owes it Accepted Landed
0283 Only the end of the active segment may be torn Accepted Landed
0284 A cancellation sees the children created in its own batch Accepted (amended) Landed
0285 A batch is one framed unit in the log Accepted Landed
0286 A list carries its own search Accepted Landed
0287 Create the worker from the incident, and run the deploy preflight on every deploy path Accepted Landed
0288 An agent's commit is authored by the person who asked for it Deprecated Not started
0289 A Worker Type carries its own setup, in the panel where it is chosen Accepted Landed
0290 A join counts tokens per incoming flow Accepted Landed
0291 One place names every resource budget, and one way sets them Accepted Landed
0292 A MIM import hands over its rows as data, and counts them as work Accepted Landed
0293 A record that rests on an open question says so, and the question expires Accepted Landed
0294 A variable is a record, a collection is a loop's harvest — two budgets Accepted Landed
0295 A migration re-indexes what its target declares Accepted Landed
0296 A loop records the element it produced, not the collection so far Accepted (amended) Landed
0297 Confine the internal worker token to the worker protocol Accepted Landed
0298 A record has two states — whether the decision holds, and whether it is built Accepted Landed
0299 What earns a Worker Type — admission criteria for a new kind Proposed Not started
0300 An OpenAPI document as element templates Accepted Landed
0301 Derive the information model from the processes that use it Accepted Landed
0302 The repository stops rewriting who authored an agent's commit Accepted Landed
0303 Isolate general-purpose script tasks inside a restrictive OS sandbox Accepted Partial
0304 A capability record says when somebody last confirmed it Accepted Landed
0305 Business capabilities and value streams as design-time records Accepted Partial
0306 A lifecycle may take its states from an «enumeration» Accepted Landed
0307 The milestone event compiles Accepted Landed
0308 Panorama binds the capability register Accepted Landed
0309 Measuring a capability Accepted Landed
0310 Read the difference between what is built and what is planned Accepted Landed
0311 The approver decides on a page of their own, in the customer's colours Accepted Partial
0312 Catalogue, order, inventory — three models, not one Accepted Partial
0313 The portal follows the browser's language; the console still does not Accepted Not started
0314 Personal data in the portal — a reference by default, a destroyable key for the rest Accepted Partial
0315 Who runs the portal — a role for the operation, a catalogue for the object Accepted Partial
0316 A theme belongs to a catalogue, the sign-in screen belongs to the operator Accepted Partial
0317 Acting on a user task is an object question Accepted Landed
0318 A user task's assignment may be an expression Accepted Landed
0319 Durable, versioned decision deployments — and latest resolved when a process is deployed Accepted Landed
0320 The decision editor is a page, not an overlay Accepted Landed
0321 A decision has a draft, and Save stops writing the model Accepted Landed
0322 One decision can be deployed on its own, from the editor Accepted Landed
0323 Two people can edit one decision together, and the lock is the decision Accepted Landed
0324 A decision is published as its own document Accepted Landed
0325 A DMN model's diagram is completed on read, and can be re-laid on request Accepted Landed
0326 A decision can be tried against sample inputs before anything is deployed Accepted Landed
0327 A deployed decision satisfies a latest-bound business rule task Accepted Landed
0328 The process document shows the decision a business rule task runs Accepted Landed
0329 A decision deployment is not deletable, and what has to be true before it is Accepted Landed
0330 A model with no reference stays findable Accepted Landed
0331 Deleting a DMN reference says what it would break Accepted Landed
0332 Accounts and groups are pulled from Entra by delta query, and the first run writes nothing Accepted Landed
0333 The inventory is taken before it is enforced, and what it records says Atlas did not grant it Accepted Partial
0334 Reconciliation reads absence as a finding, and only inside a scope somebody promised was complete Accepted Landed
0335 Starting an instance returns its key Proposed Not started
0336 Cleaning up the decision store Accepted Landed
0337 An incident flood is read by cause and resolved in bulk Accepted Landed
0338 Where a business object is used Accepted Landed
0339 The definition key space never goes backwards Accepted Landed
0340 An outage stops at the worker, not at every token Accepted (amended) Landed
0341 Recertification asks whether a right is still justified, and refuses to let silence answer Accepted Landed
0342 A conflict is a fact about a pair, and no rule can say which half is wrong Accepted Landed
0343 A reminder has to be able to ask about somebody else, and a person must not Accepted Landed
0344 A right may carry an end, and an end that has passed is a debt rather than a fact Accepted Landed
0345 The job-type index space never goes backwards Accepted Landed
0346 The remedy must not destroy the evidence, so a hold that ends leaves a row Accepted Landed
0347 A product may narrow its catalogue's audience, because a person only ever sees one catalogue Accepted Landed
0348 A favourite is a bookmark, so it stores an id and resolves nothing Accepted Landed
0349 Whose name an order may carry is a question Atlas must ask, and a hierarchy it must not invent Accepted Landed
0350 A write arrow may set several members at once Accepted Landed
0351 An «enumeration» says which values a member may take Accepted Landed
0352 Draw a relationship from the class it starts at Accepted Landed
0353 The catalogue answers forwards, and the person who maintains a service asks backwards Accepted Landed
0354 An approval list is a work list, so it starts where nobody has looked Accepted Landed
0355 A catalogue is searched over words it carries, in the browser, and a hit says where it lives Accepted Landed
0356 The recipient is picked from the directory Atlas already publishes, and a name nobody holds is the caller's mistake Accepted Landed
0357 A portable backup does not overwrite another installation's identity Accepted Landed
0358 A product names one Atlas form, and the order line carries the answers Accepted Landed
0359 A position can be withdrawn on its own and its details corrected; what is held is never changed in place Accepted Landed
0360 A category is a heading a product writes on itself, not a thing the catalogue owns Accepted Landed
0361 A price is a sentence the catalogue writes, frozen like a rule and computed by nothing Accepted Landed
0362 One decision covers a request; the engine still completes one task per line Accepted Landed
0363 The drawing says which members are used Accepted Landed
0364 The verdict is served, not duplicated Accepted Landed
0365 A number an operations view states is a counter or a walk, never the length of a page Accepted Landed
0366 The live diagram counts every parked token, not the ones a bounded scan reached Accepted Landed
0367 A directory photo arrives the way every other directory fact does Accepted Landed
0368 A picture of a person belongs to the account, in two provenances and one place Accepted Landed
0369 Addressing and the envelope for a message that leaves the node Accepted Not started
0370 A published message waits for its subscriber Accepted (amended) Not started
0371 A participant names an interface; the server says which node Proposed Not started
0372 Delivering a message to another node is a worker's job Accepted Not started
0373 A process publishes an interface, not its model Accepted Not started
0374 The white box is a cached contract, not a live look inside Proposed Not started
0375 The feed generator is Go, so the Go checks stop needing Node Superseded by ADR-0444 Superseded
0376 A product manager maintains the catalogue over MCP Accepted Landed
0377 With nobody to be, the portal shows a catalogue and refuses an order Accepted Landed
0378 A capped listing answers with a page, not with an array Accepted Landed
0379 A DMN document's own namespace decides which DMN version reads and writes it Accepted Landed
0380 A commitment is checked against what the process actually does Proposed Not started
0381 A problem is an aggregation over incidents, not a BPMN element Proposed Not started
0382 Whole-store reads leave the writer, and the store is sized for what it holds Accepted Landed
0383 A position's level is read from the graph once, and a root with no parts is not a bundle Accepted Landed
0384 A position is a product and the shape of it, and it says so in its own key Accepted Landed
0385 A decision is addressed by both of its names Accepted Landed
0386 A decision result keeps the type the model declares for it Accepted Landed
0387 The catalogue keeps its own model, and answers to TMF620 at the boundary Proposed Not started
0388 A call that can only be null is refused at deploy Accepted Landed
0389 Forked instance migration Accepted Landed
0390 The orderer is told where their position stands, from a route of their own Accepted Landed
0391 A product is shown, and its picture is not part of what was promised Accepted Landed
0392 The same refusal at every door that already refuses Accepted Landed
0393 A rule the compiler gains later is a gate on deploy, whatever stage it lives in Accepted Landed
0394 An approval is read and decided where the work already is Accepted Landed
0395 A product's structure is assembled per product, not related pairwise Accepted Landed
0396 The service catalogue is drawn on the starmap, as derived fact Accepted Landed
0397 The orderable window is enforced when an order is placed Accepted Landed
0398 A business rule task can call a decision service Accepted Landed
0399 The service portal carries its own sign-in Accepted Landed
0400 An edge that was taken is not an edge that was declared Accepted Partial
0401 What a node is called when there is more than one log Accepted Partial
0402 One estate, several nodes — a starmap stitched from subgraphs Accepted (amended) Landed
0403 The estate graph is derived from the log, never the log from the graph Accepted Not started
0404 The whole graph can be walked, in a projection with a stated scope Accepted Partial
0405 A decision is shown, not described Accepted Landed
0406 A decision service is drawn, and drawn around its members Accepted Landed
0407 Requirements as design-time records, attached by a declared edge Proposed Not started
0408 The portal's order rows carry one link into a process, not three Accepted Landed
0409 The archive substrate waits, its schema does not Proposed Partial
0410 A peer credential carries the reach a membership cannot give it Accepted Partial
0411 The shipped system processes call Atlas as REST connector tasks, told their own address Accepted Landed
0412 A catalogue heading is a key that groups and a wording per language that shows Accepted Landed
0413 A language tag is checked where it is written, and nowhere else Accepted Landed
0414 A missing translation is reported, not refused Accepted Landed
0415 The portal offers the languages its catalogue is kept in Accepted Landed
0416 The shop shows an order's open tasks, and the order records the instances that work it Accepted Landed
0417 The shop and Tasks are one column wide on a narrow screen Accepted Landed
0418 A withdrawn ordered right goes back through its order Accepted Landed
0419 A declared type is honoured at the decision boundary Accepted Landed
0420 One graph model, several projections, and a successor view built beside the starmap Proposed Not started
0421 A task is listed to whoever it was addressed to Accepted Landed
0422 Who decided is recorded, on the task, the order and the right Accepted Landed
0423 A business rule task follows the newest decision when it runs, or the version it names Accepted Landed
0424 A Playground rule can demand the cases it speaks about Proposed Not started
0425 A product's lifecycle is one process, and each operation is a message start it is triggered at Accepted Partial
0426 An untriggered create never seeds several start events Accepted Landed
0427 Converting a product to a lifecycle process keeps the old processes alive until nothing needs them Accepted Landed
0428 A product lifecycle may run as one instance per position, and its later operations are delivered to that instance Accepted Landed
0429 A product declares its actions, each a command whose outcome is a fact published beyond Atlas Accepted Partial
0430 The event feed has its own role and its own token scope Accepted Landed
0431 A system process announces its facts as signals Accepted Landed
0432 The event feed is narrowed by the catalogue that maintains the product Accepted Landed
0433 The event feed is pushed to a CloudEvents endpoint Accepted Landed
0434 The catalogue can be switched off, and switching it off removes a surface, not a semantics Accepted Landed
0435 Atlas keeps one catalogue of the events it emits Accepted Partial
0436 A catalogue is imported as one document Accepted Landed
0437 A package is imported from the command line Accepted Landed
0438 The mail Worker reads its mailbox — an inbound watch, mailbox operations, and who may use them Accepted Landed
0439 On Windows, a supervised worker ends with the server, however the server ends Accepted Landed
0440 A worker runs the jobs of one type concurrently, up to the places it has free Accepted (amended) Landed
0441 A position's answers reach its processes Accepted Landed
0442 An S3 Worker Type — a process puts a file down, finds it again, and hands it out Accepted Landed
0443 Publishing warns about answers in the clear Accepted Landed
0444 The Console shows the CHANGELOG as release notes, read while the server runs Accepted Landed
0445 A FEEL expression is written in a conversation, and checked by the engine before anybody reads it Accepted Landed
0446 Atlas shows the messages its deployed models send and receive Proposed Not started
0447 Atlas shows the signals its deployed models throw and receive Proposed Not started
0448 Trusted proxies — the client's address behind a load balancer, and the health check that is not news Accepted Landed
0449 A wizard is a subprocess one filler walks through Proposed Not started
0450 A scope drains only when no token is in transit into it Accepted Landed

The two states of a record

A record answers two questions, and they are not the same question. Status says whether the decision holds. Implementation says whether it is built. go test ./docs/adr checks both against their word lists and against each other, and the index above carries both.

Status
  • Proposed — under discussion
  • Accepted — decided and in effect
  • Superseded by ADR-XXXX — replaced by a later decision
  • Deprecated — no longer relevant

A parenthetical after the word is fine and common: Accepted (amended 2026-08-17: the marker runs on every activity kind). A different word is not — the guard rejects it.

Implementation
  • Not started — none of the chosen option is in the tree
  • Partial — part of what the record decided is built and part is not
  • Landed — the chosen option is in production code, with tests
  • Superseded — a later record replaced the decision; nothing here left to measure

This field carries no parenthetical, because the index is rendered from it. What is built and what is not belongs in the record, where a reader is.

Partial is the word that needs care. An extension the record itself defers is not what makes a record partial — ADR-0154 decided an LDAP connector and listed a delta cookie as a follow-up, and that record is Landed. A piece of the decision that is missing is what makes it partial — ADR-0027 decided that an author selects and applies an element template, and only the store behind it exists.

Why they are two fields

They were one until 2026-09. Status was asked to carry both, readers used it for the more useful question, and the two meanings drifted apart the only way they could: 115 records whose code had shipped, with tests, still read Proposed, because nothing in the process ever went back to change the line. An audit said so in August and named 28 of them; two weeks later not one had moved. That is a missing mechanism, not a missing afternoon.

So there is a rule with a test behind it: Implementation: Landed requires Status: Accepted. Nobody merges code against a decision that has not been made, so a record with landed code is a record whose decision was taken — whatever the front matter still says. The reconciliation that applied this to all 295 records, and the evidence behind each classification, is in the status reconciliation.

Documentation

Overview

Package adr carries the ADR directory's own conventions as code: the parser for a decision record, the guard tests in adr_test.go that keep the directory and its index honest, and the merge-time numbering below.

The problem it solves is structural. A number used to be taken when a record was written, on a branch — so two branches that each wrote a record each took the same "next free" number, and whichever merged second had to renumber: rename the file, fix the heading, move the index row, and chase every citation. Numbers 0090, 0103 and 0105 were each shared by two unrelated decisions that way before there was a test; afterwards the test caught it, but the renumbering churn stayed, and one record walked 0164 → 0165 → 0166 → 0167 → 0168 → 0169 across six merges without a word of its content changing.

So a record in flight carries no number at all. It lives at draft-<slug>.md, heads with `# ADR-DRAFT: title`, and is cited as ADR-draft-<slug>. Two branches writing records touch different files and no shared line of the index, so they cannot collide. AssignNumbers gives every draft its number once it is on main — where "the next free number" is finally a question with one answer — and rewrites the file name, the heading, the index row and every citation in one pass.

A number, once assigned, is never reassigned. That is what makes a citation safe: ADR-0168 in a comment means today what it will mean in a year.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BaseStatus added in v0.7.0

func BaseStatus(s string) string

BaseStatus strips a status's parenthetical. The index deliberately abbreviates "Accepted (amended 2026-08-17: …)" to "Accepted (amended)", so only the status itself — Proposed, Accepted, Superseded by ADR-NNNN, Deprecated — is comparable.

Types

type Assignment

type Assignment struct {
	Slug           string
	Num            int
	From           string // draft-<slug>.md
	To             string // NNNN-<slug>.md
	Title          string
	Status         string
	Implementation string
}

Assignment is one draft becoming a numbered record.

func AssignNumbers

func AssignNumbers(root string) ([]Assignment, error)

AssignNumbers gives every draft under root/docs/adr the next free number and makes the whole repository agree about it: the file is renamed, its heading rewritten, its row appended to the index, and every `ADR-draft-<slug>` citation and `draft-<slug>.md` link in the tree rewritten to the number it just got.

It is meant to run on main, where "the next free number" has one answer — see the package comment. With no drafts present it changes nothing, which is the common case for the workflow that runs it on every push.

type Record

type Record struct {
	Num    int    // 0 while the record is a draft
	Name   string // file name within docs/adr
	Slug   string // the file name's kebab-case part, stable across numbering
	Title  string // from the `# ADR-NNNN: title` heading
	Status string // from the `- **Status:** ...` line
	// Implementation is the second half of a record's state, from the
	// `- **Implementation:** ...` line. Status answers "does this decision hold";
	// Implementation answers "is it built". One field carried both for 295 records
	// and could answer neither: a decision whose code had shipped still read
	// `Proposed`, because nothing in the process ever went back to change it.
	// ADR-0298 says why they are separate rather than merged.
	Implementation string
	// OpenQuestion and QuestionChecked are the optional pair a record carries when
	// its reasoning rests on something nobody could answer: what the question is,
	// and the YYYY-MM month somebody last looked at it. They come as a pair or not
	// at all — a question with no date cannot go stale and would quietly read as
	// settled, a date with no question says a thing was checked without saying what
	// — and a guard test fails once a date has stood for a year.
	OpenQuestion    string
	QuestionChecked string
}

Record is one decision record on disk. A record still in flight has Num 0 and lives at draft-<slug>.md; a record that has landed carries its number in both the file name and the heading, and the guard tests insist the two agree.

func LoadRecords

func LoadRecords(dir string) ([]Record, error)

LoadRecords reads every record in dir — numbered and draft alike — and reports every malformed one in a single joined error, so a run names all the problems rather than the first. Records that parsed well enough to identify are returned even when the error is non-nil, which is what lets the guard tests check the index against the directory while still reporting a record's own defects.

func (Record) IsDraft

func (r Record) IsDraft() bool

IsDraft reports whether this record is still waiting for a number.

func (Record) QuestionAge added in v0.6.0

func (r Record) QuestionAge(now time.Time) (time.Duration, bool)

QuestionAge reports how long ago the record's open question was last looked at, relative to now, and whether there was a month to read at all. A negative age is a date in the future — a typo, and the one that would make a freshness check quieter rather than louder, so the guard can fail on it rather than wait it out.

The month is taken at its first day: a check stated as "2026-09" is treated as having happened on 1 September, which ages it slightly faster than it happened. That is the safe direction for a freshness rule.

Directories

Path Synopsis
cmd
adrnum command
Command adrnum assigns a number to every architecture decision record that is still in flight, and makes the repository agree about it.
Command adrnum assigns a number to every architecture decision record that is still in flight, and makes the repository agree about it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL