skip to main content

NAME

kb-record — read and maintain decision records

SYNOPSIS

kb record list [–project P] [–workspace] [–status S] [–kind K] [–trigger T] [–initiative I] [–since DATE]

kb record show RECORD_ID [–project P] [–workspace]

kb record set-status RECORD_ID STATUS [–project P] [–workspace] [–root DIR]

kb record supersede NEW OLD [–partial] [–project P] [–workspace] [–root DIR]

kb record new –title T –trigger G (–project P | –workspace) [–kind K] [–dir DIR] [–root DIR]

kb record fmt PATH [–dry-run]

kb record concepts RECORD_ID [–project P] [–workspace]

kb record fuzzy-tag –project P [–concept NAME,…] [–write] [–dry-run] [–root DIR]

kb record delete RECORD_ID [–project P] [–workspace] [–root DIR] [–dry-run]

DESCRIPTION

A decision record is one file, indexed by ingest. new writes a project-scoped record to agents/projects/PROJECT/decisions/ by default (–dir overrides this) and a workspace-scoped one to agents/decisions/. Records are listed oldest first, sorted by date and then by id — never by id alone, because ids are identity, not chronology: a correction can carry a lower id than the record it supersedes.

A record id is not by itself an identity, since two projects may each have a DR-0001. Where a bare id is ambiguous, the command reports the candidates and asks for –project or –workspace rather than choosing one.

list
print matching records, one per line. –status, –kind, –trigger and –initiative filter on those fields and –since DATE (YYYY, YYYY-MM or YYYY-MM-DD) keeps records dated on or after it; filters combine. “no matching records” means a real filter matched nothing. A value that no record carries and the vocabularies below do not list is a typo, and is an error that names what is known (exit 1, a lookup that found nothing; record new and set-status, which write a value, exit 2 for the same thing). A record ID that exists in more than one tier is ambiguous and exits 2: qualify it with –project or –workspace. A value outside the vocabularies that some record does carry still filters. –workspace and –project cannot be combined, since workspace-tier records have no project
show
print one record with its body and its relations resolved in both directions. Only supersedes is stored; superseded_by is its inverse
set-status
set a record’s status in both its file and the database. The promotion path from proposed to accepted. Also refreshes the corpus’s index.md if one is already present, since status is one of the fields it renders — never creates one where the corpus has not already opted in. A status outside the vocabularies below is refused (exit 2) unless a record already carries it, the rule record new applies to –trigger and –kind and record list applies to its filters, so a typo such as “acepted” cannot leave a record in limbo; the file and database are untouched. Ingest of a hand-edited file still only warns
supersede
write both sides of a supersession — supersedes on NEW, superseded_by on OLD, the relation, and unless –partial, OLD’s superseded status. Both files and the database are written together or not at all. Also refreshes index.md, same as set-status, for both NEW’s and OLD’s corpora if either already has one
new
scaffold a record: allocate the next id for the tier, fill the fields a tool owns, set status to proposed, and print all five body headings whether or not they get filled. Writes the file; does not ingest it. –trigger is required here even though a converted record may carry an empty one, because on a newly authored record it is cheap and accurate to say where the need was discovered. –trigger and –kind follow the rule record list uses for its filters: a value outside the vocabularies below is refused unless a record already in the database carries it, and the error names what is known. That catches a typo before it is written into a file, where it would otherwise become a value the filter rule accepts
fmt
rewrite every record under PATH into canonical form. This is the normalisation path ingest deliberately lacks, since ingest never writes to a record file
concepts
list the concepts ingest linked to a record, from [Name] wikilinks in its body and its frontmatter tags list
fuzzy-tag
report near-miss spellings of known concepts in a project’s records (a detoast for the concept toast), which exact matching never links. Per record it lists the concept, the variants found, how many times, the plain exact mentions that link nothing (reported as “exact mention, not linked”) and the tags: line that would link the concept. Only the body is searched, never the frontmatter. A concept is skipped for a record only when the record already links it, in tags: or as a [[wikilink]]. Without –concept the same conservative length and distance rules as kb-document(1)’s fuzzy-tag apply; –concept NAME,… names concepts whose variants are reported up to the matcher’s ceiling (a 5-letter concept such as toast is under the default length floor, so name it). By default nothing is written. –write adds the concept to the tags: of records whose status is proposed and reports how many accepted records it skipped; an accepted record is history and is never modified, nor is any other status. Only the tags: line changes (a one-line flow list, a block list, or a new line if absent); a form it will not edit is refused and nothing is written, since writes are both-or-neither. –dry-run with –write says what would happen and writes nothing. The database is not touched: run kb ingest on the records directory to link the new tags. An unknown project or concept is exit 1. See DR-0052 (knowledge/decisions/).
delete
drop the database row of a record whose file is already gone: its relations, its concept links and its search entry go with it. A record’s file is the truth and ingest is additive, so a row whose file vanished otherwise stays (ingest only reports it). While the file exists this is refused (exit 1), because the next ingest of the changed file would only undo it, and kb never deletes a decision record from disk: delete the file yourself first, or retire the record with set-status cancelled. –dry-run reports and changes nothing.

