adr

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package adr parses ADR files under docs/decisions, renders the INDEX.md decision index, and scaffolds new ADR files from the rendered template (awf new adr). Generated by awf sync (regenerates docs/decisions/INDEX.md).

Index

Constants

View Source
const (
	V1FormatMarker = "current-state-v1"
	V2FormatMarker = "current-state-v2"
)

Format markers are the exact governed `format:` frontmatter values.

Variables

View Source
var FilenameRe = regexp.MustCompile(`^(\d{4})-.+\.md$`)

FilenameRe matches an ADR filename (NNNN-slug.md); group 1 is the 4-digit number.

Functions

func AdoptionBoundary added in v0.22.0

func AdoptionBoundary(dir string) (cutoff int, gaps []int, err error)

AdoptionBoundary validates and seals the existing brownfield ADR identities. Every existing decision is legacy at first adoption; governed records cannot appear below the newly established cutoff.

func ContentDigest added in v0.22.0

func ContentDigest(sections map[string]string) string

ContentDigest computes the current-state-v1 content-sha256 over the five canonical sections in fixed order, excluding frontmatter and Status history. Each section is serialized as its heading line followed by its body with trailing whitespace stripped, so cosmetic trailing-blank-line noise does not change the digest while any substantive edit does. Accepted freezes this value; a later terminal Status-history entry must repeat it. awf both computes and re-verifies it, so this canonical form is the single source of truth.

func FrozenContentEqual added in v0.22.0

func FrozenContentEqual(before, after ADR) bool

FrozenContentEqual reports whether a pair preserves canonical ADR content. Proposed records remain editable; every later status freezes the five content-sha256 sections at their before-state digest.

func HistoryTransitionValid added in v0.22.0

func HistoryTransitionValid(before, after ADR) bool

HistoryTransitionValid reports whether a pair preserves append-only Status history: equal histories at the same status, or an exact before prefix plus one entry when the status follows a legal lifecycle edge.

func NewFile added in v0.6.0

func NewFile(dir, title string, format Format) (string, error)

NewFile scaffolds a new ADR under dir: the next sequential number, the rendered template.md with every marker comment stripped and its date and title heading filled in, named NNNN-slug.md. Refuses to overwrite an existing file at that path. touches-state: adr-system/adr-lifecycle:adr-new-strips-markers - NewFile strips every marker comment from the copied template; proof in adr_test.go touches-state: adr-system/adr-lifecycle:adr-new-heading-matches-file - NewFile fills the heading from the allocated file number; proof in adr_test.go touches-state: adr-system/adr-lifecycle:adr-new-no-overwrite - refuse-overwrite guard; unbacked (unreachable), see ADR-0042 Verify note

func NextNumber added in v0.6.0

func NextNumber(dir string) (string, error)

NextNumber returns the next available 4-digit ADR number for dir: one more than the highest number ParseDir finds, or "0001" for an ADR-less dir. touches-state: adr-system/adr-lifecycle:adr-new-sequential-numbering - NextNumber returns highest-plus-one; proof in adr_test.go

func RenderIndexMD added in v0.22.0

func RenderIndexMD(corpus Corpus) string

RenderIndexMD renders the decisions/INDEX.md index for corpus (ADR-0135 item 8). It replaces the status-partitioned ACTIVE.md with two sections: "In flight" lists the Proposed and Accepted ADRs whose adoption is still under way, and "History" is a compact roll of the terminal Implemented and Abandoned decisions kept only as rationale. Both sections always render, with a placeholder line when empty, so the content is never blank and its document-map link resolves out of the box. The content carries no generated-by banner - that is the caller's job (internal/project's generateIndexMD, via injectBanner).

func TransitionLegal added in v0.22.0

func TransitionLegal(from, to string, format ...Format) bool

TransitionLegal reports whether from -> to is legal for the selected format. Omitting format preserves the V1 behavior used by existing callers.

Types

type ADR

type ADR struct {
	Number        string            // e.g. "0001"
	Title         string            // e.g. "ADR-0001: Template Overlay Rendering Engine"
	Status        string            // e.g. "Accepted"
	Date          string            // frontmatter date, retained verbatim as YYYY-MM-DD text
	Filename      string            // e.g. "0001-template-overlay-rendering-engine.md"
	Path          string            // path as globbed
	Domains       []string          // `domains:` frontmatter (ADR-0014)
	Tags          []string          // `tags:` frontmatter (keyword labels)
	Related       []int             // `related:` frontmatter (ADR numbers)
	Sections      map[string]string // `## ` heading -> non-fenced section body
	DecisionStart int               // raw file byte offset of the Decision heading; 0 when absent
	DecisionEnd   int               // raw file byte offset immediately after the Decision section; 0 when absent

	// Governed fields are populated only for an ADR at or above one of the
	// lock's format cutoffs. A legacy-format record leaves them zero.
	Format     Format         // Legacy, CurrentStateV1, or CurrentStateV2
	NoneState  bool           // State changes section is exactly "None."
	Operations []Operation    // parsed `## State changes` operations
	History    []HistoryEvent // parsed `## Status history` events
}

ADR is a parsed ADR record.

func ParseBytes added in v0.18.0

func ParseBytes(name string, data []byte) (ADR, bool, error)

ParseBytes parses one ADR from bytes: status and the other frontmatter fields, plus the title from the first `# ` heading. It is the seam the git-blob consumers take (ADR-0130 item 5): internal/audit reads history rather than the working tree, so it cannot take a Corpus, but it can share the parser and the frontmatter schema, which is where the duplication actually was.

found reports whether frontmatter was present at all, which is the tri-state the audit needs: absent frontmatter is a legitimate empty status, while present-but-unparseable is an error. name is the ADR's base filename, from which Filename and Number are derived; Path is left empty, since a blob-sourced record has no working-tree path.

func ParseDir

func ParseDir(dir string) ([]ADR, error)

ParseDir scans dir for ADR files (NNNN-*.md) and parses each into an ADR.

func ParseRecord added in v0.22.0

func ParseRecord(name string, data []byte, boundaries FormatBoundaries) (ADR, error)

ParseRecord routes by the V1 and V2 format boundaries.

func ParseV1 added in v0.22.0

func ParseV1(name string, data []byte) (ADR, error)

ParseV1 parses and validates one current-state-v1 ADR. name is the base filename (Number is derived from it); Title comes from the first `# ` heading. It enforces the exact frontmatter, status enum, section order, sequential Decision items, State-changes and Status-history grammar, and the per-ADR lifecycle and digest rules. Cross-ADR facts (sequence contiguity, ID reuse, claim provenance) are validated at the corpus level.

func ParseV2 added in v0.22.0

func ParseV2(name string, data []byte) (ADR, error)

ParseV2 parses and validates one current-state-v2 ADR.

func (ADR) ApplicationBatches added in v0.22.0

func (a ADR) ApplicationBatches() ([]ApplicationBatch, error)

ApplicationBatches projects the application records owned by a governed ADR.

func (ADR) Bucket added in v0.18.0

func (a ADR) Bucket() string

Bucket returns the legacy status group for an ADR. Every superseded ADR folds into one group regardless of the successor its status names.

func (ADR) DecisionItems added in v0.18.0

func (a ADR) DecisionItems() []int

DecisionItems returns the numbers of the column-0 numbered items of the Decision section, in order of appearance.

func (ADR) DeclaredSlugs added in v0.18.0

func (a ADR) DeclaredSlugs() []string

DeclaredSlugs returns the invariant slugs a's Invariants section declares, backed and unbacked alike, in declaration order.

func (ADR) HasSameStatus added in v0.18.0

func (a ADR) HasSameStatus(other ADR) bool

HasSameStatus reports exact status equality without exporting literal comparisons to migration consumers.

func (ADR) HasStatus added in v0.18.0

func (a ADR) HasStatus() bool

HasStatus reports whether the record carries a frontmatter status at all. The audit distinguishes an ADR with no status from one with a real status, and that tri-state is what the bytes seam carries (ADR-0130 item 3).

func (ADR) InvariantDecls added in v0.18.0

func (a ADR) InvariantDecls() []InvariantDecl

InvariantDecls returns the declarations a's Invariants section carries, in declaration order. Status-independent: the ref-validity check and the retirement migration resolve slug anchors against any ADR's declarations, not just Implemented ones (ADR-0120 item 2).

func (ADR) IsAbandoned added in v0.22.0

func (a ADR) IsAbandoned() bool

IsAbandoned reports the current-state-v1 terminal Abandoned state.

func (ADR) IsAccepted added in v0.22.0

func (a ADR) IsAccepted() bool

IsAccepted reports the current-state-v1 Accepted state: the decision is normative only for executing its pending State changes, which never override the topic claims describing current reality (ADR-0135).

