Docs ↔ Implementation Tagging¶
What this solves¶
When implementation and the docs that describe it live in different files,
they drift silently. A reader editing sync/mod.rs has no way to know that
two architecture docs are quoting its heartbeat interval — so the docs go
stale without anyone noticing. This skill defines a minimal, greppable
convention to make that link visible from both directions.
Two tags, no tooling required:
Last updated byheader at the top of every doc that mirrors implementation behaviour.@docs:block at the top of every source file that implements anything those docs claim.
Together they support two everyday workflows:
- "I'm editing this code — which docs might need updating?" → read the
source file's
@docs:block. - "I'm editing this doc — which source files implement it?" → grep the repo for the doc filename.
Convention 1 — Last updated by header on docs¶
Add a single-line header above the existing doc body (after the title, version, and date if those exist):
Rules:
- Update whenever the doc's content changes to reflect a code change, not just typo fixes. The point of the header is to tell a reader when the doc was last reconciled against reality.
- Use absolute dates (
2026-05-12), never relative ("yesterday", "last week"). - One name + email + date per header line. Keep it short — this is a freshness signal, not a changelog.
- Leave existing
Document Version/Datelines alone; the new header sits alongside them. - Do not guess the email. Use the author's organisation email
(e.g.
<name>@fonterra.com).git config user.emailis not a reliable source — it frequently carries a personal or stale domain (this repo's history shows the same person under@fonterra.com, a personal domain, and@gmail.com). If the identity an agent has on hand is a personal domain, ambiguous, or otherwise doesn't look like the org address, stop and ask the author to confirm their actual email rather than writing a guess into the header.
Convention 2 — @docs: block on source files¶
A short comment block near the top of each implementation file, listing the docs (and ideally the doc sections) that describe its behaviour.
Format — comment-style languages (Rust, Go, C/C++, shell)¶
// ==============================================
// Module Name
// ==============================================
//
// <existing module description>
//
// @docs:
// - docs/COMMUNICATION-FLOWS.md §3 Heartbeat, §4 Sync loops
// - docs/platform-architecture-overview.md §2 LA↔Backend
//
use ...;
Format — JSDoc-style languages (TypeScript, JavaScript, Java)¶
// ==============================================
// Module Name
// ==============================================
// <existing module description>
//
// @docs:
// - docs/COMMUNICATION-FLOWS.md §1 On-Device flow
// - docs/platform-architecture-overview.md §1 IPC channels
import { ... } from '...';
Format — Python¶
"""Module description.
@docs:
- docs/COMMUNICATION-FLOWS.md §3 Heartbeat
- docs/platform-architecture-overview.md §2 LA↔Backend
"""
Rules¶
- The literal string
@docs:is the marker.grep -rn "@docs:"must return every tagged file — do not abbreviate, capitalise, or reflow it. - Paths are repo-relative (e.g.
docs/COMMUNICATION-FLOWS.md) so the entry reads identically from any file. - Cite the doc section (e.g.
§3 Heartbeat) when the file maps to a specific section. If the whole doc is relevant, omit the anchor. - List one doc per line as a bullet. Two- or three-doc blocks are normal; four or more usually means the file is doing too much.
- Place the block inside the existing top-of-file comment, not as a separate banner. Visual noise matters; the block is supplementary metadata, not the file's main introduction.
Choosing which source files to tag¶
Tag a file when both are true:
- The file implements behaviour that at least one doc explicitly describes (intervals, schemas, endpoints, port numbers, channel names, ID formats).
- Changing the file without updating the doc would mislead a future reader.
Files that deserve the tag:
- Sync / heartbeat loops, scheduler intervals, retry policies.
- Wire schemas (request/response structs, protobufs, JSON shapes).
- Registration / identity / fingerprinting logic.
- IPC handler registries (channel names, command tables).
- Network listeners (port numbers, bind paths, socket paths).
- Config defaults that downstream systems depend on.
Files that don't need it:
- Pure utilities (string helpers, math, formatting).
- UI components with no documented behaviour contract.
- Tests, fixtures, generated code.
- Build / tooling scripts unless they encode a documented workflow.
Aim for 10–20 tagged files per service, not every file. A bloated tag-set is as bad as none — it trains readers to ignore the marker.
Good vs bad examples¶
These come from the original VortexNav rollout — verbatim.
Good — anchored, scoped, integrated¶
// ==============================================
// Sync Module
// ==============================================
//
// Handles synchronization with Horizon:
// - Periodic heartbeats
// - Entitlement refresh
// - Usage reporting
//
// @docs:
// - docs/COMMUNICATION-FLOWS.md §2 Device-to-Cloud, §3 Heartbeat, §4 Sync loops, §6 Usage
// - docs/platform-architecture-overview.md §2 LA↔Backend (transport, auth, token lifecycle)
//
Why it works: lives inside the file's existing top-of-file comment; cites multiple sections so the reader knows which paragraphs to check; two-deep aligned columns make it readable at a glance.
Good — single-doc, single-section¶
Why it works: precise. A DeviceTelemetry struct change forces a doc
update at exactly one location.
Bad — vague, unscoped¶
Problem: not greppable (no @docs: marker), no path, no section
anchor. The reader has to know which docs to read and where to look.
Bad — section count exceeds the file's responsibility¶
// @docs:
// - docs/COMMUNICATION-FLOWS.md §1, §2, §3, §4, §5, §6, §7
// - docs/platform-architecture-overview.md §1, §2, §3, §4, §5, §6, §7, §8
Problem: this file claims to back the entire platform. Either the file is a god-object that should be split, or (more often) the author copied the same block into every file. Both cases mean the marker has stopped carrying signal.
Bad — path drift not caught¶
Problem: relative paths break when the file moves. Always repo-root relative.
Bad — separate banner box¶
// ===============================================================
// @docs
// ===============================================================
// docs/COMMUNICATION-FLOWS.md
// docs/platform-architecture-overview.md
// ===============================================================
Problem: visual heavyweight. The block is metadata, not a section heading. Integrate it into the existing comment.
The maintenance contract¶
The tags only have value if they are kept honest. Two rules:
- When editing a tagged source file, scan its
@docs:block. If your change touches anything those docs describe — intervals, schemas, endpoint paths, channel names, port numbers — update the doc in the same PR and bump itsLast updated byheader. - When editing a tagged doc, grep for its filename across the
source tree. If the file you just changed contradicts one of the
source files'
@docs:entries, the source needs updating too.
If a tagged doc is renamed or deleted: grep and update every source
file that referenced it. Treat broken @docs: paths the same as
broken imports.
Discovery commands¶
# Every tagged source file in the repo
grep -rln "@docs:" --include='*.rs' --include='*.ts' --include='*.py'
# Files associated with a specific doc
grep -rln "COMMUNICATION-FLOWS.md" --include='*.rs' --include='*.ts'
# Last-updated dates across all docs (sanity check for staleness)
grep -rn "Last updated by" docs/
A CI check that fails when a tagged path references a missing file is optional but high-value. Keep it as a separate skill if you build it — the tagging convention itself must stay tooling-free.
Roll-out steps for a new project¶
- Pick the docs that will carry
Last updated byheaders. Usually the small set that mirrors current implementation (communication flows, API surface, architecture overview). - Add the headers with today's date and the human who reconciled the doc against code. From this moment forward, anyone touching the doc updates the header.
- Pick the source files to tag (10–20 per service, see "Choosing which source files to tag").
- Add
@docs:blocks to each. Cite section anchors where the file maps to a specific section. - Add one paragraph to the project's
AGENTS.md/CLAUDE.md/ contributor docs naming the convention so reviewers know to enforce it. - First PR review under the convention: scan for
@docs:hits in the diff and check that any listed doc was updated alongside the code.
Troubleshooting¶
Error: grep "@docs:" returns nothing in a project that should be
tagged.
Cause: Tags haven't been rolled out yet, or someone abbreviated the
marker (@doc:, // docs:).
Solution: Run the roll-out steps above. Fix abbreviated markers —
the literal @docs: is non-negotiable.
Error: A @docs: block references a path that no longer exists.
Cause: The doc was renamed or deleted without updating callers.
Solution: grep -rln "<old-doc-name>" across the source tree, then
update every referrer. Same workflow as fixing a broken import.
Error: Every file in a directory has an identical four-doc @docs:
block.
Cause: Copy-paste during roll-out without scoping per file.
Solution: For each file, ask "which doc section would a reader
actually need if they were changing this file?". Trim to the
specific entries. A block longer than 3 lines is a smell.
Error: Docs have Last updated by headers but the dates are months
behind recent code changes.
Cause: The maintenance contract isn't being enforced in review.
Solution: Treat a code change that touches documented behaviour
without a doc-header bump as a review block. Add an example to the
team's PR checklist.
Error: A doc's Last updated by shows last month's date but the
content was edited yesterday for a typo.
Cause: Confusion about when to bump the header.
Solution: Bump only when the doc's content is reconciled against
implementation, not for cosmetic edits. Typo fixes can leave the
header alone.