API reference
ADR domain model: statuses, relations, errors and parsing.
TRANSITIONS = frozenset({(Status.PROPOSED, Status.ACCEPTED), (Status.PROPOSED, Status.REJECTED), (Status.ACCEPTED, Status.DEPRECATED), (Status.ACCEPTED, Status.SUPERSEDED)})
module-attribute
Status changes allowed without --force, as (from, to) pairs.
Adr
dataclass
One parsed ADR file.
meta is the round-trip frontmatter that gets written back; relations always has the five
STORED_RELATIONS keys. text is the whole file as read and body what follows the
frontmatter; number and slug come from the filename, title from the first # heading.
Source code in src/adrinaline/model.py
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
baseline
property
Start of the drift window: the later of date and reviewed (SPEC §8).
AdrError
Bases: Exception
A domain error with a stable machine-readable code (exit code 3).
Source code in src/adrinaline/model.py
51 52 53 54 55 56 57 58 | |
Frontmatter
Bases: BaseModel
Validated frontmatter. Hyphenated YAML keys fill the snake_case fields; the snake_case spellings are not read and fall into the extra keys. Other unknown keys are kept as well.
Source code in src/adrinaline/model.py
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
parse_adr(path, text)
Parse one ADR; path supplies the number and slug. Raises AdrError with code:
bad-filename; no-frontmatter (missing, unterminated, invalid YAML or not a mapping);
bad-date (unparseable date, or a bad date, review-by or reviewed value);
missing-field; bad-status; bad-scope; bad-field (any other invalid field); no-title.
Only the first validation error is reported.
Source code in src/adrinaline/model.py
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
Locate the ADR directory, load ADRs, resolve references and write files.
AdrSet
dataclass
Parsed ADRs sorted by (number, slug), the findings for files that did not parse, and every number seen in a valid filename (including files that failed to parse).
Source code in src/adrinaline/repo.py
43 44 45 46 47 48 49 50 | |
Location
dataclass
Where ADRs live: dir, git toplevel root (None outside git), template, and the
drift-commits threshold from config (0 turns stale-decision off).
Source code in src/adrinaline/repo.py
32 33 34 35 36 37 38 39 40 | |
load(loc, at=None)
Read and parse the ADR directory, or its state at git revision at.
Raises no-adr-dir when the directory is missing.
Source code in src/adrinaline/repo.py
161 162 163 164 165 166 167 168 169 170 171 | |
locate(cwd, env, explicit=None)
Find the ADR directory. First hit wins: explicit (--dir), ADR_DIR, config dir,
.adr-dir, then <git root or cwd>/docs/adr.
Config is the nearest .adrinaline.toml or pyproject.toml with [tool.adrinaline], searched
from cwd up to the git root; its paths are relative to the file. --dir and ADR_DIR are
relative to cwd. Raises bad-config for an unreadable or mistyped config.
Source code in src/adrinaline/repo.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
next_number(loc)
The next free number and the numbers it skipped.
One more than the highest number in the directory or on any other branch or remote-tracking
ref; skipped maps numbers above the local maximum that another ref already holds to that
ref. A misnamed NNNN-*.md file also holds its number.
Source code in src/adrinaline/repo.py
324 325 326 327 328 329 330 331 332 333 334 335 336 337 | |
parse_all(items)
Parse (path, text) pairs. Dotfiles and README.md are skipped silently; other names that
are not NNNN-slug.md give a bad-filename warning; a parse error becomes an error finding.
Source code in src/adrinaline/repo.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
renumber(s, adr, to)
Rename adr to number to; return the new path and the other ADRs rewritten.
Relations in other ADRs that point at the old number are rewritten, and NNNN-slug.md links
in every body are updated. If the old number is shared by several ADRs, the numeric relations
are left alone (they cannot tell which file is meant) and only the filename links change.
Raises exists if to is taken.
Source code in src/adrinaline/repo.py
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | |
resolve(adrs, ref)
Find the one ADR a reference names. All digits match the number; anything else is
slugified (Use PostgreSQL is use-postgresql) and matches an exact slug, else a slug that
contains it.
Raises not-found (with close matches as candidates) or ambiguous.
Source code in src/adrinaline/repo.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
set_status(adr, status, today)
Set the status and reset date to today, in memory only; the caller saves.
Source code in src/adrinaline/repo.py
235 236 237 238 239 240 | |
supersede(graph, old, new, *, force, today)
Make new supersede old: update both relation lists, set old to superseded (its
date is reset) and write both files.
Raises self-relation, invalid-transition (old is not accepted, unless force) or cycle.
Source code in src/adrinaline/repo.py
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | |
transition(adr, to, *, force=False, today)
set_status (so date is reset) if TRANSITIONS allows it or force, else raise
invalid-transition. Does not save.
Source code in src/adrinaline/repo.py
251 252 253 254 255 256 257 258 259 260 261 | |
In-memory ADR graph: typed edges, derived inverses, traversal and rendering.
AdrGraph
Typed edges between ADRs, built from their stored relations.
A stored superseded-by becomes a supersedes edge from the other ADR, so each pair appears
once. Edges may point at numbers with no ADR. With duplicate numbers, the first ADR is the
node. Inverse names (superseded-by, amended-by, dependency-of) exist only in related.
Source code in src/adrinaline/graph.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
cycle(relation, extra=None)
A cycle in relation as a list of numbers, else None. extra is a hypothetical edge.
Source code in src/adrinaline/graph.py
139 140 141 142 143 144 145 146 147 148 149 | |
governing(path, *, include_proposed=False)
ADRs in force whose scope globs match path (PurePosixPath.full_match, so **
spans directories). include_proposed appends matching proposed ADRs after them.
Source code in src/adrinaline/graph.py
190 191 192 193 194 195 196 197 | |
in_force()
Accepted ADRs that nothing supersedes, by number. Judged from the edges, so a
one-sided supersedes already counts.
Source code in src/adrinaline/graph.py
116 117 118 119 120 121 122 123 | |
label(number)
0003 Title, or 0003 (missing) when no ADR has that number.
Source code in src/adrinaline/graph.py
95 96 97 98 | |
rank(damping=0.85, tolerance=1e-09, max_iterations=100)
PageRank by number; the ranks sum to 1 ({} for an empty graph).
Counted edges: depends-on and amends, from the depending ADR to the one it depends on
or amends, and relates-to in both directions. supersedes is excluded, so replacing an
ADR does not move rank. Edges to numbers with no ADR are ignored.
Source code in src/adrinaline/graph.py
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
reachable(start, relations=FORWARD, *, reverse=False, depth=None)
Breadth-first walk from start over relations, each edge visited once.
reverse walks edges backwards and names each step by its inverse (superseded-by, ...).
relates-to is followed both ways either way. depth caps the levels (None: no cap).
Subgraph.steps are (parent, child, relation as seen from the parent).
Source code in src/adrinaline/graph.py
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
related(number)
Relation name to sorted numbers, seen from number: its own relations plus the derived
inverses (superseded-by, amended-by, dependency-of).
Source code in src/adrinaline/graph.py
105 106 107 108 109 110 111 112 113 114 | |
status(number)
The status value, or missing when no ADR has that number.
Source code in src/adrinaline/graph.py
100 101 102 103 | |
successor(number)
Follow superseded-by to the end of the chain; number itself if never superseded.
With several successors it takes the last (highest-numbered). A cycle stops the walk at the first number seen twice.
Source code in src/adrinaline/graph.py
125 126 127 128 129 130 131 132 133 134 135 136 137 | |
Edge
dataclass
src has relation to dst. Only forward relations are stored; inverses are derived.
Source code in src/adrinaline/graph.py
16 17 18 19 20 21 22 | |
Subgraph
dataclass
Result of AdrGraph.reachable: nodes start with the start ADR, in discovery order.
Source code in src/adrinaline/graph.py
25 26 27 28 29 30 31 | |
Validation rules for adr check. Each rule returns Finding objects.
run_checks(s, *, template_exists, tracked=None, changed=None, taken_elsewhere=None, today=None, commits=None, drift_commits=0, range_commits=None, uncommitted=None, skills=None)
Run every rule and return the findings sorted by (path, code).
Optional inputs switch rules on: tracked (files in the repo) enables scope-matches-nothing;
changed (files changed against a base) enables scoped-change, minus files whose commits in
range_commits cite or exempt the ADR (unless still in uncommitted); skills maps an
installed skill dir to its version; taken_elsewhere numbers are not reported as gaps.
commits feeds stale-decision. None means no git history was read: the threshold is forced
to 0, which disables the rule. [] means history was read and found nothing (or the rule was
disabled upstream); the threshold stays, but no ADR has churn, so the result is the same.
review-overdue does not depend on commits.
Source code in src/adrinaline/check.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |
Agent briefing for adr prime: decisions in force, proposals and usage rules.
Briefing
dataclass
The briefing Markdown plus the parts it was built from.
Source code in src/adrinaline/prime.py
80 81 82 83 84 85 86 87 88 89 | |
briefing(graph, dir_label, changed, budget=None, review=None)
Build the adr prime Markdown: ADRs in force, review findings, proposed ADRs and rules.
With a budget (words) the in-force list is cut by select, ranked against the changed
paths (SPEC §12); without one, all are shown and changed is unused. Proposed ADRs are never
cut. dir_label is the ADR directory as shown to the reader.
Source code in src/adrinaline/prime.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | |
scores(graph, adrs, changed)
Score each ADR in adrs from rank, a scope hit on a changed path and recency; weights
and formula in SPEC §12. Rank is normalised by the highest rank in the whole graph, recency
by the dates within adrs (1.0 when they are all equal).
Source code in src/adrinaline/prime.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
select(graph, adrs, changed, budget)
Fill a budget of words with adrs by descending scores (ties: lower number first).
The top-scored ADR is always taken, even over budget; after that an ADR that does not fit is skipped and smaller ones may still fill the space. Returns the chosen ADRs sorted by number and how many were left out.
Source code in src/adrinaline/prime.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
Split, parse and render Markdown files with YAML frontmatter (round-trip safe).
FrontmatterDateError
Bases: FrontmatterError
A date field in the frontmatter could not be parsed.
Source code in src/adrinaline/frontmatter.py
19 20 | |
FrontmatterError
Bases: ValueError
The file has no valid YAML frontmatter mapping.
Source code in src/adrinaline/frontmatter.py
11 12 | |
FrontmatterMissing
Bases: FrontmatterError
The file does not start with a frontmatter block.
Source code in src/adrinaline/frontmatter.py
15 16 | |
flow(items)
A list that renders in flow style ([1, 2]) instead of one item per line.
Source code in src/adrinaline/frontmatter.py
61 62 63 64 65 | |
render(meta, body)
Inverse of split: comments, key order and quoting of meta survive.
Source code in src/adrinaline/frontmatter.py
52 53 54 55 56 57 58 | |
split(text)
Split a file into its frontmatter mapping and the body after the closing ---.
Source code in src/adrinaline/frontmatter.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | |
Read-only wrappers around the git executable. Never stage, commit or fetch.
commits_in(root, revs, files=True)
Non-merge commits of a revision or range (HEAD, base..HEAD), newest first, with
trailers and (unless files=False) the files each one touched. [] when git fails.
Source code in src/adrinaline/git.py
195 196 197 198 | |
commits_since(root, since, rev=None)
Non-merge commits reachable from rev (default HEAD) since a date, newest first, with the files each one touched. [] when git fails (for example no commits yet).
Source code in src/adrinaline/git.py
185 186 187 188 189 190 191 192 | |
log(root, path, rev=None)
Commits that touched path, following renames, newest first, from rev (default HEAD).
Each row has commit, date, author and subject. Raises no-git without a repository;
[] when git fails.
Source code in src/adrinaline/git.py
129 130 131 132 133 134 135 136 137 138 139 140 141 | |
numbers_on_refs(root, directory)
ADR numbers in directory on local branches and remote-tracking refs, each mapped to the
first ref (in refname order) that has it. The current branch is skipped. {} if directory
is outside root or the refs cannot be listed.
Source code in src/adrinaline/git.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
read_dir_at(root, rev, directory)
(path, text) for each file directly in directory at rev, as repo.parse_all takes.
Subdirectories and missing objects are skipped; undecodable bytes are replaced. Raises
no-git (no repository or git), bad-revision, or no-adr-dir (outside the repository or
absent at rev).
Source code in src/adrinaline/git.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
run(args, cwd, stdin=None)
Run git and return stdout, or None if git exits non-zero or is not installed. The two cases look the same to the caller; it never raises.
Source code in src/adrinaline/git.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
verify_rev(root, rev)
Fail with bad-revision unless rev names a commit; --end-of-options keeps a rev such as
--output=f from reaching git as an option.
Source code in src/adrinaline/git.py
71 72 73 74 75 76 77 78 | |
Human and JSON output, the error envelope and exit codes.
Out
Output channel. result prints JSON in json_mode, else the text unless quiet; note
writes a hint to stderr unless quiet; rel shortens a path relative to base; paint adds
ANSI colour only when color is set.
Source code in src/adrinaline/output.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
adr_dict(adr, out, *, relations=None, body=False, **extra)
The JSON object for one ADR.
relations defaults to the stored relations that are not empty; pass one to show others, such
as derived inverses. meta holds the frontmatter keys that have no field of their own. body
adds the Markdown body; extra is merged last and wins on a clash.
Source code in src/adrinaline/output.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
error(err, json_mode)
Report err: a JSON envelope on stdout, or adr: code: message on stderr. Returns the
domain exit code (3).
Source code in src/adrinaline/output.py
147 148 149 150 151 152 153 154 155 | |
github_annotation(row)
One GitHub Actions workflow command for a check finding.
Source code in src/adrinaline/output.py
46 47 48 49 50 | |
gitlab_report(rows, fallback)
A GitLab Code Quality report for check findings; set-wide ones point at fallback.
Fingerprints hash code, path and occurrence index, not the message, which carries volatile text such as commit hashes and churn counts.
Source code in src/adrinaline/output.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
Decision health: churn over an ADR's scope since its baseline, and the review findings.
Drift
dataclass
Commits that touched one ADR's scope since its baseline.
Source code in src/adrinaline/drift.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
compute(adrs, commits)
One Drift per ADR with a scope, highest churn first. Churn counts commits whose committer date is a later day than the baseline (SPEC §8); ADRs without a scope are skipped.
Source code in src/adrinaline/drift.py
36 37 38 39 40 41 42 43 44 45 46 47 | |
earliest_baseline(adrs)
Start of the one git log call that serves every scoped ADR.
Source code in src/adrinaline/drift.py
31 32 33 | |
review_findings(adrs, drifts, today, threshold)
review-overdue for every ADR whose review-by has passed; stale-decision for every
drift at or above the threshold (threshold 0 disables it). Order: overdue first, then
stale in drift order.
Source code in src/adrinaline/drift.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
Repairs for mechanical check findings (ADR 0010).
Fix
dataclass
One repair made by apply: the finding code, the file path and a short message.
Source code in src/adrinaline/fix.py
12 13 14 15 16 17 18 | |
apply(s, loc)
Repair the codes ADR 0010 lists, write the files, return what changed.
Renamed keys return early; call again on a reloaded set until it returns nothing.
Source code in src/adrinaline/fix.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |