Architectural Decisions in the LLM Wiki¶
The LLM Wiki's decision plane extracts Architectural Decision Records (ADRs) from your codebase and answers "why" questions with cited decision excerpts. It can also generate labeled candidate rationales for code sections that lack documented decisions. However, it deliberately does not turn an inference into documented history — accepting a candidate records the team's agreement, but the record stays inferred with an unknown source status forever.
Quick Start¶
# Build the wiki (ADRs refresh automatically)
wikitoolkit build
# Look up decisions that apply to a symbol
wikitoolkit adr lookup sym:parrot.knowledge.wiki.cli.WikiStore
# Answer a "why" question with cited decisions
wikitoolkit adr why "why does the wiki use SQLite?"
Documented vs Inferred — Read the Labels¶
The central concept is the distinction between documented and inferred decisions:
| Origin | Review Status | Meaning | Licensed Conclusion |
|---|---|---|---|
DOCUMENTED |
ACCEPTED |
ADR file in the repo, accepted by the team | This is documented history |
DOCUMENTED |
UNKNOWN |
ADR file in the repo, never reviewed | This is documented but unvalidated |
INFERRED |
UNREVIEWED |
Generated candidate, never reviewed | This is a hypothesis only |
INFERRED |
ACCEPTED |
Generated candidate, accepted by the team | The team agrees with this rationale |
An accepted candidate is a team agreement, not a historical record. The origin stays inferred and the source status stays unknown forever.
What Gets Parsed¶
ADRs are parsed from Markdown files matching the configured globs (defaults to docs/adr/**/*.md, docs/adrs/**/*.md, docs/decisions/**/*.md). The parser extracts:
- Frontmatter:
id,title,status,supersedes(case-insensitive). Nested YAML is unsupported. - Sections:
Context,Decision,Consequences,Status(case-insensitive). Fenced headings are ignored. - Missing
Decisionsection means the file is treated as an ordinary document, not an ADR. - Status conflicts (e.g., frontmatter says
acceptedbut section saysdeprecated) result inunknownstatus andADR_STATUS_CONFLICTin diagnostics.
How Decisions Attach to Code¶
Automatic citation extraction is Python only, from comment tokens and docstrings:
- Module comments attach at file scope
- Function/class docstrings attach to the respective symbol
- An executable string literal is never a citation
- Applicability never propagates through the call graph
- Duplicate ADR-42 aliases stay unresolved
Other languages use explicit Markdown sym: links only.
Freshness, Renames, and What Goes Stale¶
Freshness values indicate the relationship between cited code and decision evidence:
current— cited code matches the decision's evidence exactlystale— cited code changed since the decision was last refreshedmissing— cited code no longer existsunverified— decision comes from a remote namespace
A remote namespace is always unverified. Stale means the cited code changed, NOT that the decision is wrong. A rename creates a new record and leaves the old one with missing evidence — v1 does not infer identity across renames.
Generating Candidates (opt-in)¶
Generation is opt-in and requires:
- decisions.generation_enabled = true in .parrot/wiki.json
- WIKI_ADR_LLM environment variable or an injected client
- Credentials in environment variables only (never in config)
Budget limits (per call): - 1 LLM call - 8 files maximum - 12000 input tokens - 2000 output tokens - 3 candidates maximum - 60-second timeout
A rerun over unchanged evidence reuses and costs nothing. Every candidate cites supplied evidence only, with observations kept apart from hypotheses.
Reviewing and Accepting¶
Acceptance changes review status ONLY — origin stays inferred and source status stays unknown, forever. This is the single most important limitation to understand.
Review is CLI-only — no MCP tool or model can accept a candidate. The four actions are:
accept— mark as team-agreed (review_status=accepted, origin=inferred forever)reject— mark as team-rejected (review_status=rejected)revise— replace with edited content (--edit-file required)link— associate with a documented ADR (--documented-id required)
Use --expected-revision to guard against concurrent edits. ADR_REVISION_CONFLICT means the decision was edited since you last read it. Rejection does not delete; revise resets to unreviewed. wikitoolkit adr export prints Markdown and committing it is optional.
Limits and Error Codes¶
Inventory bound: 10000 records (deliberate v1 tradeoff). Configurable via ADR_INVENTORY_LIMIT.
| Error Code | Meaning | What To Do |
|---|---|---|
ADR_INVALID_ARGUMENT |
Bad CLI flag, malformed request, or no such decision ID | Check the command syntax / ID spelling |
ADR_RECORD_TOO_LARGE |
Record exceeds 1 MiB | Split into smaller decisions |
ADR_REVISION_CONFLICT |
Expected revision mismatch | Re-read before editing |
ADR_STATUS_CONFLICT |
Frontmatter vs section status | Make them consistent |
ADR_INVENTORY_LIMIT |
Too many records | Delete unused decisions |
ADR_MODEL_UNCONFIGURED |
Generation requested but no LLM is configured | Enable generation and set a model in config |
What This Feature Does Not Do¶
This feature does not: - Provide compliance or drift verdicts - Mine Git history for decisions - Ingest PRs or Jira tickets as decisions - Automatically commit accepted candidates - Provide a review UI - Extract citations outside Python automatically
These are all §1 Non-Goals, not roadmap items.
Command Reference¶
adr sync¶
wikitoolkit adr sync [OPTIONS] [PATHS]...
Refresh ADR records from source. Never invokes a model.
Options:
--path PATH Project root.
--json Emit the SyncResult as JSON.
adr lookup¶
wikitoolkit adr lookup [OPTIONS] SYMBOL
Find the decisions that apply to SYMBOL, with citations.
Options:
--path PATH Project root.
--include-history Include rejected/deprecated/superseded records.
--limit INTEGER Maximum hits (1-50). [default: 10]
--budget INTEGER Token budget (min 256). [default: 3000]
--json Emit the typed result as JSON.
--ns TEXT Namespace to read ('all' is not supported).
adr why¶
wikitoolkit adr why [OPTIONS] QUESTION
Answer a 'why' question with cited decision excerpts.
Options:
--path PATH Project root.
--include-history Include rejected/deprecated/superseded records.
--limit INTEGER Maximum hits (1-50). [default: 10]
--budget INTEGER Token budget (min 256). [default: 3000]
--json Emit the typed result as JSON.
--ns TEXT Namespace to read ('all' is not supported).
adr generate¶
wikitoolkit adr generate [OPTIONS] TARGET
Generate labeled CANDIDATE rationale for TARGET (a sym: id or a file).
Requires generation to be enabled and a model configured. Candidates are
created unreviewed; accepting one is a separate, explicit `adr review`.
Options:
--path PATH Project root.
--json Emit the GenerationResult as JSON.
adr review¶
wikitoolkit adr review [OPTIONS] DECISION_ID
Accept, reject, revise, or link a decision record.
Accepting a candidate records the team's agreement. It does NOT turn the
candidate into documented history: the record stays inferred with an
unknown source status, and no ADR file is written (Q3, AC11).
Options:
--action [accept|reject|revise|link] [required]
--expected-revision INTEGER Revision you read; guards concurrent edits. [required]
--actor TEXT Who is reviewing, e.g. 'human:jlara'. Attribution is mandatory. [required]
--reason TEXT Why.
--edit-file PATH CandidateEdit JSON; required for --action revise.
--documented-id TEXT Documented ADR to associate; required for --action link.
--path PATH Project root.
--json Emit the updated record as JSON.
adr export¶
wikitoolkit adr export [OPTIONS] DECISION_ID
Print DECISION_ID as Markdown on stdout.
Writing or committing this output is deliberately outside the feature —
accepting a candidate in the wiki is sufficient (Q3).
Options:
--path PATH Project root.
Exit codes: - 0 — Success or empty/ambiguous result - 1 — Failed (error occurred) - 2 — Invalid argument