adr

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 11 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
0001 Event sourcing and log-structured state Accepted
0002 Single-writer partition model Accepted
0003 Pebble as embedded state store Accepted
0004 Compile BPMN to an integer-indexed graph Accepted
0005 Group commit and fsync strategy Accepted
0006 Partition routing and cross-partition communication Accepted
0007 Job worker protocol Accepted
0008 FEEL expression compilation strategy Accepted
0009 Record serialization format Accepted
0010 Go as implementation language, no CGO Accepted
0011 Single-binary distribution with an embedded web viewer and editor Accepted
0012 A buildless, self-contained web UI app shell Accepted
0013 Embed the bpmn-js modeler as a vendored asset Accepted
0014 DMN business rule tasks via the temis engine Accepted
0015 Reuse the external FEEL engine behind an expr boundary Accepted
0016 Model Context Protocol server as a stdio adapter over the HTTP API Accepted
0017 Retain finished process instances in a history index Accepted
0018 Test-driven development as the default workflow Accepted
0019 Durable deployments via an on-disk sidecar store Accepted
0020 Message events and correlation Accepted
0021 Diagram drafts, separate from deployments Accepted
0022 Retain a per-element token-visit history for the Operations overlay Accepted
0023 Collaborations and pools as multi-process deployments Accepted
0024 Parallel gateway join synchronization Accepted
0025 Extend the hand-written properties panel instead of vendoring bpmn-js-properties-panel Proposed
0026 A Problems panel with validation targeted at an engine version Proposed
0027 Element templates for pre-configured, reusable elements Proposed
0028 User tasks, forms, and the Tasks app Proposed
0029 Public process start via a published form link Accepted
0030 Play mode — ephemeral in-Modeler process simulation Proposed
0031 Diagram version history in the Modeler Proposed
0032 In-Modeler AI copilot over the MCP/HTTP surface Proposed
0033 Inclusive gateway join synchronization Accepted
0034 Projects as containers for heterogeneous artifacts Accepted
0035 Message start events and the processInstanceKey built-in Accepted
0036 A clio connector — server-registered event-store integration Accepted
0037 Structured JSON variables Accepted
0038 Collaboration message-flow replay Accepted
0039 Input/output variable mappings for business rule tasks Accepted
0040 Boundary events — timer and message, interrupting and non-interrupting Accepted
0041 Connector management and the secret store Accepted
0042 User-task runtime assignment and claim/unclaim Accepted
0043 An OpenAPI spec and an embedded API explorer for the HTTP API Accepted
0044 User management and the authentication boundary Accepted
0045 Binding user-task assignment to real identities Accepted
0046 Single-process step-by-step replay Accepted
0047 Polyglot script tasks (PowerShell, …) via job workers Accepted
0048 Per-step variable snapshots in the single-process replay Accepted
0049 Internal service authentication for the in-process MCP adapter Accepted
0050 Central DMN decisions via a temis decision connector Accepted
0051 Timer start events (duration, date, cycle) Proposed
0052 Message end events Accepted
0053 First-class data objects — typed, event-sourced state, and lineage Accepted
0054 Date and cycle timers for catch and boundary events Proposed
0055 FEEL-expression timer schedules for catch and boundary events Proposed
0056 FEEL cycles, and FEEL on timer start events Proposed
0057 First-class FEEL temporals for timer schedules Proposed
0058 Data output associations — write a value and transition a data object's state Accepted
0059 Data input associations — read a data object into an activity Accepted
0060 Field-level data object writes — set one member of a structured object Accepted
0061 Incident model — job-failure incidents, raise, resolve, resume Proposed
0062 An embedded DMN editor (dmn-js) Accepted
0063 DMN decision binding (latest vs deployment) Accepted
0064 Timer FEEL-failure incidents — park and raise instead of firing immediately Proposed
0065 Multi-token process replay and causal token lineage Accepted
0066 Durable decision-evaluation records for debugging Accepted
0067 A service-task connector catalog, and REST with a model-authored endpoint Accepted
0068 Task input/output variable mappings with activity-local scopes Proposed
0069 An engine-internal encrypted secret vault (ADR-0041 option A3) Accepted
0070 The secret vault is on by default, with a generated key Accepted
0071 Sharing scopes — private and shared access boundaries for design-time work Accepted
0072 Multiple DMN models per process deployment Accepted
0073 A principals directory for member and assignee pickers Accepted
0074 Embedded subprocesses (scope lifecycle via child counters) Accepted
0075 A clio inbound event bridge — at-least-once ingestion with engine-side idempotent delivery Accepted
0076 Call activities (single-partition) Accepted
0077 Multi-instance activities (parallel and sequential) Accepted (amended)
0078 Design-view token simulation — a client-side control-flow walkthrough Accepted
0079 An outbound mail connector (SMTP first) Accepted (amended)
0080 Sublinear runtime views via maintained aggregate counters Accepted
0081 A community marketplace for connectors, service tasks, and script tasks Proposed
0082 Event subprocesses (message- and timer-triggered, interrupting and non-interrupting) Proposed
0083 An O(1) instances summary — per-definition finished-count and last-activity counters Accepted
0084 CSV batch validation — upload a file, validate every row against business rules, correct the failures Proposed
0085 Process-instance TTL — self-cleaning via the due-timer index Accepted
0086 Gateway conditions resolve over the scope chain Proposed
0087 In-process CSV ingestion — upload in a user task, parse in the process Accepted
0088 Signal events (broadcast throw/catch) Accepted
0089 Error events (scoped propagation to the nearest handler) Accepted
0090 Bulk-terminate running instances — an explicit selection and a filtered scope Accepted
0091 User-task scheduling — priority and due date Accepted
0092 One-click clio credential provisioning Accepted
0093 Native Gmail and Microsoft Graph mail providers Accepted
0094 Singleton message start — at most one live instance per correlation key Accepted
0095 External variable modification on a running instance Accepted
0096 Token simulation — event triggers, inclusive gateways, and an auto-decide mode Accepted
0097 Token simulation — message starts, event-subprocess triggers, and multi-instance Accepted
0098 Audit trail for external variable modifications Accepted
0099 An ArchiMate 3.2 enterprise-architecture view Accepted
0100 Token simulation — configurable multi-instance count, modelled cardinality wins Accepted
0101 Token simulation — a thrown message/signal delivers to a waiting catch Accepted
0102 Receive tasks Accepted
0103 Compensation and compensation handlers Accepted
0104 Token simulation — entering embedded subprocesses Accepted
0105 Per-server call-activity target overrides Accepted
0106 A BMC Remedy connector — server-registered ITSM entry creation Accepted
0107 Backup and restore — a one-file download of the design-time data directory Accepted
0108 BPMN transactions (cancel end event, cancel boundary, transactional compensation) Accepted
0109 Whole-instance snapshot — a full backup that includes running instances Accepted
0110 Event-based gateways (deferred choice) Accepted
0111 Completing the incident model — retry backoff and timer-FEEL failure incidents Accepted
0112 Send tasks Accepted
0113 Org-wide UI brand theme Accepted
0114 OpenSearch event exporter — a WAL-tailing sink, off the hot path Accepted
0115 History retention — an export-gated, age-based hard delete of finished instances Accepted
0116 Terminate end events Accepted
0117 An AI agent task — an LLM agent as a managed connector on the job path Proposed
0118 A web-scraping connector — model-authored URL + CSS selector extraction Accepted
0119 Deactivating a deployed process Accepted
0120 Mockup (engine-simulated) service tasks Accepted
0121 BPMN lanes Accepted (Layer A)
0122 A protected system project and bootstrap-deployed platform processes Accepted
0123 A sanctioned automated user-provisioning path for system processes Accepted (amended)
0124 Server-side BPMN diagram auto-layout in Go Accepted
0125 Escalation events (non-interrupting, propagating throw/catch) Accepted
0126 Self-service registration link on the login screen Accepted
0127 A layered layout pipeline and executable layout invariants Accepted (amended)
0128 Process applications — the project, elevated into a deployable, versioned, portable unit Proposed
0129 Remote deployment targets — publish an application to another Atlas server Proposed
0130 Deprecating a process version — a drain state distinct from pausing Proposed
0131 Engine recovery checkpoints and WAL compaction Accepted
0132 Link events (intra-scope goto — a compile-time synthetic flow) Accepted
0133 Standard loop activities (the ↻ marker) Accepted (amended)
0134 Git-backed applications — a repository as an application's source of truth Proposed
0135 Retries as a property of every job-backed task Accepted
0136 Terminated tokens in the step-by-step replay Accepted
0137 Conditional events (data-triggered catch/boundary) Accepted
0138 Ad-hoc subprocesses (on-demand, unordered contained activities) Accepted
0139 A first-class "CSV to JSON" connector kind with model-authored layout Accepted
0140 Live collaborative modeling sessions — real-time co-editing of drafts by people and AI agents Proposed
0141 A SharePoint connector (create list item, via Microsoft Graph) Accepted
0142 Operational metrics over a Prometheus endpoint Accepted
0143 Process documentation export Accepted
0144 Per-definition history TTL — retention the model declares Accepted
0145 A Developer View for code-bearing fields Accepted
0146 History expiry as a due-date index — retention that scales with what is due Accepted
0147 Splitting the api Server object, without weakening the single writer Accepted
0148 Org-wide brand logo Accepted
0149 A bounded outbound-call budget for every connector Accepted (amended)
0150 A preview mail provider, and incidents on the live diagram Accepted (amended)
0151 Incidents beyond the live diagram — the replay, the lists, and one shared action Accepted
0152 OAuth2 client-credentials for the REST connector Proposed
0153 SCIM 2.0 provisioning connector Proposed
0154 Generic LDAP connector Proposed
0155 The Secrets panel says what a value has to be Accepted
0156 In-process vs. out-of-process service tasks — where a step's work runs, and what we recommend Proposed
0157 Every side-effecting task on a worker process — atlas worker, optional supervision, and a Workers console Proposed
0158 A connector reference that explains itself — and an incident you can actually resolve Accepted
0159 Auditable manual task completion Accepted
0160 Fix the connector from the incident Accepted
0161 What an element was handed, on the diagram Accepted
0162 Process instance migration Proposed
0163 Deleting a connector deployed models still reference — and keeping a table inside its card Accepted
0164 No in-process service tasks — the core loop must never be able to get stuck Accepted
0165 SOAP / Web Services (WSDL) connector Proposed
0166 Active Directory connector Proposed
0167 A released connector ships in the marketplace Proposed
0168 Moving a connector onto a worker — where the task detail travels, and where the credential lives Accepted
0169 A form on the incident — repairing an instance with named fields instead of raw JSON Accepted
0170 ADR numbers are assigned at merge, not on a branch Accepted
0171 A directory-file connector — LDIF and DSML Proposed
0172 A Microsoft Entra ID connector Proposed
0173 Three SQL connectors, and the first kinds born on a worker Proposed
0174 A connector task's input mappings are its outbound payload Proposed
0175 Replicated partition cells for horizontal scale-out Proposed
0176 Standards boundary and the Atlas runtime contract Proposed
0177 Reload skips the deploy-time validation gate Proposed
0178 Responsibility metadata — RACI on the element, with R derived from the assignment Proposed
0179 A worker's job history lives in clio, not in Atlas Accepted
0180 Groups as scope members Proposed
0181 Mock mode for the Active Directory connector Proposed
0182 Active Directory runs on a worker by default Proposed
0183 The Modeler asks the server where an authored kind runs Accepted
0184 Grant audit log Proposed
0185 Live group membership Proposed
0186 Embedding a public start form cross-origin (scoped CORS) Proposed
0187 Database change events — captured in the database, read on a worker, deduplicated in the engine Proposed
0188 A database is a Console entry, not a start parameter — and a worker is never a thing you create Proposed
0189 Panorama architecture modeling and live operational overlays Accepted (amended)
0190 Add explicit RSS and Atom extraction to the web-scraping connector Proposed
0191 TLS 1.3 in the binary — an optional listener with operator-supplied certificates Accepted
0192 BMC Remedy runs on a worker by default Proposed
0193 The Active Directory mockup switch belongs in the Console Proposed
0194 API tokens — a credential a machine can actually be given Proposed
0195 Requiring a login is the default Proposed
0196 The MCP transport is authenticated, and acts as its caller Proposed
0197 A throttle on the login, and a security audit trail Proposed
0198 The Prometheus exposition moves behind the boundary Proposed
0199 Every mounted route declares its access class Proposed
0200 Atlas as an OAuth resource server, so a hosted MCP client can connect Accepted
0201 Atlas Jira connector Proposed
0202 Atlas holds the AD mockup's starting entries Proposed
0203 Worker execution model and integration terminology Proposed
0204 Hosted apps — user HTML/JS served from an isolated origin Proposed
0205 Who owns a connector, and who may use the events it brings in Accepted
0206 Active Directory is a connector you configure, not one you write into a model Proposed
0207 Package Worker Types as signed external runtime artifacts Proposed
0208 Worker Type package contract, trust, and distribution Proposed
0209 Roles per endpoint group Accepted
0210 Federated authentication Accepted
0211 Panorama's derived landscape mesh and notation projections Accepted
0212 The element-template applier, and what a template binding means in Atlas Proposed
0213 The mock Active Directory is visible in the Console Accepted
0214 Jira as an inbound event source — a polled issue watch, deduplicated per issue Accepted
0215 The Modeler Playground — batch simulation and analysis of a draft Proposed
0216 Mockups are one view, not one per kind Proposed
0217 A mock REST API served from an OpenAPI document Accepted
0218 Jira runs on a worker by default Proposed
0219 Variable write attribution Accepted
0220 The Console may dial a database, and the engine still links no driver Proposed
0221 A database task runs against seeded answers, not against a SQL engine Proposed
0222 An artifact's id is its identity — renaming moves it, collisions are refused Proposed
0223 Jira account lookup Proposed
0224 A mockup run is visible, and it carries what the process bound Proposed
0225 An inbound watch has an hourly budget Proposed
0226 A start event is a trigger, and the one that fires is the one that starts Proposed
0227 A Jira read is bounded and moves forward Proposed
0228 User presence in the Console Proposed
0229 The Modeler's editor bar carries two acts and a menu Accepted
0230 The process information model — UML classes above BPMN's data objects Proposed
0231 Structured HTML extraction, richer feed entries, and a fetch that survives the real web Proposed
0232 Importing a UML class diagram — reading what somebody else drew Proposed
0233 Finish ADR-0164 — in-process connector work becomes a finite list, then nothing Proposed
0234 Google Sheets and Drive as inbound event sources — a polled row watch and a polled folder watch Proposed
0235 Google Sheets as a Worker Type — a spreadsheet is a process data source Proposed
0236 A non-interrupting message or signal boundary event stays armed Accepted
0237 The class canvas on diagram-js Proposed (amended)
0238 A reverse index from call activity to child instance Proposed
0239 Read-only queries run off the run loop, on a consistent view Proposed
0240 Variables stays on the Modeler's bar, as a pressed button with a shortcut Accepted
0241 Finding an instance — a key lookup and a per-definition index Accepted
0242 One route table describes the shell Proposed
0243 The views are built from shared parts Proposed
0244 Searchable variables — a declared value index Accepted
0245 The call activity's "+" is the way into the process it calls Accepted
0246 The Tasks app descends into a called process instead of navigating to it Accepted
0247 An instance that is gone is still findable Proposed
0248 A search term is literal, and widening is asked for Proposed
0249 Cancelled tokens on the runtime overlay, and a deferred choice drawn once Accepted
0250 Documentation prose is Markdown, rendered by one closed renderer Proposed
0251 Adjusting a deployed definition's diagram without redeploying it Proposed
0252 Runtime badges hang outside the shape, clear of its caption Proposed
0253 Agent tool calls drive ad-hoc activation — the toolbox is the model Accepted
0254 An agent round on a worker — the toolbox travels out, the tool calls travel back Accepted
0255 An agent model is a Console Worker — the one field that is not a secret Accepted (amended)
0256 The model is authored, the provider is configured — and one call is a task Proposed
0257 What an agent may read is authored, the way its reach already is Proposed
0258 Discord as a Worker Type — a process speaks in the channel the team already reads Proposed
0259 The data object lifecycle — what the BPMN data state resolves against Proposed
0260 A form is generated at design time, by the Worker an operator already configured Proposed
0261 The diagram is the query — filtering instances by the element they sit on Proposed
0262 Discord as an inbound event source — a channel is a log, and a snowflake is its sequence Proposed
0263 The brand palette reaches the form runtime Proposed
0264 A row watch's idempotency mark is its own, and the cursor is why that needs no migration Proposed
0265 Signing in does not wait for the run loop Proposed
0266 The runtime counts leave the run loop, and take the write paths with them Proposed
0267 The console speaks German first, through a catalogue rather than a rewrite Proposed
0268 Task folders are saved filters, stored as rules and generated into FEEL Proposed
0269 The engine stays namespace-blind, and the deploy says so Proposed
0270 A poll costs a page, not a backlog Proposed
0271 A batch persists the work it still owes Proposed
0272 One token may not hold the writer forever Proposed
0273 A gateway that cannot route parks, it does not complete Proposed
0274 The in-process runner claims what it works Proposed
0275 Reading a running instance is an object question Proposed
0276 A loop's size is checked before it is built Proposed
0277 A join synchronizes within its own execution scope Proposed
0278 Filing a deployment into a project is a write on that project Proposed
0279 Topology is compiled, including the join's ancestors Proposed
0280 Recovery proves its prefix or refuses to start Proposed
0281 A role change takes effect on the next request, not the next login Proposed
0282 One inventory of what is on disk, and what a backup owes it Proposed
0283 Only the end of the active segment may be torn Proposed
0284 A cancellation sees the children created in its own batch Proposed
0285 A batch is one framed unit in the log Proposed
0286 A list carries its own search Proposed
0287 Create the worker from the incident, and run the deploy preflight on every deploy path Proposed
0288 An agent's commit is authored by the person who asked for it Proposed
0289 A Worker Type carries its own setup, in the panel where it is chosen Proposed
0290 A join counts tokens per incoming flow Proposed
0291 One place names every resource budget, and one way sets them Draft
0292 A MIM import hands over its rows as data, and counts them as work Accepted
0293 A record that rests on an open question says so, and the question expires Proposed
0294 A variable is a record, a collection is a loop's harvest — two budgets Draft

Status values

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

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

This section is empty.

Types

type Assignment

type Assignment struct {
	Slug   string
	Num    int
	From   string // draft-<slug>.md
	To     string // NNNN-<slug>.md
	Title  string
	Status 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
	// 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