5  Authoring in Quarto

5.1 .need blocks

The authoring surface remains simple: objects are declared as Pandoc Div blocks with the .need class.

::: {.need #NFR-001 type="non-functional-requirement" status="approved" priority="high" tags="performance"}
verified-by: TC-006
implemented-by: COMP-002

## Respond to authorization within the latency objective

The authorization decision shall remain within the defined SLO under nominal load.

### Rationale

Centralized policy is acceptable only if it does not materially degrade the experience.
:::

Machine-queryable metadata stays in the block preamble; human explanations remain in Markdown. This distinction prevents documentation from becoming an excessively structured form.

A ### Rationale section inside the block is indexed into the object’s machine-readable rationale field — so per-field diffs, baselines, and exports see it — while the body itself is preserved verbatim. A rationale: preamble key, when present, takes precedence.

5.1.1 Anatomy of a block

A .need block has three parts, in this order:

  1. The opening fence, which carries the required #ID and optional inline attributes.
  2. The preamble, a YAML-like key: value region. It ends at the first heading, or at the first line that is neither key: value nor a - list item. Blank lines inside it are ignored, not terminators.
  3. The body, ordinary Markdown. Its first heading becomes the object title; without a heading, the title falls back to the ID.

Because a prose line closes the preamble, keep every key: value line above the body. A metadata line written after a sentence is read as body text: it is not an attribute, not a relation, and produces no diagnostic.

Both the fence attributes and the preamble write to the same set of keys, so status can be declared in either place. When the same key appears in both, the preamble wins. Use the fence for short scalars and the preamble for anything that benefits from being on its own line.

5.1.2 Reserved keys

These keys have defined meaning; everything else becomes a queryable attribute.

Key Default Meaning
#ID Required. The object’s identity, given on the fence as a Pandoc identifier.
type need Engineering type. Drives roles, required attributes, prefixes, and badge color.
status draft Lifecycle status. Validated against allowed-statuses when configured.
title first body heading Object title. Falls back to the ID when neither is present.
rationale ### Rationale section Machine-readable rationale. The key takes precedence over the section.

priority, tags, and date are ordinary attributes rather than reserved keys, but they receive dedicated presentation: priority gets a badge and rank-aware sorting, tags renders as tag chips and feeds the tag catalog, and date appears on architecture-decision cards.

5.1.3 Relations and attributes

Any preamble key that names a relation in the catalog declares relations; every other key becomes an attribute readable as attributes.<name> in named queries.

::: {.need #FUN-014 type="functional-requirement" status="approved" priority="high"}
derives-from: SYS-003
verified-by: TC-020, TC-021
implemented-by:
  - COMP-PARSER
  - COMP-ANALYSIS
owner: Platform team
expires: 2027-01-31

## Reject malformed declarations
:::

Multiple targets are written either as one value separated by commas or semicolons, or as a - list on the following lines. The two forms are equivalent.

Declare each key once per block. The preamble is a mapping, so repeating a key keeps only its last occurrence; write verified-by: TC-020, TC-021 or the list form instead of two verified-by: lines. A repeated key is reported as QND003 so the discarded value cannot pass unnoticed.

Relation targets are not resolved while parsing. A target that names no declared object is reported as REQ005 during validation, which is how typos surface instead of becoming silent dead ends.

5.2 References and views

The book can project the graph with shortcodes such as:

{{< need NFR-001 title=true >}}
{{< need-table types="architecture-decision" columns="id;title;status" >}}
{{< need-matrix rows="functional-requirement" columns="test-case" relation="verified-by" >}}
{{< need-flow root="NFR-001" depth="4" direction="LR" >}}
{{< need-inspector ADR-002 >}}
{{< need-graph view="graph-exploration" >}}
{{< need-c4 root="SYS-001" level="context" >}}
{{< adr-table status="accepted" >}}
{{< adr-count status="accepted" >}}

Views read the canonical artifact; they do not reimplement semantic rules in the renderer.

The views reference documents every registered shortcode, its selectors and options, interactive controls, and page-sidebar configuration.

5.3 Tag index and deep linking

For a clickable tag catalog, create a chapter (e.g. tags.qmd) containing:

{{< need-tags >}}

This shortcode renders interactive tag chips and a consolidated table of all tagged objects. To link every tag badge across the site into this page, configure _quarto.yml:

quarto-needs:
  tags-page: tags

When configured, clicking any tag badge anywhere in the project navigates to tags.html?tag=<slug> with that tag pre-filtered.

5.4 Localized titles

Object titles can be localized without duplicating the engineering model. Place a translated sibling next to its canonical source file, adding the locale to the stem — for example, declare objects in requirements.qmd and provide requirements.pt-BR.qmd:

  • Both files must declare the same object IDs with the same types, statuses, attributes, and relations. Only the title, body, and rationale may differ; a sibling that changes engineering metadata fails the render with an error.
  • Localized siblings are presentation sources. The canonical graph is built from the unsuffixed files; localized files never add, remove, or hide objects.
  • quarto-needs scan validates every localized sibling and writes the approved titles to .quarto-needs/i18n/<locale>.json.
  • The page filename suffix selects the locale (for example, chapter.pt-BR.qmd renders as pt-BR). Views, tables, dashboards, and the need-graph canvas render the localized titles for that locale, falling back to the canonical title when a locale has no entry for an object. Spell the locale identically in sibling filenames and page filenames — the mapping is exact, not case-normalized.

English pages keep the canonical titles; no default title map is required. The language server keeps localized siblings consistent across ID renames.

5.5 Prefix configuration

Prefixes are declared per type:

[types.system-requirement]
id-prefix = "SYS-"

[types.architecture-decision]
id-prefix = "ADR-"

When configured, an incompatible ID produces ID001. For a project that needs a compound namespace:

[types.architecture-decision]
id-prefix = "PAY-ADR-"

When the project context already defines the domain, use shorter prefixes. The goal is readable identity, not ritual namespace repetition.