Skip to Content
Conventionscovers: / owns: frontmatter

Convention: covers: / owns: frontmatter

Every page under docs/app/business-logic/** declares which code paths it documents via YAML frontmatter at the top of page.mdx. The docs-update skill and the CI safety check both consume this declaration. Without it, the skill can’t know which doc to propose edits for when code changes, and the CI check has no rules to enforce.

This page is the canonical reference. The full implementation lives in spec 003 .

The two fields

--- title: "Business logic: Suspend & unsuspend skills" description: "..." # Wide net (drift-detection): paths that, if changed, *might* require a # doc update. Globs are repo-root-relative. Brace expansion supported. covers: - api/src/features/account/change-request/** - api/src/features/account/logbook/** # Narrow net (ownership claim): paths the page *directly defines* the # behaviour of. Every owns: glob MUST also be matched by covers: # (lint-enforced). owns: - api/src/features/account/change-request/** ---

covers: — wide net

The set of code paths the page documents or refers to. When any of these paths change, the docs-update skill flags the page as potentially affected and offers to propose an edit (interactive yes/no).

Think of it as the answer to: “Which code paths, if changed, might need a corresponding update to this doc?”

owns: — narrow net (optional)

A strict subset of covers:. The set of paths the page directly defines the behaviour of. When any of these change, the skill auto-proposes an edit without prompting.

Think of it as the answer to: “Which code paths does this doc claim to be the source of truth for?”

Every entry in owns: MUST also be matched by at least one entry in covers: on the same page. Enforced by npm run docs:lint-covers.

coversRationale: — opt-out (when covers is empty)

A page may opt out of covers: by setting it to an empty array and adding a coversRationale: explaining why no code path applies:

--- title: "Business logic: Approval levels" description: "..." covers: [] coversRationale: > Approval levels are a conceptual layer documented here for reference; the rules are implemented across many features rather than at a single code path. See specific feature pages (suspend-unsuspend-skills, currency, etc.) for the actual code paths. ---

The lint accepts these; the docs-update skill skips them.

The five lint rules

Run npm run docs:lint-covers from docs/. Five rules, all enforced on every PR touching docs/**:

RulePass condition
covers-resolvesEvery covers: glob matches at least one tracked file in the monorepo.
owns-resolvesEvery owns: glob matches at least one tracked file.
owns-subset-of-coversEvery owns: glob is also matched by at least one covers: glob on the same page.
opt-out-rationale-requiredIf covers: [], then coversRationale: MUST be non-empty.
malformed-frontmatterThe YAML frontmatter parses cleanly.

When to add owns:

Add owns: only when the page genuinely defines the behaviour of a code path. Sometimes the page just describes code that’s defined elsewhere — in that case, covers: is enough.

Heuristic

Ask yourself: “If a developer rewrote this code path tomorrow, should the answer to ‘what does it do?’ change in this doc?”

  • Yes → add it to owns:. The doc is the source of truth.
  • No, but the doc mentions it → keep it in covers: only. The doc references the behaviour but isn’t authoritative.
  • No, and the doc doesn’t really touch it → don’t add it. Drift detection on irrelevant paths just creates noise.

Examples

A page that owns a single feature

covers: - api/src/features/account/change-request/** - api/src/features/account/logbook/** owns: - api/src/features/account/change-request/**

The page is the source of truth for change-request/**. It mentions logbook/** because the suspend flow updates the logbook, but the logbook’s own behaviour is documented elsewhere.

A page that owns nothing (cross-cutting policy)

covers: [] coversRationale: > Cross-cutting policy doc; behaviour is implemented across many features.

Approval levels, currency cycles, conventions — these don’t map to a single code path. The opt-out is the right call.

A page that covers a shared helper

covers: - api/src/shared/billing/** # No owns: — this page mentions billing helpers but doesn't define them.

docs-update will prompt before proposing edits, because the page might or might not need updating depending on what changed.

How the skill uses this

When you run /docs-update (or npm run docs:update), the skill:

  1. Computes your branch diff vs main.
  2. Walks every /business-logic/ page and parses its frontmatter.
  3. For each changed file, classifies into one of four buckets:
    • owned → auto-propose an edit (no prompt).
    • covered → prompt: “page X covers this path but doesn’t claim ownership. Propose an edit? [y/N]”.
    • unmatched (matches businessLogicRoots but no page covers it) → flag as a coverage gap.
    • irrelevant → do nothing.

The skill never writes to owns:-matched pages without an LLM reasoning step about whether the change is genuinely behavioural. See spec 003 § 5  for the full classification → action mapping.

How the CI safety check uses this

The blocking docs-required workflow doesn’t read covers: per se — it consults the configured businessLogicRoots globs from docs/.docs-update.config.json. If your PR touches those paths but no docs/** files, the check fails (unless the PR description contains docs-update: not-needed (reason: ...)).

The covers: map is consulted by the skill itself, not by the CI gate. The two layers are complementary:

  • covers: / owns: = “which doc page is on the hook for this change?” → drives skill proposals.
  • businessLogicRoots = “is this a business-logic change at all?” → drives the deterministic gate.

See also

Last updated on