Documentation that parses
docujoint is a deterministic engine for markdown documentation. You describe your format in one YAML file — types, typed tables, evidence schemes, the rules that matter to you — and it parses every document into data it can check, chart, answer questions about, and refuse to let drift.
MIT · Node 20+ · no database, no lock-in — your documents stay markdown
## Features | ID | Feature | Implemented | Gap | |----|--------------------|-----------------------|-----| | f1 | Buyer pays by card | repo://shop/pay.ts | — | | f2 | Saved wallets | repo://shop/wallet.ts | — | | f3 | Apple Pay | — | not built yet |
wallet.ts is gone from the inventory — the claim outlived the code.
Drift
Each of these reads the same document. None of them is a second copy of it.
Structure, frontmatter, table shapes, id grammars, cross-references, and checks you write yourself in a real expression language. Zero findings is a bar CI can hold.
Indicators, coverage, filters, tabs, and a board per person from one definition. Every number traces to a query — nothing is maintained by hand.
Answer a question in the page and the edit goes through the linter. An edit that would break the format is refused, not committed.
Documents, rows, links and citations as nodes and edges, with full-text and optional semantic search over every row.
Agents write the documentation; the engine decides when it is done. Five packaged skills teach them your format.
Markdown in your repo, one file per concept, ordinary links. Delete docujoint tomorrow and your documentation is untouched.
01 · The format is yours
The engine ships no vocabulary. It does not know what built
means, or that a table called Features is special. Your
format.yaml says which document types exist, what tables they
carry, which columns are evidence, and how state is computed from them.
Two teams can use the same word for opposite things, and both are right.
blocks: features: heading: Features columns: - { name: ID, id_grammar: "f<n>", unique: error } - { name: Implemented, refs: [repo, api] } - { name: Gap } # state is computed, never typed into a column derive: state: - { when: "implemented && !gap && ref_broken", then: drift } - { when: "implemented && !gap", then: built } - { when: "!implemented && gap", then: missing } forbid_columns: - { name: Status, severity: error }
02 · Nothing is stored twice
Anything that can be computed is computed: from what the row says, and from whether the code it cites still exists. A document cannot claim done while pointing at a file nobody has — that combination has a name, and it shows up as a number.
Point it at a scan of your codebase and the reverse also holds: what exists and nothing documents.
Apps/Storefront/Pages/Checkout Page.md error f2: cites evidence the scan no longer confirms (drift) warn f5: complete but no test:// evidence (feature-untested) warn q1: open question blocks f3 (unspecified) 498 file(s), 488 concept(s): 0 error(s), 627 warning(s) exit 0 — pass --warnings-as-errors to make CI mean it
03 · One vault, three surfaces
The same parse becomes an interactive dashboard you declare in YAML — a page per team member, tables you act on in place, forms that write answers back through the linter so a bad edit is refused rather than committed.
And a graph: every document, row, link and citation projected into SQLite, so "what would this change touch" is a query instead of an afternoon.
4,187 nodes (488 document · 3,699 artifact) 7,408 edges (4,394 rows · 1,960 citations · 631 links · 423 joins) -- who cites the endpoint you are about to change? SELECT r.node, r.key FROM cite c JOIN row_ r ON r.id = c.row WHERE c.uri = 'api://payments/charges'; 11 rows · 0.03 ms
Measured on a 498-file vault
Sixty seconds
# a working vault: format, dashboard, sample documents $ npm i -g docujoint $ dj init my-docs && cd my-docs # zero findings is the bar, and CI can hold you to it $ dj lint --all --vault vault --format format.yaml # the app, served — forms write back through the linter $ dj dashboard --vault vault --format format.yaml \ --dashboard dashboard.yaml # the same render as one file that opens offline, no server $ dj dashboard ... --out dashboard.html
Agents write documentation and the engine decides when it is done —
zero errors, deterministically, from the same binary CI runs.
dj skills install --all teaches them the format.
Your format is a profile over Google's Open Knowledge Format. One markdown file per concept, ordinary links, no database — a vault stays readable in any editor and diffable in any repo.
Full-text over every row, and semantic search when you bring an embedding model. A Spanish question finds the right English rows — the engine stores the vectors and never calls a model itself.
Open core