Documentation
¶
Overview ¶
Package adr parses ADR files under docs/decisions, renders the ACTIVE.md index, and scaffolds new ADR files from the rendered template (awf new adr). Generated by awf sync (regenerates docs/decisions/ACTIVE.md).
Index ¶
- Variables
- func NewFile(dir, title string) (string, error)
- func NextNumber(dir string) (string, error)
- func RenderActiveMD(corpus Corpus) string
- func RenderDomainIndex(corpus Corpus, domain string) string
- type ADR
- func (a ADR) Bucket() string
- func (a ADR) DecisionItems() []int
- func (a ADR) DeclaredSlugs() []string
- func (a ADR) HasSameStatus(other ADR) bool
- func (a ADR) HasStatus() bool
- func (a ADR) InvariantDecls() []InvariantDecl
- func (a ADR) IsImplemented() bool
- func (a ADR) IsInflight() bool
- func (a ADR) IsLegacyShipped() bool
- func (a ADR) IsLive() bool
- func (a ADR) IsProposed() bool
- func (a ADR) IsSuperseded() bool
- type Anchor
- type Chain
- type Citation
- type Claim
- type Corpus
- func (c Corpus) All() []ADR
- func (c Corpus) Anchors(num string) []Anchor
- func (c Corpus) AnnotatedAnchors() []Claim
- func (c Corpus) ByNumber(num string) (ADR, bool)
- func (c Corpus) Chains() []Chain
- func (c Corpus) Citations(num string) []Citation
- func (c Corpus) ClaimsOn(num string) []Claim
- func (c Corpus) DecisionItems(num string) []int
- func (c Corpus) DeclaredSlugs(num string) []string
- func (c Corpus) GraphFaults() ([]SelfClaim, []Cycle)
- func (c Corpus) Has(num string) bool
- func (c Corpus) Raw(num string) ([]byte, error)
- func (c Corpus) RefsOf(num string) []SupersessionRef
- func (c Corpus) Retirers(num string) []string
- func (c Corpus) State(num string) State
- func (c Corpus) UncoveredAnchors(num string) []Anchor
- type Cycle
- type InvariantDecl
- type Relation
- type SelfClaim
- type State
- type SupersessionRef
Constants ¶
This section is empty.
Variables ¶
var FilenameRe = regexp.MustCompile(`^(\d{4})-.+\.md$`)
FilenameRe matches an ADR filename (NNNN-slug.md); group 1 is the 4-digit number.
Functions ¶
func NewFile ¶ added in v0.6.0
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-invariant: adr-new-strips-markers - NewFile strips every marker comment from the copied template; proof in adr_test.go touches-invariant: adr-new-heading-matches-file - NewFile fills the heading from the allocated file number; proof in adr_test.go touches-invariant: adr-new-no-overwrite - refuse-overwrite guard; unbacked (unreachable), see ADR-0042 Verify note
func NextNumber ¶ added in v0.6.0
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-invariant: adr-new-sequential-numbering - NextNumber returns highest-plus-one; proof in adr_test.go
func RenderActiveMD ¶
RenderActiveMD renders the ACTIVE.md index for corpus, grouped by status. It returns a placeholder index when the corpus holds no ADRs (ADR-0020). The content carries no generated-by banner - like RenderDomainIndex, that is the caller's job (internal/project's generateActiveMD, via injectBanner) so every rendered artifact's banner comes from the one canonical source.
func RenderDomainIndex ¶
RenderDomainIndex renders the per-domain ADR index for corpus: every ADR whose domains frontmatter includes domain, grouped by status in the same order as ACTIVE.md, with links relative to docs/domains/ (one dir over) and each superseded entry annotated with its successor. Returns a placeholder line when no ADR matches, so the rendered section is never empty.
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)
Refs []SupersessionRef // inline partial-supersession tokens in the Decision section (ADR-0120)
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
}
ADR is a parsed ADR record.
func ParseBytes ¶ added in v0.18.0
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 (ADR) Bucket ¶ added in v0.18.0
Bucket is the ACTIVE.md section an ADR belongs to. Every superseded ADR folds into one group regardless of the successor its status names.
func (ADR) DecisionItems ¶ added in v0.18.0
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
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
HasSameStatus reports exact status equality without exporting literal comparisons to migration consumers.
func (ADR) HasStatus ¶ added in v0.18.0
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) IsImplemented ¶ added in v0.18.0
IsImplemented reports whether the ADR's decisions have shipped. Invariant backing and token retirement are both gated on this.
func (ADR) IsInflight ¶ added in v0.18.0
IsInflight reports a legacy decision that must be resolved before bridge attestation.
func (ADR) IsLegacyShipped ¶ added in v0.18.0
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
IsLive reports whether the ADR's decisions are current guidance.
func (ADR) IsProposed ¶ added in v0.18.0
IsProposed reports whether the ADR's body is still mutable.
func (ADR) IsSuperseded ¶ added in v0.18.0
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.
type Anchor ¶ added in v0.18.0
type Anchor struct {
ADR string // 4-digit target ADR number
Item int // Decision item number; 0 for a slug anchor
Slug string // invariant slug; "" for an item anchor
}
Anchor is one addressable decision unit: a Decision item or a declared invariant slug on a specific ADR. Anchors are the nodes of ADR-0129's model; claims are the edges. Exactly one of Item/Slug is set.
func (Anchor) Describe ¶ added in v0.18.0
Describe renders the anchor's local part for a human line: "item N" or "slug `<slug>`".
Named Describe rather than Label because ADR-0129's supersession-model-single-source requires the identifiers SupersessionIndex, Override, and Label to appear nowhere in the tree - a greppable check that the retired render index is really gone. A new method reusing that name would defeat the grep while looking innocent.
type Chain ¶ added in v0.18.0
Chain is one supersedence relationship for the ACTIVE.md index: a covered predecessor and every ADR that retired one of its anchors. The shape is one-to-many (ADR-0129 item 6) because coverage may split across successors, which is exactly why the scalar `superseded_by:` field could not express it.
type Citation ¶ added in v0.18.0
type Citation struct {
Carrier string // citing ADR's number
CarrierItem int // the citing ADR's own Decision item; 0 before the first
Anchor Anchor // the cited anchor
HasVerb bool // an override verb occurs in the same Decision item
}
Citation is one citation of another ADR's anchor inside a Decision item (ADR-0131 item 2). HasVerb records whether an override verb occurs in the same Decision item, which is what separates a mention from a claim the record is expected to encode.
Extraction lives here rather than in internal/project because ADR-0130's corpus-owns-field-reads confines Sections reads to this package: consumers take parsed citations and never touch raw section text.
type Claim ¶ added in v0.18.0
type Claim struct {
Anchor Anchor
Carrier string // claiming ADR number
CarrierItem int // the claiming ADR's Decision item that carries the token
Relation Relation // retirement or refinement
}
Claim is one edge: an ADR claiming an anchor, carrying the relation and the claiming ADR's own Decision item, so the rationale site is addressable (ADR-0129 item 2).
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
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
NewCorpus builds the view over an already-parsed slice. Construction is the single seam where derived structure is built, so nothing downstream rebuilds it (corpus-model-not-rebuilt).
func (Corpus) Anchors ¶ added in v0.18.0
Anchors returns the named ADR's anchors: its Decision items then its declared invariant slugs.
func (Corpus) AnnotatedAnchors ¶ added in v0.18.0
AnnotatedAnchors returns the claimed anchors of ADRs that are still live - the "superseded anchors on live ADRs" view. A covered ADR is excluded: its whole record is retired, so per-anchor annotation would be noise, and the chains subsection already names its retirers.
func (Corpus) ByNumber ¶ added in v0.18.0
ByNumber returns the ADR with the given four-digit number. The ADR number is the sole identity key (ADR-0130 item 4).
func (Corpus) Citations ¶ added in v0.18.0
Citations returns every anchor citation in the named ADR's Decision section, in no guaranteed order beyond shape grouping. An absent ADR, or one with no Decision section, cites nothing.
func (Corpus) ClaimsOn ¶ added in v0.18.0
ClaimsOn returns the claims made on the named ADR's anchors, ordered by anchor then carrier. This is the "who claims this" question ACTIVE.md's chains and the domain index both ask.
func (Corpus) DecisionItems ¶ added in v0.18.0
DecisionItems returns the Decision item numbers the named ADR enumerates. An absent ADR yields no items rather than an error: every caller is already validating existence separately, and a token into a missing target is that check's finding to report, not this one's.
func (Corpus) DeclaredSlugs ¶ added in v0.18.0
DeclaredSlugs returns the invariant slugs the named ADR declares, backed and unbacked alike, in declaration order.
func (Corpus) GraphFaults ¶ added in v0.18.0
GraphFaults returns the irreflexivity and acyclicity violations in the retirement relation (ADR-0129 item 7). Nothing previously forbade either: the single-claimant check that used to stand in for this died with the frontmatter encoding, and a self-targeting token or an A-to-B-to-A cycle would otherwise derive a coherent-looking state from a contradiction.
Acyclicity is scoped to ADRs the model classifies as Covered. A cycle among live ADRs is not yet a contradiction: partial claims in both directions are legitimate, since two ADRs may each refine or retire some of the other's anchors while both remain current. Only when the cycle's members are all fully retired does it assert that each is dead because the other is.
func (Corpus) Has ¶ added in v0.18.0
Has reports whether the corpus contains an ADR with the given number.
func (Corpus) Raw ¶ added in v0.18.0
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.
func (Corpus) RefsOf ¶ added in v0.18.0
func (c Corpus) RefsOf(num string) []SupersessionRef
RefsOf returns the supersession tokens the named ADR carries, in document order. This is the "what does this ADR claim" question consumers previously answered by ranging over ADR.Refs themselves (corpus-owns-field-reads).
The mirror question - "who claims this anchor" - is not here yet: nothing asks it until the coverage model needs it, and the dead-code gate refuses a production method no main can reach.
func (Corpus) Retirers ¶ added in v0.18.0
Retirers returns the numbers of the Implemented ADRs that retired at least one of the named ADR's anchors, sorted and deduplicated. This is what renders in place of the scalar successor the frontmatter used to carry: coverage may split across several carriers, so the answer is a set, not a name.
func (Corpus) State ¶ added in v0.18.0
State returns the ADR's derived supersession state. An unknown number is Live: it has no anchors anyone could have retired.
func (Corpus) UncoveredAnchors ¶ added in v0.18.0
UncoveredAnchors returns the named ADR's anchors that carry no counting retirement, in anchor order. The coverage-versus-status check names these when a `Superseded` ADR is not in fact fully covered, so the author is told exactly which decision still needs a successor rather than merely that the status is wrong.
type Cycle ¶ added in v0.18.0
type Cycle []string
Cycle is one retirement cycle among covered ADRs, as the sequence of ADR numbers that closes back on its first element.
type InvariantDecl ¶ added in v0.18.0
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 Relation ¶ added in v0.18.0
type Relation string
Relation distinguishes the three claims an ADR can record on an anchor. A retirement replaces the anchor and counts toward the target's coverage; a refinement adapts it and counts toward nothing, so an ADR whose items have only ever been refined is still live; a citation claims nothing at all and exists only to mark the reference informational (ADR-0128 item 2 for the first two, ADR-0131 item 4 for the third).
The split is corpus-driven: of the 37 pre-existing item tokens, 22 were refinements and only 13 genuine retirements. ADR-0034 item 1 is the live precedent - refined by ADR-0057, and only actually retired by ADR-0121 years later. Slug anchors have no refinement form: a slug is atomic, so `supersedes-invariant:` is always a retirement.
const ( // Retires is `supersedes:` (items) and `supersedes-invariant:` (slugs). Retires Relation = "retires" // Refines is `refines:`, items only. Refines Relation = "refines" // Cites is `cites:`: an informational citation asserting no claim on the // anchor. It contributes to nothing and exists only to suppress the // citation check (ADR-0131 item 4). Cites Relation = "cites" )
type SelfClaim ¶ added in v0.18.0
SelfClaim is an ADR whose own Decision section claims one of its own anchors.
type State ¶ added in v0.18.0
type State string
State is an ADR's derived supersession state (ADR-0129 item 3). It is computed from anchor coverage, never stored: the frontmatter keys that used to assert it are gone, and `status: Superseded` is checked against this rather than believed.
type SupersessionRef ¶ added in v0.18.0
type SupersessionRef struct {
Target string // 4-digit target ADR number, e.g. "0116"
Item int // Decision item number; 0 for an invariant ref
Slug string // invariant slug; "" for an item ref
// Relation is whether the claim retires the anchor or adapts it.
Relation Relation
// CarrierItem is the carrying ADR's own Decision item number - the item
// whose prose justifies the claim. ADR-0129 item 2 makes the rationale site
// addressable, which is the whole reason the token sits inside a Decision
// item rather than in frontmatter. 0 when the token precedes the first item.
CarrierItem int
}
SupersessionRef is one inline partial-supersession token (ADR-0120): `supersedes: ADR-NNNN#<item>` or `supersedes-invariant: ADR-NNNN#<slug>` as an inline code token inside a Decision section. Exactly one of Item/Slug is set; the key names the kind, never the anchor's shape.