Graph-informed release — detection improvements and integration surfaces
adapted from patterns proven in
graphify (MIT), rebuilt
zero-dependency and deterministic for DocGuard. The detectors were empirically
validated read-only against five real production repos before shipping: zero
false positives; the indirect-impact analysis surfaced genuine, explainable
chains on two of them. Validator count unchanged (27).
Added
- VALIDATION.md — an honest benchmarks-style page documenting the
empirical method every detector goes through before shipping enabled
(read-only corpus runs on real production repos, keep/cut/tune, dogfooding)
and the measured v0.31/v0.32 results, including what DocGuard does NOT
claim. Linked from the README header. - PR doc-conflict analysis —
docguard impact --prs. Maps every open
PR's changed files to the canonical docs they impact (same reference index
as regular impact; a PR editing a canonical doc directly counts too) and
reports pairs of PRs impacting the SAME doc — a merge-order risk: whichever
lands second must re-verify the shared doc. Uses theghCLI (no token
handling in DocGuard); degrades to a clear message whenghis missing or
the repo isn't on GitHub. Capped at 20 open PRs per scan. The
graph-community version of this idea ships in graphify'sprs --conflicts;
this is the doc-integrity equivalent. - MCP Streamable HTTP transport —
docguard mcp --transport http. One
shared process can now serve the DocGuard tools to a whole team: JSON-RPC
over POST (single + batch), 202 for notification-only bodies, session id
issued on initialize (stateless server — accepted, never required), GET
correctly 405s (no SSE stream offered). Zero-dep (node:http). Security
posture: binds127.0.0.1by default; binding any non-loopback host
REFUSES to start without--api-key/DOCGUARD_API_KEY; when a key is set
every request must carry it (Authorization: BearerorX-API-Key);
browser cross-site origins are rejected (DNS-rebinding guard); 4 MiB body
cap. Flags:--port(default 8585),--host,--api-key,--path
(default/mcp). The stdio transport is unchanged and remains the default;
both share one JSON-RPC dispatcher. - Agent nudge hook —
docguard hooks --claude(graphify's always-on-hook
distribution pattern, pointed at doc integrity). Registers aPostToolUse
hook in the project's.claude/settings.json: after the agent edits a
canonical/agent doc it is nudged to rundocguard guard --changed-only;
after it edits a code file the docs reference, it is nudged toward
docguard impact. Merge-safe (only DocGuard's own entry is added/removed;
an unparseable settings.json is never touched), idempotent, throttled (one
nudge per file per 30 min via.docguard/nudge-state.json), and the
docguard nudge-hookruntime is silent-on-error by contract — it can never
break an agent session. Explicit opt-in;init/ensureSkillsnever install
it. Remove withdocguard hooks --claude --remove. - ADR-citation check (REF002, reference-existence) — the code→doc direction
of reference existence. A code comment citing a decision record (uppercase
ADR-+ number in a comment) is now verified against the ADR documents the
repo actually defines (single-fileADR.mdsections,ADR-*.mdfilenames,
and madr-styledocs/adr/0007-*.md). Numbers compare as integers
(ADR-0011matchesADR-11). Citations only count inside comments — string
literals and identifiers are ignored — and tests/fixtures are excluded
(non-product scoping). IETF RFC citations are deliberately out of scope
(external registry — would false-positive on everyRFC 793comment). Soft
(confidence: low), capped at 10 findings, suppressible with
// docguard:ignore REF002, disable with
referenceExistence.adrCitations: false. - Obsidian wikilink support (Cross-Reference + impact) —
[[Doc]],
[[Doc#Heading]], and[[Doc|alias]]are now validated like inline links
(broken target → XRF001, broken heading → XRF002), andimpact's doc→doc
blast radius sees wikilink dependents. Precision-gated: wikilinks are only
validated when the repo demonstrably uses them as FILE links (.obsidian/
exists, or at least one wikilink target resolves) — repos using[[name]]
as a non-file convention are skipped silently. Image embeds![[x.png]]
never count. Wikilink targets resolve sibling-first, then vault-wide by
basename across the project's doc homes. - Indirect impact via the import graph (
docguard impact) — a changed file
with no doc references can still invalidate docs about the modules that
IMPORT it.impactnow walks the reverse import graph (reusing the
Architecture validator's graph builder — one builder, not two) up to 2 hops
and reports docs describing an importer of a changed file, with the
explainable chain (doc describes X, which imports changed Y). Hub modules
(>15 imports, e.g. a CLI dispatcher) are suppressed — their docs would flag
on every dependency change. Docs already directly affected are not repeated.
JSON addsindirectDocs; disable with--no-indirect. JS/TS import graphs
only (the graph builder's scope). - Graphify knowledge-graph interop (Traceability) — teams that commit
graphify-out/graph.json(a tree-sitter knowledge graph) get its doc↔code
edges counted as linkage evidence before an "unlinked doc" (TRC002) is
raised. Trust rules: onlyEXTRACTEDedges count (never the LLM-derived
INFERRED/AMBIGUOUStiers), at least one linked code file must still
exist (a stale graph can't vouch), and the graph is evidence-only — it can
turn a warning into a pass but never produces a finding. Zero-dependency:
one JSON read; malformed graphs are silently ignored.
Fixed
- Cross-Reference link parsing — query strings are stripped before target
resolution (./DOC.md?plain=1#anchornow resolves toDOC.mdinstead of a
phantom file), and CommonMark angle-bracket targets with spaces
([t](<my doc.md>)) are resolved instead of being silently skipped. - Semantic-claim extractor honors
.docguardignore— a doc the user
explicitly excluded from validation no longer feeds the "unverified claims"
pool (guard notice,verify --semantic, the ALCOAAccuratepillar). On
DocGuard's own repo an ignored historical audit contributed 28 of 39
reported claims, burying the actionable ones.