func (ADR) IsGoverned added in v0.22.0

func (a ADR) IsGoverned() bool

IsGoverned reports whether the record uses either current-state format.

func (ADR) IsImplemented added in v0.18.0

func (a ADR) IsImplemented() bool

IsImplemented reports whether the ADR's decisions have shipped. Invariant backing and token retirement are both gated on this.

func (ADR) IsImplementing added in v0.22.0

func (a ADR) IsImplementing() bool

IsImplementing reports whether a V2 decision has applied only part of its declared operations.

func (ADR) IsInflight added in v0.18.0

func (a ADR) IsInflight() bool

IsInflight reports a decision still under review or implementation.

func (ADR) IsLegacyShipped added in v0.18.0

func (a ADR) IsLegacyShipped() bool

IsLegacyShipped reports whether a legacy decision shipped, including the historical Superseded state. Migration inventory uses this broader predicate; normal legacy authority continues to use its existing predicates.

func (ADR) IsLive added in v0.18.0

func (a ADR) IsLive() bool

IsLive reports whether the ADR's decisions are current guidance.

func (ADR) IsProposed added in v0.18.0

func (a ADR) IsProposed() bool

IsProposed reports whether the ADR's body is still mutable.

func (ADR) IsSuperseded added in v0.18.0

func (a ADR) IsSuperseded() bool

IsSuperseded reports whether the ADR has been retired. The prefix test tolerates the pre-generation-12 suffixed form as well as the bare status ADR-0128 item 4 moves to.

func (ADR) IsV1 added in v0.22.0

func (a ADR) IsV1() bool

IsV1 reports whether the record was parsed as current-state-v1.

func (ADR) IsV2 added in v0.22.0

func (a ADR) IsV2() bool

IsV2 reports whether the record was parsed as current-state-v2.

func (ADR) OperationProgress added in v0.22.0

func (a ADR) OperationProgress() (OperationProgress, error)

OperationProgress projects declared operations into applied, remaining, and canceled partitions without inferring removal from claim absence.

func (ADR) ReachedAccepted added in v0.22.0

func (a ADR) ReachedAccepted() bool

ReachedAccepted reports whether the ADR's history entered Accepted, including a later transition to a terminal state.

type ApplicationBatch added in v0.22.0

type ApplicationBatch struct {
	Sequence   int
	Operations []Operation
	Implicit   bool
}

ApplicationBatch is one implicit or explicit application of declared state operations. Operations are retained in declaration/event order.

type AppliedOperation added in v0.22.0

type AppliedOperation struct {
	Operation Operation
	Sequence  int
}

AppliedOperation is one applied declaration and its inherited batch sequence.

type ClaimOperationHistory added in v0.22.0

type ClaimOperationHistory struct {
	Origin         *OperationRecord
	LegacyBaseline bool
	RevisedBy      []OperationRecord
	RemovedBy      *OperationRecord
}

ClaimOperationHistory is the implemented add/update/remove history for one qualified claim identity.

type Corpus added in v0.18.0

type Corpus struct {
	// contains filtered or unexported fields
}

Corpus is the parsed decisions directory: one parse, threaded to every consumer that needs an ADR fact (ADR-0130 item 1). It answers questions rather than exposing fields for a caller to re-derive an answer from (item 2), which is what collapsed the three-way "is live" and the twice-built supersession relation into one place.

The zero value is not useful; construct with NewCorpus.

func LoadCorpus added in v0.18.0

func LoadCorpus(dir string) (Corpus, error)

LoadCorpus parses a decisions directory into the view. It is the single construction seam: adr.ParseDir has no production caller outside this package, so every consumer - the *Project that threads the view to the checks, and the schema migrations, which run before a Project can be opened and so cannot be handed one - enters through here.

func NewCorpus added in v0.18.0

func NewCorpus(adrs []ADR) Corpus

NewCorpus builds the view over an already-parsed slice.

func (Corpus) All added in v0.18.0

func (c Corpus) All() []ADR

All returns every parsed ADR in directory order.

func (Corpus) ByNumber added in v0.18.0

func (c Corpus) ByNumber(num string) (ADR, bool)

ByNumber returns the ADR with the given four-digit number. The ADR number is the sole identity key (ADR-0130 item 4).

func (Corpus) ClaimOperationHistory added in v0.22.0

func (c Corpus) ClaimOperationHistory(claimID string) (ClaimOperationHistory, bool)

