harn docs
harn docsharn docs meta <path> [key] [--json]harn docs metadata validate [--repo <name>] [--cached]harn docs metadata sync [files...] [--repo <name>] [--check] [--cached] [--reviewed]harn docs new <type> <path> --summary <text> --owner <id> [--status <status>] [--severity <severity>]harn docs lint [--fast] [--repo <name>] [--cached]harn docs links [--repo <name>] [--no-fragments] [--strict] [--check-escapes] [--fail]harn docs sweep [--repo <name>]harn docs index [--dry-run] [--repo <name>]The docs command group gives an embedding host one documentation maintenance
surface. The host supplies its repository root, submodule list, exclusions, and
any project-specific root allowlist through HarneryProgramContext.
Read frontmatter
Section titled “Read frontmatter”docs meta reads a markdown file’s leading YAML block:
harn docs meta docs/plans/cache-redesign.md# {"status":"in-progress","date":"2026-07-13","tags":["cache"]}
harn docs meta docs/plans/cache-redesign.md status# in-progressRelative paths resolve from the configured repository root. Absolute paths also
work. The command exits nonzero when the file, frontmatter block, or requested
top-level key is missing. A requested scalar prints plainly in an interactive
terminal; piped output and --json use JSON.
Validate versioned metadata
Section titled “Validate versioned metadata”docs metadata validate checks every managed document. Managed files must use
schema: harnery-doc/v2; legacy or missing metadata is an error:
harn docs metadata validate --repo .The report separates valid, invalid, legacy, and missing files. Validation checks canonical UTC timestamps, timestamp order, document types, lifecycle status, owner ids, summaries, tags, relationships, access grants, and the fields required by each profile.
Keep timestamps current
Section titled “Keep timestamps current”docs metadata sync updates timestamps for staged Markdown changes, or for the
explicit files passed on the command line:
harn docs metadata syncharn docs metadata sync docs/runbook.md --reviewedharn docs metadata sync --checkIt advances updated_at for semantic metadata or body changes and advances
status_changed_at only when status changes. created_at is immutable.
--reviewed records a runbook review and sets its next review 180 days later.
Use --check in hooks or CI to reject timestamp drift without writing.
Add --cached when the check must read the staged blobs from Git rather than
the working tree. Cached synchronization is read-only and requires --check.
Create a managed document
Section titled “Create a managed document”docs new scaffolds valid v2 metadata for a plan, issue, handoff, runbook, or
topic. It refuses to overwrite an existing file and validates the requested
type, status, owner, and severity before writing.
Check internal links
Section titled “Check internal links”docs links resolves every relative Markdown target in the tracked docs and
validates the heading fragment each one points at:
harn docs links --repo .harn docs links --format json | jq '.findings[] | select(.rule == "missing-fragment")'It reports four rules. missing-target is a path that does not exist.
missing-fragment is a target that exists while the #heading after it does
not, which is the failure a plain file-existence check cannot see: the reader
lands silently at the top of the page. case-mismatch is a target that differs
from the file on disk only by letter case, so it resolves on macOS and breaks on
Linux; the finding carries the on-disk path as suggestion. escapes-repo is
opt-in via --check-escapes and flags a link that resolves on your machine only
because a sibling repository happens to sit next to it.
Fragments are matched against GitHub’s anchor rules: heading slugs including the
duplicate -1/-2 suffixes, explicit {#custom-id} suffixes, and HTML id or
name attributes. Pass --no-fragments to check target existence alone.
What it deliberately ignores
Section titled “What it deliberately ignores”A link checker is only useful if its output is worth reading, so these never produce a finding:
- External URLs,
mailto:/tel:schemes, and protocol-relative links. - Anything inside a fenced code block or an inline code span, so documented example links are not mistaken for real ones.
- Targets containing template syntax such as
{{slug}},${DIR}, or a<placeholder>, which cannot resolve by construction. - Root-absolute targets like
/pricing, which are far more often site routes than repository paths. #L42line references and#/spa/routehash routes, neither of which can name a heading.- Everything under the host’s configured documentation exclusions.
Findings in documents that record a past state — anything under archive/,
audits/, changelogs/, handoffs/, or decisions/ — are reported as
warnings rather than errors, because a document naming a path as it existed then
is accurate rather than broken. The same downgrade applies to lifecycle docs
(under issues/, plans/, or handoffs/) whose YAML frontmatter carries a
terminal status: (resolved, wontfix, shipped, abandoned): a resolved
issue is a settled record, while an open one keeps error severity because its
guidance is live. --strict reports all of these as errors.
For a single link that must stay as written, put <!-- links-allow: reason -->
on its line; for a whole document, put <!-- links-allow-file: reason -->
anywhere in it.
Advisory by default
Section titled “Advisory by default”docs links exits zero even when it finds errors. Whether link health should
block a commit or a CI run is an enforcement decision that belongs to the host
project, not a default this command imposes. Pass --fail to exit nonzero when
there is at least one error finding.
Other subcommands
Section titled “Other subcommands”docs lintchecks directory layout, filenames, and lifecycle status.--fastskips content checks for pre-commit use.docs lint --cachedvalidates the complete Git-index tree. During a path-scoped commit, Git supplies the proposed partial-commit index, so unrelated working-tree edits cannot change the verdict. Repository-wide entry and naming rules still run.docs sweepreports stalled plans, cold issues and handoffs, stale topic docs, and unverified runbooks.docs indexrebuilds marker-delimited audit and issue index tables.- Bare
docsreports markdown freshness from Git history.
Lint, sweep, and index read lifecycle status: only from strict
harnery-doc/v2 frontmatter. Legacy metadata is not read or normalized.