ADR-003: YAML Config instead of JSON/AsciiDoc

Status

Accepted

Context

The plugin needs a configuration format for steering rules. The config is written by the user and read by the ConfigLoader component. Requirements:

  • Readability: Must be easy for humans to write (no expert knowledge required)

  • Diff-friendliness: One changed rule = one line diff

  • Validatability: Must be checkable against a schema (Zod)

  • Comments: Users must be able to comment out / deactivate rules

  • Ecosystem Alignment: Should be compatible with existing standards

Alternatives Considered

Option A: JSON

{
  "contracts": [
    {
      "id": "step-confirmation",
      "mode": "BLOCK",
      "triggers": [{"type": "tool", "pattern": "*", "count": 3}]
    }
  ]
}

Advantages: - Natively supported by Zod (JSON.parse) - Widely used, every language can read JSON - Strict format (no interpreted values)

Disadvantages: - No comments — Users cannot comment out rules - Poor diff readability — especially with nested objects - Trailing commas not allowed (source of errors) - Users must quote keys (unnecessary typing effort) - More syntactic "noise" than YAML for comparable structures

Option B: YAML (chosen)

contracts:
  - id: step-confirmation
    mode: BLOCK
    # Temporary disabled:
    # - id: source-anchor
    triggers:
      - type: tool
        pattern: "*"
        count: 3

Advantages: - Comments with # — rules can be deactivated without being deleted - Excellent diff readability — one line change = one line diff - Easier to write (no quotes around keys, fewer brackets) - Already established for comparable agent-steering formats: - agentcontract/spec uses .contract.yaml - Kiros /steering uses YAML - Natively parsed by js-yaml library - Can be validated with Zod (after YAML→JS object transformation)

Disadvantages: - Significant whitespace — indentation errors cause parse failures - Not type-safe by default (Zod schema validation catches this) - Less strict than JSON (empty values become null) - js-yaml as an additional runtime dependency

Option C: AsciiDoc (.adoc)

Format of the Semantic-Anchors repository. Could serve as a native config format.

Disadvantages: - Not a structured data format — AsciiDoc is for documentation, not configuration - No standardised schema validation - Would need to be parsed with regex/ADoc parser (no Zod support) - Far less common for config purposes - Would make contribution to the Semantic-Anchors repo easier, but worsen user experience

Option D: opencode.jsonc (JSON with Comments)

opencode itself uses opencode.jsonc as its config format.

Disadvantages: - Positioning: Plugin config should be separate from opencode config (own file) - .jsonc (JSON with Comments) has no native parser — would need stripping before JSON.parse - Less common than YAML for steering rules - agentcontract/spec and Kiros also use YAML — alignment more important than opencode convention

Evaluation Criteria

Criterion Weight Description

Readability

High

Easy for humans to write and read

Diff-friendliness

High

Small diff size for individual changes

Comments

High

Users must be able to deactivate rules

Schema-validatability

High

Must be checkable with Zod

Ecosystem alignment

Medium

Should harmonise with agentcontract/spec etc.

Type safety

Medium

Wrong types should be detected early

Decision

Option B: YAML was chosen.

Rationale: - Only option with comments + diff-friendliness + schema validation - Alignment with agentcontract/spec (YAML) and Kiros (YAML) is strategically more important than alignment with opencode (JSONC) - js-yaml + Zod provide sufficient type safety - Indentation problems are detected early through Zod schema errors

Consequences

  • Positive: Users can comment out rules (feature JSON does not offer)

  • Positive: Small diff size for config changes

  • Positive: Alignment with agentcontract/spec and Kiros

  • Negative: Additional dependency (js-yaml)

  • Negative: Indentation errors are a common source of mistakes for YAML beginners

  • Negative: No 1:1 mapping to opencode config format (two different formats in the setup)

  • Trade-off: YAML + Zod schema = slightly more complexity when loading, but better UX when writing

  • Decision 3 in 04-solution-strategy.md

  • config/loader.ts in 05-building-block-view.md

  • config/schema.ts in 05-building-block-view.md

Sources