ADR validation

Purpose

The docs build pipeline validates ADRs before Hugo builds the aggregated site.

The validator exists to keep three things true at the same time:

  • ADR frontmatter must satisfy the pinned upstream Structured MADR base schema.
  • Dataverket-specific metadata and document structure rules must remain consistent across repos.
  • The aggregated docs corpus must remain safe to combine into one site without ambiguous ADR filenames.

Where the validator runs

The canonical entrypoint is the Task-based docs pipeline.

  • task validate runs advisory validation.
  • task validate-strict runs the same validator in strict mode.
  • task build runs validation before Hugo builds the site.

The CLI command is smadr-validator.

Sources of truth

The validator has three layers of definition:

  1. Policy configuration: config/smadr-validator.yaml
  2. Implementation: internal/smadrvalidator/
  3. Regression coverage and examples: internal/smadrvalidator/validator_test.go

Use the configuration file to understand which rules are enabled and whether they are blocking. Use the Go package to understand exact resolution and normalization behavior.

Frontmatter contract

Dataverket ADRs use the Structured MADR frontmatter model.

For the author-facing field definitions, allowed values, examples, and Dataverket’s guidance on using related for flexible ADR relationships, see ADR frontmatter.

Use the upstream specification to understand the baseline field meanings and structure. Use the local schema and validator config to understand what Dataverket actually accepts.

If this document and the code-backed contract ever disagree, the schema and validator config win.

Enforcement summary

This page focuses on what the validator enforces rather than on frontmatter authoring guidance.

At a high level, the validator enforces three contract layers:

  • required and optional fields from the configured JSON Schema
  • Dataverket allowlists and filename policy from config/smadr-validator.yaml
  • required ADR body sections in markdown

Today that includes Dataverket-specific policy such as local allowlists for project and category, support for status: rejected, support for relative related paths, and required sections Status, Context, Decision, Consequences, Decision Outcome, and Audit.

Validation model

Validation runs in layers.

1. Corpus-level namespace checks

The validator scans all discovered ADRs in the aggregated docs corpus and checks for duplicate basenames.

This is important because ADR references are currently filename-based. If two repos both contain the same ADR basename, filename-only references become ambiguous in the combined site.

Current policy:

  • duplicate_basename is a blocking error.

2. Frontmatter parse

Each ADR must have valid YAML frontmatter.

Current policy:

  • missing frontmatter is an error
  • invalid frontmatter is an error

3. Base Structured MADR schema validation

The validator uses a locally pinned copy of the upstream Structured MADR JSON Schema as its base.

That base schema currently lives at:

  • internal/smadrvalidator/schemas/structured-madr.schema.json

It is embedded into the validator binary so local runs, CI, and containerized runs all use the same baseline by default.

Dataverket can also replace that default with a local custom schema through schema.path in config/smadr-validator.yaml.

The current local custom schema lives at:

  • internal/smadrvalidator/schemas/dataverket-policy.schema.json

That schema is a Dataverket-specific copy derived from the upstream baseline. It keeps the standard structure but applies local policy adjustments, such as allowing the rejected status and relative related paths.

Current policy:

  • schema_validation is a blocking error.

4. Dataverket-specific metadata checks

After base-schema validation, the validator applies Dataverket policy from config/smadr-validator.yaml.

This includes:

  • allowed project values
  • allowed category values
  • filename format expectations

Current policy:

  • unsupported allowed values are errors
  • invalid filenames are errors

5. Document structure checks

The validator checks that required ADR sections exist in the markdown body.

Current default sections are:

  • Status
  • Context
  • Decision
  • Consequences
  • Decision Outcome
  • Audit

Current policy:

  • missing required sections are warnings

The related field is validated in two ways.

  • local relative filesystem references work
  • filename-only references are resolved against the aggregated ADR corpus by basename
  • external URLs are ignored by this rule

Current policy:

  • invalid_related_target is a warning

This makes cross-repo references possible without path traversal in frontmatter, as long as ADR basenames remain globally unique across the aggregated corpus. Duplicate basenames are blocked separately by the duplicate_basename rule because they make filename-only references ambiguous.

Advisory vs strict mode

The validator always reports both errors and warnings.

  • advisory mode prints the findings and exits successfully
  • strict mode exits non-zero if blocking errors are present

This allows the same validator to support local authoring and later stricter CI behavior.

In practice, the docs-builder entrypoint keeps local builds advisory, while the Forgejo pipeline runs a separate strict validation step before the full site build.

YAML frontmatter notes

The validator uses yaml.v3 through the shared frontmatter parser.

  • consistently indented lists work with either 2-space or 4-space indentation
  • mixed indentation within the same YAML list can parse in surprising ways and should be treated as invalid authoring

Date-like YAML values are normalized to plain YYYY-MM-DD strings before JSON Schema validation so the upstream date format checks behave consistently.

Maintenance guidance

When changing validator behavior:

  1. Update config/smadr-validator.yaml if the policy changes.
  2. Update internal/smadrvalidator/ if the implementation changes.
  3. Update internal/smadrvalidator/validator_test.go when the expected behavior changes.
  4. Update this document if the author-facing contract changes.