Documentation Style Guide

Writing and formatting conventions for all rfgen documentation. Pages that deviate from this guide require an explicit exemption comment.

Audience and tone

  • Write for an engineering audience: implementers, plugin authors, downstream data consumers, and reviewers.

  • Use short paragraphs (three sentences or fewer) and bullets for lists.

  • No em-dashes. Use commas, semicolons, or rewrite the sentence.

  • Avoid LLM-voice phrasings: “it’s worth noting”, “importantly”, “this allows us to”, “as we can see”, “deep dive”. State the fact directly.

Heading conventions

  • Use ## See Also (title-case “See Also”, always two words) for the cross-link section at the bottom of every concept page. Do not use “Where to look next”, “Where to Look Next”, “Further reading”, or any other variant.

  • Reserve ## for major page sections. Use ### for subsections, #### for sub-subsections.

  • Do not skip heading levels.

Pre-implementation admonition

Every page that describes an API surface (class, method, config field, CLI command) and does not have a corresponding code implementation carries this admonition at the top, immediately after the page title:

:::{warning}
**Pre-implementation.** This page describes proposed contracts. Class signatures, parameter types, config field names, and behavior are subject to change before code lands. Once implementation exists, content here will be regenerated from docstrings or sourced from running tests.
:::

Rules:

  • Place the admonition before any module summary or introductory prose.

  • Do not add it to docs/index.md (which carries its own pre-release warning), docs/glossary.md, or this file.

  • Pure conceptual pages without specific API claims do not require it.

  • Section index pages (getting-started/index.md, how-to/index.md, etc.) carry a shorter form adapted to README/index format (a :::{warning} callout, not a code block).

Maturity label vocabulary

Use these exact labels in tables and admonitions. No other variants.

Label

Meaning

planned

Named roadmap item; no contract yet.

proposed-contract

Intended API or schema shape. May still change before code lands.

implemented

Code exists; examples are runnable.

verified

Implementation has contract tests or golden validation.

runtime-isolated

Available only through subprocess, ZMQ, or a license boundary.

reference-only

Used for validation or explanation, not runtime generation.

deprecated

Still present but scheduled for removal; use the replacement instead.

See Reference / Maturity Matrix for the per-feature status table.

Enum vs string in Python examples

Framework-owned closed choices (placement strategies, storage backends, overlap policies, annotation types, channel model names, record axis modes, etc.) use StrEnum members in Python code:

# Correct
storage_cfg = StorageConfig(backend=StorageBackend.ZARR_LOCAL, path="./out")
labeler = JointLabeler(overlap_policy=SceneOverlapPolicy.REJECT)

# Incorrect
storage_cfg = StorageConfig(backend="zarr_local", path="./out")

YAML configs may use the enum string value because Pydantic deserializes it at validation time:

# Correct in YAML
storage:
  backend: zarr_local
  path: ./out

Open plugin registry names such as ExecutorConfig.name stay as plain strings because they resolve through the plugin registry, not a closed enum.

Code-span linking rule

Every code span in prose that has an API reference entry must link to that entry’s anchor. Examples:

  • {ref}`BaseEmitter <class-rfgen-emitters-baseemitter>`

  • {ref}`StorageConfig <class-rfgen-config-storageconfig>`

  • {ref}`Stage <class-rfgen-channels-stage>`

This applies in concept pages, how-to guides, and cross-links in reference pages. It does not apply inside code blocks.

API anchor format

  • Class: class-rfgen-<module>-<lowercased-classname>

    • Example: class-rfgen-config-storageconfig, class-rfgen-channels-basechannel

  • Method on a class: method-<lowercased-classname>-<methodname> or method-rfgen-<module>-<lowercased-classname>-<methodname>

    • Example: method-baseemitter-generate, method-rfgen-config-sceneconfig-model_validate

These mirror what sphinx.ext.autodoc emits, so swapping hand-written stubs for generated pages is mechanical.

Section index exemptions

Section index pages (*/index.md) are exempt from the ## Minimal Example and ## See Also requirements because their job is navigation, not instruction. They should carry audience framing (Who should read this, Before you start, What this section is not) instead.

Sub-pages that are reference or catalog content rather than instructional concept pages (e.g., emitters/coverage.md, emitters/library-landscape.md) are also exempt from the ## Minimal Example requirement. These pages should carry a one-line pointer: See [Parent § Minimal Example](../parent.md#minimal-example) for instantiation patterns.

Reference table conventions

Reference-surface tables (the kind that document a contract or enumerate a set of plugin slots, fields, or backends) follow these rules. They apply to concept pages, sub-pages, and reference/ pages alike.

Column headers carry the canonical field name. Use Tag, Transformation, ABC, Default backend, Notes, Scope, Sample rate, Carrier frame. Do not use #, Item, Thing, or other ad-hoc names. The header is the contract; treat it like a parameter name.

One row, one entity. Every row describes a single thing of the same kind. Do not mix transformations and operational steps (sums, composer-driven combine points) in the same table. Move operational steps into a :::{note} callout adjacent to the table.

No grouping by column repetition. If rows share a group, split into per-group sub-tables under sub-section headings, or use the table on a per-group sub-page. Do not write 1 TX impairments on five consecutive rows.

Every code identifier links. Class, ABC, method, enum, and config-field names in cells use {ref} links to their canonical anchor (see “Code-span linking rule” above). Plain backticks are reserved for shell commands and inline literals that have no API surface.

Enum cells show both the member and a human label when both are useful. When a row references an enum member, include the member name (e.g., DAC_QUANTIZATION) so the reader can wire up the contract directly. The human label (e.g., “DAC quantization”) goes in a Name column adjacent to the member, not in the same cell.

Pipeline overview tables list groups, not transformations. Page-level overview tables that summarize a multi-stage pipeline have one row per group with columns like Group, Scope, Sample rate, Transformations (a tag range), Reference (the per-group sub-page link). Per-transformation rows belong on the sub-page, not the overview.

Footnotes and admonitions for operational caveats. When a row has caveats that do not fit a column (“composer-driven”, “subject to change at v0”, “not yet implemented”), put the caveat in a :::{note} directly below the table or as a numbered footnote. Do not stuff prose into a column meant for a structured value.

Example pipeline overview:

| Group | Scope | Sample rate | Transformations | Reference |
|-------|-------|-------------|-----------------|-----------|
| {ref}`Group.TX <class-rfgen-channels-group>` | per emitter | `R_emitter` | DAC, PA, TX phase noise, TX IQ imbalance, CFO | [TX impairments](channels/tx.md) |

Example per-transformation table:

| Name | {ref}`Transformation <class-rfgen-channels-transformation>` | ABC | Default backend |
|------|------|-----|-----------------|
| DAC quantization | `DAC_QUANTIZATION` | {ref}`BaseDACQuantization <class-rfgen-channels-basedacquantization>` | Uniform mid-tread quantizer at configured bit depth |