new, set-status, supersede, fmt and fuzzy-tag –write are the only commands that write a record file; ingest never does. A record is written proposed and stays proposed: a model may write a record, but only the author accepts one.

EXIT STATUS

The workspace convention, as described in kb(1). For record fuzzy-tag: 0 the report was produced, or the tags were written, including “nothing found”; 1 no such project or concept; 2 a bad flag, a missing –project, or a surplus argument, and on any record verb a –concept or –write it does not take; 65 a record file that is malformed or whose tags: is in a form the edit will not change (nothing is written); 66 a record’s file is missing, or there is no workspace here; 74 a write failed part way (writes already made are undone).

VOCABULARIES

These are the documented values. They are reported against, not enforced: an unknown value parses and carries a warning, because a typo in a file several harnesses write should be a fixable row, not a failed run.

status
proposed, accepted, superseded, rejected, cancelled rejected means the decisions were never adopted. superseded means a later record replaced them. cancelled means they were adopted and then the work was abandoned: its reasoning still stands and is where anyone revisiting the question should start, but it is not live. Say why in the record’s body, since “the need was met another way” and “deprioritised” tell a later reader different things; there is no field for it. Use kb record set-status ID cancelled, without writing a replacement record.
kind
decision, correction, refinement
trigger
design, plan-review, implementation, live-test, release-review, request, external. May be empty on a record converted from an existing log

OPTIONS

–project P
restrict to, or resolve within, project P
–workspace
restrict to, or resolve within, the workspace tier
–partial
on supersede, leave OLD accepted instead of marking it superseded. Use when a later record invalidates one decision inside a multi-decision episode while the rest still stand
–title T
the new record’s title, on new. The filename slug is derived from it, lowercased with punctuation stripped; the slug is cosmetic and the id is the identity
–kind K
the new record’s kind, on new. Defaults to decision
–dir DIR
on new, write to DIR instead of the default (agents/projects/PROJECT/decisions, or agents/decisions on –workspace). Relative to –root, like every other stored record path
–dry-run
on fmt, report what would change and write nothing
–root DIR
the workspace root that stored record paths are relative to. Defaults to the parent of the directory holding the database

EXAMPLES

Every correction in one project, and everything since a date:

kb record list --project clasm --kind correction
kb record list --project clasm --since 2026-08-01

Promote a proposed record, then wholly and partially supersede:

kb record set-status 0004 accepted --project knowledge
kb record supersede 0149 0148 --project clasm
kb record supersede 0159 0160 --project clasm --partial

Start a new record, and bring a corpus into canonical form:

kb record new --project clasm --title "Retry the profile attach" --trigger live-test
kb record new --project clasm --title "Filed under the old layout" --trigger request --dir clasm/decisions
kb record fmt clasm/decisions --dry-run