Installone binary
A single Rust binary — no runtime, no daemon. Build it from the public GitHub repo, or drop a prebuilt release binary on your PATH. Release binaries for 6.x are macOS only, Apple silicon and Intel.
built by aktagon.com
Keep the markdown your agents retrieve correct, current, and groundable — checked in CI on every commit.
ctxgrd is a CLI linter for the structured markdown your team and your AI both depend on. ADRs, PRDs, runbooks — every .md file with frontmatter and an ID.
Same input, same output, same exit code.
$ ctxgrd init Created ctxgrd.toml $ ctxgrd pack add agents Added pack 'agents' — 5 namespaces, 4 rule families. $ ctxgrd error[core.cross-ref]: cross-reference 'ADR-007' does not resolve to a known document --> docs/adrs/ADR-013.md:14:5 | 14 | Per ADR-007, the rule compiler is pure, so its output is cacheable. | ^^^^^^^ | help: use an existing ID, or mark retired with ~~ADR-007~~ found: 1 error · 0 warnings
Lint your context. · Markdown only. On purpose.
01 For the context engineer
ctxgrd catches broken references, tangled decision lineage, and duplicate IDs before they reach the model — and fails CI like a type error.
$ ctxgrd error[core.cross-ref]: cross-reference 'ADR-007' does not resolve to a known document --> docs/adrs/ADR-013.md:14:5 | 14 | Superseded by ADR-007, the rule compiler is pure. | ^^^^^^^ deleted in 9d7192b × 1 broken reference · exit 1
02 One contract, two readers
Every command emits --format json, keeps stdout a clean parseable stream, and returns documented exit codes. An agent can drive ctxgrd without screen-scraping — the same way your CI does.
$ ctxgrd lint --format json | jq '.diagnostics[0]' { "rule": "core.cross-ref", "severity": "error", "file": "docs/adrs/ADR-013.md", "line": 14, "message": "'ADR-007' does not resolve" } $ echo $? 1 # branch on it, no text parsing
03 Every capability
Scaffold ctxgrd.toml and your first namespaces in one command.
Check every claimed doc. Human output, or --format json for agents.
See the decision frontier — what's ready, what's blocked, what's stale.
Drop in reusable namespace bundles — agents, ops, project-docs, gdpr.
Wire ctxgrd into pre-commit, CI, or a Claude stop-hook.
Generate a correctly-structured ADR, PRD, or runbook from a template.
A single Rust binary — no runtime, no daemon. Build it from the public GitHub repo, or drop a prebuilt release binary on your PATH. Release binaries for 6.x are macOS only, Apple silicon and Intel.
One command writes a ctxgrd.toml that already passes its own validator, then lists every built-in pack — pick one to get a whole family of document types at once. The generated file names the tool and links its docs at the top, so nobody inherits an anonymous config.
Picks the next free ID for the namespace, slugifies the title, and writes the required-heading skeleton. Prints the path so you can pipe it to your editor.
Before an agent writes a document, it needs the types this repo uses, where each one goes, and who owns it. `ctxgrd namespaces` reads that from ctxgrd.toml: one row per namespace, with its paths, owner, document count and a line on when to write one. `--for <path>` names the namespace that claims a file. `--format json` prints a bare array with every key present, null where a value is missing.
The default command. Walks the tree, lints every claimed document against its type, and points at the line. Exit 1 on errors — drop it straight into CI.
Point a run at part of the tree. `--namespace ADR` lints the ADRs and skips everything else; `--pack ops` lints every namespace that pack contributed, resolved through the stamps in your own config rather than the pack definition, so it never scopes to something you never adopted. A name the config does not declare exits 2 instead of quietly linting nothing.
A document marked accepted while the decision under it is still a draft records something that has not happened yet. The rule reads `depends_on` and names both halves: a terminal-status document may not depend on a non-terminal one. Settle the dependency, or reopen the dependent until it settles. One `terminal` list cannot tell a sealed checklist from an abandoned one, because both are end states. `satisfied-by` names, per upstream namespace, the statuses that count.
A draft PRD should cite the roadmap item it serves. A draft ADR should cite a PRD or a roadmap item. The project-docs pack warns when one cites neither. It checks drafts only, so settled history stays quiet, and it warns rather than fails, so CI stays green while the links go in. `requires-any`, `statuses` and `severity` let you set the same rule on your own namespaces.
A new record replaces an old one, and the old one still says `accepted`. A reader who lands on it implements a retired decision. List what a record replaces in `supersedes`. Once that record is accepted, each record it names must be `superseded`, with `superseded_by` pointing back. The `project-docs` pack binds it on ADR and PRD at warning. Set `severity` to make it an error.
A README or a campaign brief never needs an id, and a decision can still depend on one. `depends_on` takes a repo-relative path to any claimed document. It also takes a bare namespace name when that namespace binds `core.singleton`. Name any other namespace bare, and the error offers both fixes.
Some namespaces hold exactly one file: the README, DESIGN.md, a positioning statement. Bind `core.singleton` and ctxgrd enforces that. A second file matching the namespace's `paths` is an error naming the file that already holds the slot. The same binding lets other documents write `depends_on: [POSITIONING]`. Fifteen built-in pack namespaces bind it.
A relative link is a pointer, and pointers rot. This resolves every relative link and image in a claimed document against the disk, from that document's own directory — no URL is ever fetched. Three faults, three messages: the target is gone, it sits outside the linted root where nothing guarantees it travels with the repo, or it matches on disk only because macOS ignores case and Linux does not. That last one is invisible on the machine that wrote it. The rule ships in the zero-config set at warning severity, so a repo with no ctxgrd.toml gets the report without its exit code moving.
A `status` field has a vocabulary. A `date` field has a shape. This rule checks each present value against its format word: `date` is YYYY-MM-DD, `datetime` is RFC 3339 with an offset. `03/09/2026` is March to one reader and September to the next, and `2026-9-3` does not sort. The packs bind it at warning on ADR, ROADMAP, DEC, CR, FEEDBACK, TEST and HANDOFF, so an upgrade lists the backlog without failing CI. A missing value stays with `core.required-metadata`.
Most style guides carry a list of words the team agreed to stop writing. Put that list in ctxgrd.toml and the rule reports each hit at its line and column. Words match on word boundaries, and code spans are skipped, so an identifier in a sample is never reported. Findings are errors by default; set `severity = "warning"` while you clear a backlog.
An instruction file that grows past the model's own limit gets truncated, and the part that falls off the end is not the part you would have chosen. The rule counts characters against a budget — 150,000 by default, Claude's own ceiling — and says how many to cut. Raise `max_chars` per namespace when the size is deliberate.
A source that exits clean without emitting anything would otherwise produce a run byte-identical to one where every document passed. `[sources.<name>]` carries a floor: `expect_min`, default 1, and falling under it warns. A namespace whose paths match only files the walker skipped says so too, rather than counting zero and reporting green. Set `expect_min = 0` for a source that may legitimately emit nothing.
Every rule declares what it needs: an `id:`, a frontmatter block, or a file on disk. `ctxgrd rules` lists those needs. Bind a rule to a document that lacks one, and ctxgrd reports `cfg.rule-inert` on that file. A skipped rule would otherwise look like a passing one. A misspelled parameter key gets its own error, `cfg.param-unknown`, naming the key the rule does read.
Each namespace block carries a one-line `summary`, and a block without one is a config error. `ctxgrd pack migrate` adds the pack's line to every block a pack wrote, including blocks you edited since, and changes nothing else in them. A block you wrote yourself needs its line from you, and migrate lists those. A config written before summaries existed fails until you migrate it, so plan the upgrade.
Reads `depends_on` across every document and sorts them into ready, blocked, and settled. A document is ready when everything it depends on is accepted; a blocked row names the dependency holding it up, and every row carries its title, so the queue reads without knowing the corpus by heart. There is no stage order to declare — the graph is the order. Add --format json for agents.
The readiness census as one row per document: id, namespace, title, status, whether it can be picked up, and the ids holding it back. Clean stdout, so a router chooses the next piece of work without screen-scraping a report written for a person — and without a second call to recover what each id is called.
The same document graph as diagram source — Mermaid `flowchart LR` or Graphviz DOT, emitted as text and never rendered. Pipe it into a docs build or a PR comment to show what is waiting on what.
A read-only local view of the governed docs — the namespace index, server-rendered markdown, clickable `depends_on` edges, and the same work queue. It binds loopback only and prints its URL as one JSON line on stdout, so an agent can discover the port it landed on. Logs go to stderr.
Release notes derived from the document graph instead of a file everyone forgets to edit. Each whitelisted document is attributed to the first release tag whose tree marks it terminal, so a shipped section is read back from the tag and cannot drift. `--check` is the CI gate, `--write` regenerates in place, `--format json` feeds an agent.
Pin a document to the commit of the code it documents. When that code moves, the doc is flagged stale — re-verify, then bless it back to green.
A pack is a reusable bundle of namespaces plus the rules they need. Adding one appends its blocks to ctxgrd.toml — it never clobbers what you already have.
A pack is a generator, not a dependency — once its block is in your ctxgrd.toml, the block is yours. So `pack outdated` asks whether the pack moved since that block was stamped, not whether the block still matches the pack. Set an owner, bind an extra rule, override the paths: all silent. It goes red only when the pack itself changed under you, and then prints the pack's block beside yours to reconcile by hand.
Migrate re-renders a block only when it can prove the rewrite loses nothing — that is, when the block is still byte-identical to its pack. Anything you customized is left exactly as written, even where the pack has moved; that diff is yours to resolve. A block carrying a pre-fingerprint stamp gets its baseline recorded, which changes no text and no rule. Preview with --dry-run, script it with --format json.
A block stamped before ctxgrd recorded fingerprints carries no baseline, so "has the pack moved?" has no answer for it. Calling that clean would assert something unverified; calling it drift is the lie the old model told. It gets a state of its own, and never sets the exit code. The report names the digest that would settle it and `pack show --format json` carries the same value per namespace — writing it into the `# pack:` line is an assertion the tool will not make on your behalf.
Installs ctxgrd as a numbered fragment under a tracked `.githooks/pre-commit.d/` and points `core.hooksPath` at it, so a sibling linter's gate composes beside it instead of overwriting it. A lint failure aborts the commit. The same binary and exit codes drive your CI gate and editor LSP.
Wire ctxgrd into the harness's turn-end hook and it runs the moment the agent tries to finish — Claude Code and Codex on Stop, Gemini CLI on AfterAgent, Antigravity on Stop. One flag, four dialects, one meaning: an error-severity diagnostic returns that harness's own refusal verdict carrying the reason, so the agent repairs the document before handing back. Warnings never block and a clean run is silent. The gate always exits 0 — the verdict travels in the payload, not the exit code.
04 See it work
# 052-rule-compiler.md id: ADR-042 # 031-cache-policy.md id: ADR-042 ↳ Two records claim ADR-042. The agent retrieves "ADR-042" and grounds on the wrong one.
error[core.id-unique] ADR-042 declared twice 052-rule-compiler.md, 031-cache-policy.md ✓ fixed — renumber the compiler record to ADR-058. ✓ build green · exit 0 # every ID resolves to one doc
05 What it checks
Rules ship as packs — each a bundle of namespaces, one per document type. Point ctxgrd at a tree and every file is linted against its type.
ADR, PRD, ROADMAP, RFC, BUG doc types plus the TODO state file and the README front door — engineering doc lifecycle with status vocabularies, required headings, and a README that links the entry guide.
AGENTS — the AGENTS.md cross-tool instruction standard (agents.md).
CLAUDE/CLAUDESKILLS/CLAUDEAGENTS — the files proprietary to Claude Code: the CLAUDE.md instruction file,.claude/skills SKILL.md definitions, and.claude/agents subagent definitions.
CODEXSKILLS — OpenAI Codex SKILL.md definitions (.codex/skills).
GEMINI — the GEMINI.md instruction file Gemini CLI reads.
OPENCODEAGENTS — opencode agent definitions (.opencode/agent/*.md), linted via the opencode.frontmatter rule.
SPEC/TASK/PROMPT, plus HANDOFF from the handoff pack — ctxgrd's harness-neutral agent-development document pipeline: a SPEC is implemented via TASKs, reusable PROMPTs are versioned, and a HANDOFF carries work to the next session.
RUN (runbooks) and PMR (postmortems) — the incident-management doc lifecycle, grounded in Google's SRE Book.
Security-document lifecycle (THREAT/VULN/RISK/SECREV/DEPAUDIT) plus neutral governance (POLICY/ASSET) — framework-neutral evidence discipline, no certification intent.
GDPR documentary artifacts (ROPA/DPIA/DPA) over the security base — Regulation (EU) 2016/679 Arts. 28/30/35. Paperwork existence/metadata/freshness only, never legal adequacy.
HIPAA Security Rule safeguard register (SAFEGUARD) and Business Associate Agreement register (BAA) over the security base — 45 CFR 164.308/310/312, Security Rule as in force 2026. Paperwork existence/metadata/freshness only, never legal adequacy.
SOC 2 control-to-evidence register (SOC2) over the security base — AICPA Trust Services Criteria (2017, rev. 2022): the mandatory Common Criteria CC1-CC9 plus the optional Availability/Confidentiality/Processing-Integrity/Privacy categories. Paperwork existence/metadata/freshness only, never operating-effectiveness adequacy.
ISO 27001 control-to-evidence register (ISO27001) over the security base — ISO/IEC 27001:2022 Annex A: 93 controls across the Organizational/People/Physical/Technological themes. Paperwork existence/metadata/freshness only, never implementation adequacy.
NIST 800-53 control-to-evidence register (NIST80053) over the security base — NIST SP 800-53 Rev 5: the 20 control families (AC-SR), family-level grain. Paperwork existence/metadata/freshness only, never implementation adequacy.
design — structural linting for the two root design-context files a design agent reads before it works: DESIGN.md (the visual system — how it looks) and PRODUCT.md (the strategy — register, platform, users, purpose, positioning, who/what/why).
SOUL.md/STYLE.md persona linting — section presence, order, and pairing
DDD — strategic Domain-Driven Design docs on the dependency graph: BOUNDEDCONTEXT (a model/language boundary with its Ubiquitous Language, aggregates, and domain events) and CONTEXTMAP (one Evans strategic-pattern relationship edge per file, riding depends_on).
PORT — a per-subsystem porting-parity tracker.
C4 — architecture-diagram docs typed by the C4 model level (context/container/component/code) plus the supplementary deployment/dynamic/landscape views, each declaring its level and carrying a title.
CCPA/CPRA processing register (CCPA) and service-provider agreement register (SPA) over the security base — California Civil Code Title 1.81.5, Cal. Civ. Code §§ 1798.100/1798.140. Paperwork existence/metadata/freshness only, never legal adequacy.
CHECKLIST — turns a markdown checklist into an auditable, commit-pinned sign-off: every box checked and the sealed state pinned to a real, in-history commit.
EU AI Act obligation register (AIACT) and Art. 27 fundamental rights impact assessment register (FRIA) over the security base — Regulation (EU) 2024/1689. Paperwork existence/metadata/freshness only, never conformity or legal adequacy.
GitHub community-health files — CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, SUPPORT — path-claimed as id-less docs with a warning-severity existence nudge. README stays in project-docs; LICENSE is excluded.
GitLab community-health files — CONTRIBUTING and CHANGELOG — path-claimed as id-less docs with a warning-severity existence nudge. README/LICENSE excluded; CHANGELOG is first-class on GitLab.
DEC — a program/governance decision register (docs/decisions/[0-9]*.md): one id-keyed markdown record per material program decision, carrying the decision, its rationale, cross-cutting impact, and the approving authority. First member of the RAID+ register family.
GUIDE — end-user documentation typed by the Diátaxis taxonomy (tutorial/how-to/reference/explanation) with a required title, kept discoverable from the README.
Inbound requests from downstream repos — CR (a JSM-Change-shaped, id'd, linted change request) and FEEDBACK (a light id-keyed note record: id/source/date, free-form body, on the HubSpot/Jira fields-over-headings model).
marketing — the markdown-documentable marketing-strategy artifacts on the dependency-free side of the CRM line: CAMPAIGN briefs, buyer PERSONAs, POSITIONING, and the ICP, each a structured record grounded in a named framework.
NIS2 cybersecurity risk-management register (NIS2) and Article 23 incident register (NIS2INC) over the security base — Directive (EU) 2022/2555 Arts. 20, 21(2) and 23. Paperwork existence/metadata/freshness only, never conformance to the statutory reporting clock.
TEST — a pinned Test Completion Report register (docs/tests/TEST-*.md): one id-keyed markdown record per release milestone, carrying the exit-gate verdict, the tree and contract revision it ran against, its outstanding defects, and sign-off. IEEE 829 / ISO-29119-3 completion report.
RESEARCH — deep-research reports (docs/research/**) kept evidence-honest: a sources/evidence section is required and a limitations/data-gaps section is nudged, with optional per-genre skeleton routing.
INTSTRIPE — the seven-phase Stripe web-integration checklist: a commit-pinned sign-off whose phases and @stripe.* anchors ctxgrd enumerates and wrkgrd's anchor-coverage proves.
arc42 architecture documentation — the 12 canonical arc42 sections as required H2 headings (ARC42 namespace).
HANDOFF — the session-continuity store: a ready-to-execute prompt for the next session, written to `docs/handoffs/` and marked `consumed` in place when picked up.
The core tool and every built-in pack are free. Source-available under the Elastic License 2.0. GDPR, HIPAA, SOC 2, ISO 27001, NIST 800-53 and arc42 are built in.