Lint a technote#

Documenteer provides a command-line linter, documenteer technote lint, that checks a technote’s metadata and structure before it is built and published. Its most important job is verifying that every author has an internal_id that resolves in the Rubin author database (authordb.yaml), since these IDs are needed to mint a DOI for the technote. The command also checks that the content declares an abstract and that requirements.txt installs Documenteer correctly.

Rubin’s technote CI (from the rubin-sphinx-technote-workflows repository) runs this command so that builds fail early when a technote’s metadata is incomplete.

Run the linter#

Run the linter from the root of a technote repository:

documenteer technote lint

If your technote uses the standard Makefile (see Migrate legacy reStructuredText/Sphinx technical notes), the linter also runs as part of the repository’s combined lint target, alongside the Pre-commit hooks and the link checker:

make lint

The command prints each issue it finds, prefixed with its stable rule code (for example, [TN101]), followed by a summary and a link to the documentation page for every rule that fired:

[TN101] Author Yusra AlSayyad is missing an internal_id. Did you mean 'alsayyady' (matched by ORCID)? Run 'documenteer technote sync-authors' after adding it.
Found 1 error(s) and 0 warning(s).
Learn more:
  TN101: https://documenteer.lsst.io/technotes/lint/tn101.html

The command exits with a non-zero status when any error-level issue remains, which is what causes a CI build to fail. Warning-level issues are reported but do not fail the command unless you pass --strict.

Options#

--dir <path>, -d <path>

Path to the technote directory to lint. Defaults to the current directory (.). The command locates technote.toml, the content file (index.rst, index.md, or index.ipynb), and requirements.txt within this directory.

--strict, -s

Promote all warnings to errors. With this flag, any finding — including the warning-level rules below — causes the command to exit non-zero.

Rules#

Each rule has a stable code so that findings are easy to triage (and, in the future, to except individually), and a landing page describing the rule, showing a failing technote, and walking through the fix. Codes are grouped by concern: TN0xx for structural rules, TN1xx for metadata, and TN2xx for content.

Technote lint rules#

Code

Group

Rule

Default severity

TN001

Structural

technote.toml conforms to the technote schema.

Error

TN002

Structural

requirements.txt declares documenteer with the [technote] extra.

Warning

TN003

Structural

requirements.txt does not pin Sphinx as a separate requirement.

Warning

TN004

Structural

technote.toml exists in the technote directory.

Error

TN005

Structural

technote.toml is syntactically valid TOML.

Error

TN006

Structural

The technote has a content file (index.rst, index.md, or index.ipynb).

Error

TN101

Metadata

Every author declares an internal_id.

Error

TN102

Metadata

Each author’s internal_id resolves in the Rubin author database.

Error

TN103

Metadata

The author database is reachable so that IDs can be resolved.

Warning

TN201

Content

The content file declares an abstract directive.

Error

TN202

Content

The abstract uses the abstract directive rather than a section heading.

Error

TN203

Content

The content file can be parsed to scan for an abstract.

Error

TN204

Content

The abstract directive has body content.

Error

Suggested author IDs#

An author internal_id that is missing (TN101) or unknown (TN102) is a dead end on its own, so the linter tries to name the ID you probably want. It searches the author database for the author’s name and appends a suggestion when exactly one entry matches confidently:

[TN101] Author Yusra AlSayyad is missing an internal_id. Did you mean 'alsayyady' (matched by ORCID)? Run 'documenteer technote sync-authors' after adding it.
[TN102] Author Lynne Jones has internal_id 'lynnej', which is not in the author database. Did you mean 'jonesrl' (R. Lynne Jones, matched by name)?

The message states what the match is based on:

  • matched by ORCID — the orcid in your technote.toml author entry is the same as that entry’s ORCID. This is the strongest evidence, and holds even when the two spell the name differently.

  • matched by name — a single near-exact name match, whose ORCID (if it has one) does not contradict yours.

The suggestion is deliberately conservative and best-effort: an ambiguous search (several equally good matches, as a common family name gives) or a match contradicted by a differing ORCID adds nothing to the message, and a failed lookup leaves the finding exactly as it would otherwise read. A suggestion never changes whether the command passes or fails — verify it before you use it.

How the abstract is found#

The abstract rules (TN2xx) work by scanning the source, not by running a Sphinx build, so it is worth knowing what the scan does and does not see.

  • Both the reStructuredText directive (.. abstract::) and the MyST fenced directive (a triple-backtick or ::: fence opening with {abstract}) are recognized, in any letter case — docutils lowercases directive names, so a {Abstract} fence builds and passes.

  • In reStructuredText, text on the marker line itself (.. abstract:: The abstract.) is directive content and passes.

  • Directive options (for example :class: dropdown) are configuration rather than content, so a directive holding only options is empty (TN204).

  • An abstract factored into another file and pulled in with .. include:: or a MyST {include} fence is found: those includes are resolved one level deep, relative to the content file. Includes within an included file are not followed, and an include that is missing or points outside the technote directory is ignored (Sphinx reports those itself).

  • An index.ipynb notebook’s markdown cells are concatenated before scanning, so its findings name the file without a line number.

Non-Sphinx technotes#

Some technote-series repositories are not Sphinx projects at all: they publish through the shared technote CI with a custom build command (an Org-mode deck, for example). When a directory has no content file (index.rst, index.md, or index.ipynb) and no conf.py, the linter treats it as one of these and checks only that its technote.toml is well formed — TN004, TN005, TN001, and the author rules (TN1xx). The requirements.txt rules (TN002/TN003), the content rules (TN2xx), and TN006 are skipped, since they describe a Documenteer/Sphinx build that these repositories do not have. A repository that does have a conf.py but no content file is a broken Sphinx technote, and is reported as TN006.

Note

Author internal_id values are the key to consistent author identification across Rubin documents, and a missing or unknown ID blocks DOI generation. See Maintaining author metadata to learn how to add and update the authors that TN101TN103 check.