Skip to content

Latest commit

 

History

History
735 lines (607 loc) · 68.4 KB

File metadata and controls

735 lines (607 loc) · 68.4 KB

CLI

All commands, all flags, all exit codes in one page. Source: packages/archkeep/cli.mjs.

Commands

command positional args summary finds violations
check [<path>...] Check imports against the boundary rules yes -- exits 1
graph (none) Print the project graph as a deterministic snapshot no
diff <baseline> Compare two graph snapshots edge by edge no
delta <baseline> | --capture Classify how boundary violations moved between a captured baseline and head yes -- exits 1
change <baseline> --intent <file> Reconcile a declared change intent against the architectural delta yes -- exits 1
drift (none) Compare the observed architecture to the declared intent no
discover (none) Report observed facts, and optionally propose candidates no
reconcile (none) Score the declared intent against the observed architecture, with proposed edits under --propose no
fitness (none) Judge every declared fitness function against the workspace; exits 1 on a failing function no*
waivers (none) List the boundary waivers and permanent suppressions on the table no
history <dir> Describe how the architecture evolved across snapshots no
trajectory <dir> Aggregate the deterministic drift trajectory across snapshots no
evolution (none) Describe how the architecture evolved across a Git revision range no
health [<snapshot-dir>] Describe architecture health metrics and trends no
report [<snapshot-dir>] One governance document: how healthy the architecture is, and why no
debt <dir> Print the architecture-debt ledger across snapshots no
impact <project> List projects that depend on the named project no
scenario <project> Evaluate a hypothetical change against the current workspace no
explain <file:line:column> Explain the judgment for one import site no
context <project> [--plan [<path>...]] Show the architecture constraints that apply to a project no
provenance (none) Describe where this run's facts came from and which rows carry an origin no
decisions <id> Walk the full chain behind one recorded decision — decision to bound rows, projects, findings, and its verification level no
adr [<id>] List recorded architecture decisions and what each binds no
rules `<list info verify
* fitness reports no boundary violation, but it is a verdict command, not a
descriptive one: a declared function that fails makes it exit 1 (and an
undetermined one, 3) — see the prose below. rules verify also exits 1 when
catalog integrity finds violations.

archkeep --help prints the help text and exits 0. archkeep --version (or -v) prints the tool name and version and exits 0. An omitted command name is a usage error (exit 2). If the first positional argument names a path that exists on disk, it is treated as check scoped to that path, the same as archkeep check <path>.

Flags

Every --config/boundaryConfig below reads the same way: a file path, unless the workspace names a profiles registry, in which case it is a profile NAME instead — check's own row states the mechanism once; profiles.md is the full model. This applies to every command below that takes a --config flag, not only check.

check

flag argument default meaning
--format text|sarif|json text Terminal report (default), SARIF 2.1.0 for GitHub code scanning, or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file — which, when the workspace names a profiles registry, is a profile NAME selected from that registry, not a path.
--evidence-out <dir> (nothing written) Also write each declared custom rule's evidence bundle into this existing directory, as <rule>.json — the exact document that rule was judged over. It changes no verdict and no exit code, and it writes the bundle even for a rule that trapped or ran out of budget, which is when it is needed. Three ways it writes nothing — no customRules declared, a path-scoped run, and a declared law that could not be loaded — and the first two say so on stderr rather than leaving an empty directory.

Naming paths scopes the run to those files. A scoped run is a fast local pre-check, not the gate: the cycle and lazy-load rules judge the file graph as a whole, so a scoped run can miss what a whole-workspace run would find — and declared fitness functions that need the whole tree, like every declared custom rule (custom-rules.md), answer not_applicable there rather than judging partial evidence.

A policy that declares fitness or customRules gets both judged on every unscoped check, by presence — there is no flag to forget, and their verdicts ride the same exit lanes as the boundary rules (exit-codes.md).

graph

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.

No positional arguments. Takes no --config flag -- graph describes the project graph, not the boundary law.

diff

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Rule-impact analysis appears whenever a boundary config is available.

The baseline file is a positional argument (a file, not a git ref). Both sides must be complete; an incomplete baseline or current workspace exits 3 and produces no diff. When a boundary config is available, the report includes a rule-impact section showing boundary violations introduced or resolved by the diff.

delta

flag argument default meaning
--capture (none) off Print an evidence snapshot of the current tree (raw import records, graph, coverage, policy fingerprint) for a later delta run to compare against. Without --output, the snapshot goes to stdout.
--format text|sarif|json text Terminal report (default), SARIF 2.1.0 of the introduced findings for GitHub code scanning, or the versioned JSON envelope.
--output <file> stdout Write the report — or, with --capture, the snapshot — to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Both sides are re-judged under whichever law this run resolves.
--event-out <dir> (nothing written) Append the delta's evolution event to this directory (one canonical record per transition; idempotent — a rerun over the same transition writes nothing new; refused loudly when the head is uncommitted or the tree is dirty — docs/concepts/evolution.md owns the event-identity precondition).

Without --capture, the baseline evidence snapshot is the single positional argument (a file, not a git ref); with --capture there are no positionals. Unlike every other descriptive-family verb, delta's compare mode is a gate: a non-waived introduced violation exits 1 — see the prose below and ../usage/delta.md.

change

flag argument default meaning
--intent <file> (required) The change-intent manifest declaring the material architectural consequences this change expects (../usage/change.md).
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout. Refused when it resolves to the manifest itself.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Declared constraints are re-judged under whichever law this run resolves.
--event-out <dir> (nothing written) Also write the reconcile EvolutionEvent to this directory (one file per run, idempotent; the classification always rides the envelope result — see docs/concepts/evolution.md); refused loudly when the head is uncommitted or the tree is dirty — the event-identity precondition that page owns.

The baseline evidence snapshot (delta --capture output) is the single positional argument. A verdict, not a description: an undeclared material change, an unfulfilled declaration, or a failed declared constraint exits 1; an unproven base identity or an undeterminable constraint exits 3. The workspace-law axis it reports is informational — check remains the authority on the law.

drift

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.

No positional arguments. Drift is descriptive — it never exits 1, only 0 on a completed comparison and 3 when coverage is incomplete or the intent cannot be verified. The intended side is the tracked architecture-intent.json at the workspace root; load it, describe the findings, print the intent fingerprint, and let check do the failing. A boundary or row side that matched no observed project so the comparison cannot be completed exits 3 with a loud message — "cannot verify" must never read as "no drift".

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.

The optional positional argument names the snapshot directory for trends (the same .archkeep/history/ directory history reads); with no argument, health reports the current run's metrics without a trend. Health is descriptive — it never exits 1 — and it exits 3 whenever any metric reads unknown: a run that could not fully inspect its own evidence is not a healthy run, and "cannot look" must never read as "clean".

report

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.

The optional positional argument is health's — the snapshot directory for trends. The --config this command resolves governs every section of the document, which is what keeps the page citing one law rather than several.

fitness

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.

No positional arguments. Fitness is a verdict, not a print job (D-09): it exits 1 on a failing function and 3 on an undetermined one — the same two lanes check uses — and 0 only on a completed judgment with nothing failed or undetermined. It exits 3 when coverage is incomplete or the policy declares no fitness at all. Each declared function is judged against the observed workspace and printed as a verdict row; check folds the same verdicts in by presence.

discover

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--propose (none) off Compute and print the candidate architecture — components, boundary assertions, tag vocabulary, rules — over the observations.
--write-intent <file> off With --propose, write the proposed components and rules to a valid architecture-intent.json file (review before use). Refuses to overwrite a file that already exists.

No positional arguments. discover is descriptive: it never exits 1, only 0 on a completed observation and 3 when coverage is incomplete, the model cannot be loaded, the plugin gap refuses the graph, or a --write-intent write was refused (target exists) or failed. Under --propose, incomplete coverage is a refusal — a proposal over an unread tree would be a fabrication wearing a proposal's name. Every candidate carries the markers proposed: true and notAuthoritative: true; the one write that materializes them is the --write-intent <file> above — explicit, named by the operator, never overwriting — and discovery.md owns the contract.

reconcile

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--propose (none) off Emit a ranked candidate list of model edits, each marked proposed — never written into architecture-intent.json.

No positional arguments. Reconcile is descriptive — it never exits 1, only 0 on a completed comparison and 3 when coverage is incomplete or the intent cannot be verified. It never writes into architecture-intent.json; --propose adds the ranked candidate list (add, removal, tag-change, boundary-change) marked proposed: true / notAuthoritative: true. The intended side is the tracked architecture-intent.json at the workspace root. A boundary or row side that matched no observed project so the comparison cannot be completed exits 3 with a loud message — "cannot verify" must never read as "no divergence". See reconciliation.md and usage/reconcile.md.

impact

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Constraint context appears whenever a boundary config is available.

The project name is a single positional argument. An empty dependents list is a claim ("nothing depends on this"), not a shrug. When a boundary config is available, the report includes a constraint-context section showing which constraint rows govern each dependent's edge and whether it violates them.

scenario

flag argument default meaning
--format text|json text Terminal report (default) or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Constraint-impact analysis depends on which boundary law is in effect.
--scenario-file <file> (required) Path to the scenario JSON file describing the hypothetical changes to evaluate.

The project name is a single positional argument. --scenario-file is required. A scenario is descriptive: it evaluates what-if changes without claiming a violation, so it exits 0 when it completes. A malformed scenario file, an unreadable file, incomplete coverage, or an unregistered-plugin graph gap is a no-verdict run (exit 3), never a clean evaluation.

explain

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.

The site argument is a single file:line:column string, 1-based. --config is accepted because the judgment depends on which boundary law is in effect. A site whose target is not statically knowable (dynamic import() with a non-literal argument) gets an UNRESOLVABLE verdict with the reason.

context

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.
--plan [<path>...] off Planning mode: the positionals after the project name are intended file paths, judged before any edit exists — see the prose below.
--history-dir <dir> (nothing) Path to the workspace's history directory. When given and the directory holds archived snapshots, the planning context includes the architecture-debt snapshot: current violations, exemptions, and gaps aged across the history. Used only with --plan; ignored otherwise.

The project name is a single positional argument. --config is accepted because the answer depends on which boundary law is in effect — a different constraint table produces a different set of matching rows.

waivers

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. The surface listed is the one this law carries.

No positional arguments. Lists every boundarySuppressions row carrying an expiresAt — a waiver — with its term and the current violations it covers, and every row with no expiresAt — a permanent suppression — with the violations it is currently hiding. Coverage is judged against the full finding set with the suppression table removed, so a row that covers nothing reads as stale. A tree whose only violations are permanently suppressed does not read as "no waivers — every boundary is enforced": this command names the suppression and what it hides instead. Descriptive: it exits 0 whenever the surface could be read, never 1.

history

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--capture (none) off Append a snapshot of the current workspace to the directory first, then describe the history that includes it.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file. Under --capture, the captured snapshot records the fingerprint of this law as the architectural intent in effect.

The directory is a single positional argument. It is a directory of graph --format json snapshots, not a git ref or an index file — the directory itself is the sole source of truth (see docs/usage/history.md). --capture writes <sequence>-<sha8>.json (zero-padded monotonic sequence plus the architecture identity's first eight hex chars, so filename byte-sort IS history order) and deduplicates when the current architecture identity already is the last snapshot and the provider has not changed — a pure provider migration surfaces as a transition rather than being swallowed by the identity match. An empty directory, an unreadable snapshot, or a malformed snapshot is a no-verdict run (exit 3), never a record of nothing.

trajectory

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.

The directory is a single positional argument — the same consumer-managed history directory history and debt read. There is deliberately no --config: the policy fingerprints being compared travel inside the stored snapshots, so each observation is judged under the law it was captured under, never under today's law. And there is no --capture: exactly one command writes snapshots, so exactly one way exists for a record to enter the history. An empty, unreadable, or malformed history directory is a no-verdict run (exit 3), never a clean trajectory; a directory holding ONE snapshot answers with an explicit insufficient_history at exit 0 — derived values are unavailable, never zero (see docs/usage/trajectory.md).

evolution

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--base <rev> (none) Required. The baseline revision — a commit SHA, branch, tag, or HEAD~n; the first revision analyzed.
--head <rev> HEAD The tip revision; must be a linear descendant of --base, with no merge commits between the two.
--event-out <dir> (nothing written) Append one EvolutionEvent per revision pair to this directory (idempotent — a re-run over the same pair records a duplicate, never a second event). docs/concepts/evolution.md owns the event model.

No positional arguments and no --config — each analyzed revision is judged under the boundary law its own tree declares, so a policy change can be attributed to the revision that made it rather than to whichever law the caller handed in. Git answers only which trees to read: both endpoints are resolved with git rev-parse --verify <rev>^{commit}, base must be an ancestor of head, every commit in between must have exactly one parent, and each selected commit is analyzed in a temporary detached worktree (git worktree add --detach) that is removed before the run reports — the caller's working tree is never touched. Any unusable selection (unknown revision, coincident endpoints, non-ancestor base, a merge inside the range) or any revision that cannot be fully analyzed is a no-verdict run (exit 3), never a shorter history.

debt

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.
--events <dir> (nothing linked) Link the evolution event store in so debt entries carry introducedBy/resolvedBy refs and a resolved list.

The directory is a single positional argument — the same consumer-managed history directory history reads, so the ledger ages across the same snapshots the evolution record is built from. The ledger is a report, never a gate: it lists the workspace's waivers (accepted violations), aspirational gaps (optional intent rows not yet built), and drift findings, each ranked by severity, aged across the snapshots (see docs/reference/debt.md for the four entry kinds, the age model, and what agings: false means). An unresolved intent, incomplete coverage, or an unreadable/malformed history directory is a no-verdict run (exit 3), never an empty ledger — an entry that cannot be read or verified must never read as "no debt".

provenance

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.

No positional arguments. provenance takes no --config flag — it reads the workspace's own declared files.

decisions <id>

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.
--config <file> (from workspace options) Read the boundary law from here instead of the workspace's configured file.

Exactly one positional argument: the ADR id whose chain to walk (0001-layers, or the adr:-prefixed spelling). It reads the boundary law because the chain's fitness leg reads the workspace's declared gates.

adr [<id>]

flag argument default meaning
--format text|json text Terminal report or the versioned JSON envelope.
--output <file> stdout Write the report to a file instead of stdout.

No --config flag — a description of what is recorded needs no boundary law.

  • Both --flag value and --flag=value work.
  • An unknown flag is a usage error (exit 2) rather than treated as a path. A typo like --fromat sarif would otherwise select no files and report a clean tree.
  • --format changes no exit code and no byte of the other formats. It is an additional rendering of the same verdict.
  • --output writes atomically (write to .tmp, then rename) so a reader never sees a truncated file. A write failure is exit 3. For history, pointing --output at a file inside the history directory is a usage error (exit 2) — the report would be read back as a snapshot on the next run.
  • --config does not move the workspace root. The tree being judged is still the consumer's.

Exit codes

code meaning when
0 clean -- and every selected file was analyzed No findings and no coverage gaps.
1 findings -- boundary violations, declared-edge violations, go.work drift, dead tsconfig path aliases, intent findings, a failing fitness gate or custom rule, a non-waived violation delta classifies as introduced, or a change-intent reconciliation that found undeclared material changes, unfulfilled declarations, or a failed declared constraint check, fitness, delta, and change. A failing fitness function is a finding (D-09), so is a custom rule's fail, so is a violation this change introduced, and so is an architectural consequence the change did not declare; every other command that finds something reports it but exits 0.
2 usage error Unknown command, unknown flag, missing argument, path outside the tree, wrong positional count.
3 no verdict -- the run could not start, a selected file could not be read, or the law itself could not be established No workspace, malformed config, moon project-graph/nx graph/git failed, unreadable file, file with no analyzer, tsconfig that will not load, a tracked architecture-intent.json that will not parse or whose boundaries match no project, a declared custom rule whose artifact will not load or that answered unknown (custom-rules.md), a change intent that cannot be verified against its declared base, or -- in a profile-selected workspace, on any command that reads a boundary law -- a profile that could not be resolved: an unknown profile name, an unknown base, a base cycle, or an unreadable registry.

Do not collapse 3 into 0. A checker that could not look must never be mistaken for one that looked and found nothing. Both 1 and 3 must fail a CI build; they differ in what you go and look at, not in whether you go and look.

check also covers partial failures: an unreadable file, a file with no analyzer, or a tsconfig that will not load each leaves a file the summary counts but no rule ever judged, and that is enough to withhold the verdict.

A descriptive command (graph, diff, drift, discover, reconcile, waivers, history, trajectory, evolution, health, report, debt, impact, scenario, explain, context, provenance, adr, decisions) exits 0 when it completes, 3 when coverage is incomplete or a metric is unknown, and 2 on usage error. None exits 1, because a descriptive result is never a finding. decisions names its gap loudly: a walk whose every hop resolves exits 0; an unknown id, or a binding that names no governed row, exits 3 — a chain that could not be established is never rendered clean. fitness, delta and change are the verdicts — a failing fitness function exits 1 (the check lane) and an undetermined one exits 3; a delta comparison exits 1 on a non-waived introduced violation and 3 on a refusal or an unclassifiable item, while its --capture mode stays descriptive (0 or 3, never 1); a change reconciliation exits 1 on undeclared material changes, unfulfilled declarations, or a failed declared constraint, and 3 on an unproven base identity or an undeterminable constraint.

What each command does

check [<path>...]

Reads the project graph, analyzes every tracked source file a project owns, judges the import sites against the workspace's boundary law, and exits 1 if anything violates it. When the workspace has a tracked go.work, also compares its use list against every project's go.mod. When the workspace tsconfig declares a paths table, also judges each alias for life. Both are workspace- level checks that ignore path scoping.

graph

Prints the project graph as a deterministic snapshot: two sorted arrays, one of projects and one of edges, with workspaceLayout included. Descriptive -- a snapshot of what is is never a finding.

It answers in a workspace that has no boundary law yet, which is where the question it exists for is usually asked: the first thing to establish about a new workspace is what Archkeep sees, and that is what the first policy gets written against. The snapshot then carries no policy field -- no law, no policy identity for diff to compare against. A boundary config that IS there and will not load still fails the run with exit 3, because an absent law and a broken one must not report alike.

Reports the observed architecture: projects, edges, tags, and the coverage a verdict over this tree could trust. With --propose, it also derives the candidate architecture those observations imply — components, boundary assertions, tag vocabulary and rules — each marked proposed: true and notAuthoritative: true. With --write-intent, the proposed components and rules are written to a valid architecture-intent.json file (review before use with drift or reconcile) — and refuse to overwrite a file that already exists, because a proposal must never silently replace what is there. Descriptive -- an observation, or a candidate, is never a finding.

diff <baseline>

Compares a graph --format json snapshot file with the current workspace, reporting projects and edges added or removed. When a boundary config is available, also reports which violations the added edges introduce and which the removed edges resolve. The baseline is a file, not a git ref. Both sides must be complete. Descriptive -- changes do not make it exit 1.

delta <baseline> | --capture

Two modes behind one verb. --capture writes an evidence snapshot of the current tree — raw import-site records, the graph they were collected against, coverage, provenance, and the policy fingerprint — for a later run to compare against. delta <baseline> loads such a snapshot, re-judges both sides through the rule engine under the current boundary config and one shared reference instant, and classifies every violation introduced | resolved | unchanged | unknown, plus a separate carried category for unresolvable import sites. A non-waived introduced violation exits 1; an unclassifiable item exits 3; an introduced violation the current waiver table covers is reported but does not gate. It refuses (exit 3) an unreadable, malformed, or foreign-schema baseline, a baseline captured under a different provider, an incomplete side, and the unregistered-plugin graph; a policy change between capture and now is a loud note, not a refusal, because both sides are judged under the current law. ../usage/delta.md owns the model.

change <baseline> --intent <file>

Reconciles a declared change-intent contract against the architectural delta actually observed between the captured evidence baseline and this tree — "did this change do exactly what it declared, architecturally?". The material delta is diff's own computation over both graphs; declared constraints are judged by re-running both sides through the rule engine under the current law (the delta arrangement). Verdicts: matched | undeclared (observed material changes no declaration covers) | unfulfilled (declared changes that never happened) | unproven (the manifest's base.commit does not match the baseline's provenance, or the baseline carries none). Undeclared, unfulfilled, or a failed declared constraint exits 1; unproven or an undeterminable constraint exits 3; matched with every declared constraint passing exits 0. The workspace-law axis it reports is informational and never moves its exit code — a change can be policy-compliant but undeclared, policy-invalid but intent-matched, both, or neither, and each combination is reported separately. ../usage/change.md owns the model.

waivers

Lists every boundarySuppressions row carrying an expiresAt — a waiver — with its term and the current violations it covers. Coverage is judged against the full finding set (the table removed), so a row that covers nothing is flagged as stale rather than silently doing nothing. A waiver that has lapsed is listed as expired with its remaining time; it covers nothing and the violation it accepted re-asserts.

It also lists every row with no expiresAt — a permanent suppression — and how many violations each is currently hiding: that removal never appears in check's findings at all, so this is the only command that names it. result.suppressions carries the rows, result.suppressed the count of distinct violations they hide, and the text report never collapses "no waivers" into "every boundary is enforced" while a permanent suppression is covering something this run measured. A waivers run is descriptive and never modifies the table — waivers and suppressions are both removed only by an explicit edit, never by the tool. Descriptive.

history <dir>

Reads every graph --format json snapshot in a directory and describes how the architecture evolved across them: each snapshot in history order (filename byte-sort) and each transition between consecutive snapshots, classified by the signals the snapshots actually carry. A changed graph is an architecture change; a changed policy.fingerprint is a policy/intent change; a changed workspace.provider is a provider change; provenance (git commit) advancing while neither architecture nor policy changed is disclosed as code drift. What a snapshot does not carry is disclosed, never asserted. --capture appends a snapshot of the current workspace before describing the record. Descriptive -- evolution never makes it exit 1.

trajectory <dir>

Reads the same history directory history reads and aggregates it: over ALL observations, how often each transition signal fired (architecture, policy, provider, code drift), what the graph gained and lost in total versus net (events against endpoint deltas, so churn and movement stay distinguishable), and which projects and edges persisted through every observation. One observation is one stored graph snapshot — a capture point, not a commit or a day — and the result names that basis. Identities are diff's own (project name; edge (source, target, type) triple); no violation-level trajectory is reported because stored snapshots carry no findings. A trend here is a fact that moved, never a score: nothing weights the signals, and no aggregate reads as "healthier" or "worse". Descriptive -- aggregation never makes it exit 1. See ../usage/trajectory.md.

evolution --base <rev> [--head <rev>]

Resolves both revisions in the workspace's own repository, materializes every selected commit into a temporary detached worktree, analyzes each through the ordinary pipeline, and describes the transitions between consecutive revisions — classified by exactly the same signals and code history uses, with the commit SHA standing in for the snapshot filename. A change is attributed to the first analyzed revision where it is observable — a fact about where history shows it, never about why it was made. Linear ranges only: a merge commit inside the range refuses the run rather than pinning a whole branch's changes on one commit. Descriptive -- it never exits 1.

health [<snapshot-dir>]

Reports deterministic architecture-health metrics for the current workspace: project and edge counts, the coverage ratio, boundary violations and the waiver surface, cycle count, edge density, debt rows from the config's own notes, and the intent (fitness) verdict. Each metric carries a verdict in the canonical vocabulary — ok, findings, not_applicable (nothing to measure) or unknown (evidence could not be fully inspected), and a metric that did not measure carries no number. Given a snapshot directory, the same structural metrics are reported across the snapshots history reads, with the disclosure that rule-impact cannot be re-derived from stored bytes. Descriptive — a description of health is never itself a finding.

report [<snapshot-dir>]

One governance document: the health metrics, the waiver and permanent-suppression table, the declared fitness gates, the recorded decisions each governed row cites, and the run's provenance — composed from the same functions health, waivers, fitness, adr and provenance call, so no section can disagree with the command that owns it. One boundary law, resolved once, governs the whole page. Each governed row carrying a decisionRef is linked to the record it cites and that record's status; a citation resolving to nothing reads unknown, never a pass. Descriptive: it never exits 1 — a live violation or a failing gate is printed over exit 0 — and it exits 3 when any surface could not be established, with the closing could not inspect block naming every one. See ../usage/report.md for the full report shapes.

debt <dir>

Reads the same history directory history reads and builds the architecture-debt ledger: every waiver the boundary config accepts, every optional intent row not yet built, and every drift finding the intent judge reports — each aged across the snapshots by the owning project and ranked by severity. A drift finding in a project that also carries an accepted waiver is ranked HIGH and the waiver is still listed; the ledger must never hide a waiver that is failing today. Descriptive -- debt never makes it exit 1.

reconcile

Scores the declared intended model against the observed architecture element by element — projects, edges, tags, boundaries, and every intent row in file order — and reports the divergence plane by plane. --propose adds the ranked candidate list of model edits (add, removal, tag-change, boundary-change), each carrying its evidence and an explicit proposed / notAuthoritative marker; the list is a suggestion, never an applied change, and the intent file stays byte-identical after every run. Descriptive.

impact <project>

Lists every project that transitively depends on the named project. Separates direct from transitive dependents. When a boundary config is available, also shows which constraint rows govern each dependent's edge and whether it violates them. Descriptive.

When a boundary config is available, --format json additionally carries an impactStatement field — a composed impact statement that includes decision impact (which recorded ADRs bind the affected constraint rows, with their lifecycle status and authority) and evolution alignment (the affected shape matching the EvolutionEvent.affected vocabulary: projects, boundaries, constraints, and decisions). A decisionRef that does not resolve is reported in unresolvedDecisionRefs, never silently dropped. See json-output.md for the full schema. Descriptive.

scenario <project>

Evaluates a hypothetical change against the current workspace and reports the current-versus-scenario comparison. The project name is a single positional argument. The hypothetical change is described in a JSON file passed via --scenario-file; the supported change types are dependency_added and dependency_removed, each naming a source and target project.

The evaluation is virtual — it never mutates the workspace, never writes to canonical history, and never emits an EvolutionEvent. Every output field carries a virtual: true / notAuthoritative marker. The scenario's would-be state is derived by applying the changes to the current graph and re-running the deterministic impact path, then compared against the current state.

When a boundary config is available, the report includes constraint-impact and decision-impact analysis for both the current and scenario states, plus a delta showing which dependents, constraints and decisions would change. Per-edge verdicts cover only depConstraints (3 of 15 violation types). Descriptive — it never exits 1.

explain <file:line:column>

Explains the judgment for one import site: which constraint row matched, which tags applied, whether it is a violation and why. Constraint rows that carry description or remediation show those fields; a violation whose row declares no remediation prints an explicit none declared pointer at the row and its decisionRef rather than nothing. A site whose target is not statically knowable gets an UNRESOLVABLE verdict with the reason. --format json additionally carries a site-level result.verdict and, on every violation entry, guaranteed remediation and allowed fields (json-output.md). Descriptive.

context <project>

Shows the architecture constraints that apply to a project: its tags, which depConstraints rows match those tags, and what each row allows or bans. Constraint rows that carry description or remediation show those fields. Useful before editing a project — the same constraint table check judges from, rendered as a readable summary rather than as a list of violations. Descriptive.

fitness

Judges every declared fitness function against the observed workspace: one line per function ✔ / ✖ / ⚠ / ◌, then an overall posture, all deterministic and clock-free. A verdict, not a print job — it exits 1 for any fail, 3 for any unknown (D-09). check folds the same per-function verdicts into its exit code — 1 for any fail, 3 for any unknown, never a new code.

context <project> --plan [<path>...]

Requests the agent architecture planning context: the deterministic facts a coding agent needs before planning a change to project. Trailing paths scope the change (which project roots or files it touches); with no paths, the whole workspace is in scope.

The planning context is facts, not a plan. Archkeep reports the current architecture snapshot, the applicable policy with the author's Intent (description/remediation), the canonical architecture-intent.json verdict (the same model check and drift judge — findings, no-verdict, or ok; absent when the workspace declares no intent), the impact of a change to the target project (dependents capped at 10 with an explicit overflow note), the current violations (the full-workspace rule-engine verdict, scoped for reporting), drift (go.work and tsconfig-path aliases, null when no manifest exists to read), coverage with the exact files that could not be analyzed, and the deterministic commands that verify the change afterwards. It never generates an LLM plan, decides an implementation strategy, or modifies source code — an agent reasons over these facts.

--plan is strictly additive: it changes no exit code and no byte of the plain context text output. In JSON the plan's fields sit under result.plan alongside the unchanged result.project/tags/constraints/dependencies (the four plain context fields keep their existing shape — see json-output.md), and result.plan.variant is "plan" so a consumer can tell the two apart. The rule verdict is computed over the whole analyzeable tree (so whole-graph rules such as circular-dependency and lazy-load are correct on every provider); on Nx and Moon workspaces this is a second analysis pass, which costs more than a plain context run. The JSON output is deterministic: two runs over an unchanged tree produce byte-identical bytes. Descriptive.