ClaimOperationHistory returns applied operation history for claimID in batch sequence order. Remaining and canceled operations are excluded, and every returned slice is fresh.

func (Corpus) Has added in v0.18.0

func (c Corpus) Has(num string) bool

Has reports whether the corpus contains an ADR with the given number.

func (Corpus) NextIdentity added in v0.22.0

func (c Corpus) NextIdentity() (int, error)

NextIdentity returns one more than the highest ADR identity, or 1 for an empty corpus. Migration code uses this semantic query rather than adding a raw decisions-directory reader.

func (Corpus) OperationProgress added in v0.22.0

func (c Corpus) OperationProgress(number string) (OperationProgress, bool, error)

OperationProgress returns the operation partition for one ADR. Missing and invalid-present records are deliberately distinct.

func (Corpus) Raw added in v0.18.0

func (c Corpus) Raw(num string) ([]byte, error)

Raw returns the ADR file's bytes. Raw access is enumerated and closed (ADR-0130 item 6): the migration's offset surgery and the retired-key frontmatter scan are the only two legitimate consumers below the semantic layer. A third caller means the view is missing a question.

type Format added in v0.22.0

type Format int

Format distinguishes legacy, current-state-v1, and current-state-v2 ADRs.

const (
	// Legacy is a pre-cutover ADR: identity, status, and date only.
	Legacy Format = iota
	// CurrentStateV1 is a `format: current-state-v1` ADR with State changes and
	// status-only history.
	CurrentStateV1
	// CurrentStateV2 is a `format: current-state-v2` ADR with heterogeneous
	// status and application history.
	CurrentStateV2
)

type FormatBoundaries added in v0.22.0

type FormatBoundaries struct {
	V1From int
	V2From int
}

FormatBoundaries are the immutable ADR format cutoffs from one snapshot. A zero V2From leaves every governed record in the V1 region.

type HistoryEvent added in v0.22.0

type HistoryEvent struct {
	Kind        HistoryEventKind
	Date        string
	Status      string
	Digest      string
	Sequence    int
	HasSequence bool
	Rationale   string
	Operations  []Operation
}

HistoryEvent is one parsed `## Status history` line.

type HistoryEventKind added in v0.22.0

type HistoryEventKind uint8

HistoryEventKind distinguishes lifecycle events from operation-application events in current-state-v2 Status history.

const (
	HistoryStatus HistoryEventKind = iota + 1
	HistoryApplied
)

type InvariantDecl added in v0.18.0

type InvariantDecl struct {
	Slug     string
	Unbacked bool
	Bullet   string
}

InvariantDecl is one invariant declaration in an ADR's Invariants section. The grammar lives here rather than in internal/invariants because ADR-0130 item 2 makes declared slugs a question the corpus view answers, and corpus-owns-field-reads forbids any other package reading ADR.Sections to re-derive it. Bullet carries the whole declaration - lead line plus wrapped continuation lines - so a consumer can scan it for the `Verify:` note without a second pass over the section.

type OpVerb added in v0.22.0

type OpVerb string

OpVerb is a current-state-v1 State-changes verb.

const (
	// OpAdd introduces a new claim.
	OpAdd OpVerb = "add"
	// OpUpdate revises an existing claim.
	OpUpdate OpVerb = "update"
	// OpRemove retires an existing claim.
	OpRemove OpVerb = "remove"
)

type Operation added in v0.22.0

type Operation struct {
	Verb OpVerb
	ID   string // qualified claim ID
	Slug string // local slug component of ID
}

Operation is one parsed `## State changes` entry: a verb over a qualified claim ID `<domain>/<topic>:<local-slug>` (ADR-0135 item 3).

type OperationProgress added in v0.22.0

type OperationProgress struct {
	Applied   []AppliedOperation
	Remaining []Operation
	Canceled  []Operation
}

OperationProgress partitions an ADR's declarations by application state.

type OperationRecord added in v0.22.0

type OperationRecord struct {
	Number        string
	Title         string
	Status        Status
	StateSequence int
}

OperationRecord is the ADR identity and implementation order for one claim operation. StateSequence orders implemented mutations independently of ADR number.

type Status added in v0.22.0

type Status = string

Status is an ADR lifecycle status as presented by semantic corpus queries.

type StatusEntry added in v0.22.0

type StatusEntry = HistoryEvent

StatusEntry preserves the source-compatible V1 name while ADR.History uses the common event representation.

Jump to

Keyboard shortcuts

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