Spec Sync Integration

spec-sync is a tool that keeps Markdown module specs aligned with the code they describe. Merlin uses it for two things:

  1. Read specs at task start to inject as planning constraints.
  2. Verify specs at task end as part of the verify lane.

How Specs Look

Every spec is a Markdown file in specs/<module>/<module>.spec.md with YAML frontmatter:

---
module: agent
version: 2
status: draft
files:
  - crates/merlin-core/src/agent.rs
  - crates/merlin-core/src/spec_loader.rs

db_tables: []
depends_on:
  - provider
  - fledge-protocol
---

# Agent

## Purpose
The agent module implements Merlin's core state machine...

## Public API
...

## Invariants
1. State always starts and ends at Idle...
...

The required sections are configured in .specsync/config.toml:

required_sections = [
    "Purpose",
    "Public API",
    "Invariants",
    "Behavioral Examples",
    "Error Cases",
    "Dependencies",
    "Change Log",
]
enforcement = "strict"

Reading Specs (Task-time)

Agent::load_relevant_specs(task) runs:

  1. fledge plugins run specsync-list → list of registered modules.
  2. spec_loader::select_relevant_specs(task, &all, top_n=3) → top matches by token overlap.
  3. For each pick: fledge plugins run specsync-read <name> → full markdown.
  4. spec_loader::extract_constraint_sections(name, content) → constraint-bearing sections only.
  5. Context::add_spec_constraint(text) → injected into the system prompt under “Active Spec Constraints”.

See Spec-aware Planning for the user-facing view.

Verifying Specs (Verify-lane)

fledge.toml wires spec-check into the verify lane:

[lanes.verify]
steps = ["fmt-check", "lint", "test", "integration-test", "spec-check"]

[tasks]
spec-check = "fledge spec check"

fledge spec check exits non-zero on any of:

  • Required section missing from a registered spec.
  • File listed in spec frontmatter doesn’t exist.
  • Code file in source_dirs references no spec (under enforcement = "strict").

When verify_before_complete = true and spec-check fails, the verifier output is fed back to the LLM as part of the retry message (see Verification & Retries).

Updating Specs

If you change a public API in code, the corresponding spec needs to move with it. Bump the spec’s version, add a row to ## Change Log, and update the API tables. The spec-check step won’t catch staleness — it catches missing sections and missing files. Discipline matters.

The default agent system prompt explicitly tells the LLM “Read relevant specs before modifying code” — and Merlin runs spec-check after every change. The combination is what makes specs binding rather than aspirational.