docir — design documents Graph

Documents / Release notes / rel-4acb78696bd6

0.26.0 — the link written in prose is a link docir reads

[[...]] cross-references resolve against ids, filename stems, title slugs and titles, render as the target's current title, and the ones pointing at nothing are a Tier 1 warning; underneath, the six largest classes were split with every surface held byte-identical.

rel-4acb78696bd6release_notepublished
View as Markdown◉ View in graph

A body cites another document two ways, and docir read only one. related: frontmatter was validated on write, reported by dangling and drawn on the graph; a [[...]] written mid-sentence was parsed by nothing — not the renderer, not a check, not the index. It published as literal brackets, and a retitle broke every inbound one in silence. This release makes the prose link a link docir reads: it resolves, docir build renders it, and docir check names the ones that point at nothing.

Upgrade notes

  • No migration. The index schema is unchanged, so docir doctor reports only stale-index-build until the first docir reindex after upgrading. docir self upgrade runs it and refreshes the generated instruction files, whose maintenance reference now documents unresolved-link.
  • A store that already writes [[...]] sees its broken ones on the next docir check. They were broken before; this is the first build that says so. Each is a warning, so docir check --strict still passes, and docir check --fix leaves them — a target nobody can name is not a repair without a guess.

A target may be a document id, a filename stem, a title slug or the title itself; a .md suffix and a leading directory are stripped, [[target|label]] keeps the label and [[target#heading]] links the anchor. Resolution reads every document, inactive and archived included: a link to a resolved issue is a working link, and "not in the default docir query" is never a reason to call one broken. An ambiguous target resolves to neither and names its candidates.

The filename stem is carried as data rather than derived, because docir update --set-title keeps the filename — so a retitled document's stem is the slug of a title it no longer has, and the links written against it are exactly the ones that must keep working.

docir build renders a resolved link as the target's current title, so a retitle cannot leave a link displaying a name no document has. An unresolved one stays the literal [[...]], like a dangling edge on the same page.

text
docir build

docir check reports a [[...]] that resolves to nothing as unresolved-link, one finding per (document, target). It is Tier 1 where unresolved-mention is Tier 2 because the two are not the same defect: an id named in a sentence is a citation, while [[...]] is link syntax with no second reading. The one false positive available is a body demonstrating the syntax, and the scan skips code spans and fences. Prose links still feed no graph — orphan reads related: alone — and nothing repairs one, so docir check --fix leaves it.

text
docir check

Verified by injecting each bug the guards claim to catch, and exercised against this repository's own store through the daemon, the CLI, MCP and docir build; 0.25.0 reads a store this build wrote and this build reads one 0.25.0 wrote, both clean.

Underneath: six classes split by reason to change

DocumentService, MaintenanceService, GraphChecker, cli/app.py, the markdown file store and publishing/infra/rendering.py were each split on the seam their own section comments had drawn, and build_mcp_server went from 646 lines to 22. No api.py signature and no CONTRACT.md moved. Every surface was held byte-identical rather than asserted: the whole Typer tree, tools/list, every finding docir check reports against the released 0.25.0, all 422 files docir build produces from this store, and ranking under 300 randomised differential trials. What the audit kept, and why DocumentService is not split further, is on record as adr-a1754eb79fe7.

Full changelog: CHANGELOG.md

To amend: Re-verify: