SpecSync
Bidirectional spec-to-code validation with cross-project references, dependency graphs, and AI-powered generation. Written in Rust. Single binary. 11 languages. VS Code extension.
Quick Start • Spec Format • CLI • VS Code Extension • Cross-Project Refs • GitHub Action • Config • Docs Site
What It Does
SpecSync validates markdown module specs (*.spec.md) against your source code in both directions:
| Direction | Severity | Meaning |
|---|---|---|
| Code exports something not in the spec | Warning | Undocumented export |
| Spec documents something missing from code | Error | Stale/phantom entry |
| Source file in spec was deleted | Error | Missing file |
| DB table in spec missing from schema | Error | Phantom table |
| Column in spec missing from migrations | Error | Phantom column |
| Column in schema not documented in spec | Warning | Undocumented column |
| Column type mismatch between spec and schema | Warning | Type drift |
| Required markdown section missing | Error | Incomplete spec |
Supported Languages
Auto-detected from file extensions. Same spec format for all.
| Language | Exports Detected | Test Exclusions |
|---|---|---|
| TypeScript/JS | export function/class/type/const/enum, re-exports, export * wildcard resolution |
.test.ts, .spec.ts, .d.ts |
| Rust | pub fn/struct/enum/trait/type/const/static/mod |
#[cfg(test)] modules |
| Go | Uppercase func/type/var/const, methods |
_test.go |
| Python | __all__, or top-level def/class (no _ prefix) |
test_*.py, *_test.py |
| Swift | public/open func/class/struct/enum/protocol/actor |
*Tests.swift |
| Kotlin | Top-level declarations (excludes private/internal) | *Test.kt, *Spec.kt |
| Java | public class/interface/enum/record/methods |
*Test.java, *Tests.java |
| C# | public class/struct/interface/enum/record/delegate |
*Test.cs, *Tests.cs |
| Dart | Top-level (no _ prefix), class/mixin/enum/typedef |
*_test.dart |
| PHP | class/interface/trait/enum, public function/const, skips private/protected and __ magic methods |
*Test.php |
| Ruby | class/module, public methods with visibility tracking, attr_accessor/attr_reader/attr_writer, constants |
*_test.rb, *_spec.rb |
Install
GitHub Action (recommended)
- uses: CorvidLabs/spec-sync@v4
with:
strict: 'true'
require-coverage: '100'
Crates.io
Pre-built binaries
Download from GitHub Releases:
# macOS (Apple Silicon)
|
# macOS (Intel)
|
# Linux (x86_64)
|
# Linux (aarch64)
|
Windows: download specsync-windows-x86_64.exe.zip from the releases page.
From source
Quick Start
Spec Format
Specs are markdown files (*.spec.md) with YAML frontmatter in your specs directory.
Frontmatter
---
module: auth # Module name (required)
version: 3 # Spec version (required)
status: stable # draft | review | stable | deprecated (required)
files: # Source files covered (required, non-empty)
- src/auth/service.ts
- src/auth/middleware.ts
db_tables: # Validated against schema dir (optional)
- users
- sessions
depends_on: # Other spec paths, validated for existence (optional)
- specs/database/database.spec.md
- corvid-labs/algochat@messaging # Cross-project ref (owner/repo@module)
---
Required Sections
Every spec must include these ## sections (configurable in specsync.json):
Purpose, Public API, Invariants, Behavioral Examples, Error Cases, Dependencies, Change Log
Note: Requirements (user stories, acceptance criteria) belong in a companion
requirements.mdfile, not inline in the spec. Specs are technical contracts; requirements are product intent. See Companion Files below.
Public API Tables
SpecSync extracts the first backtick-quoted name per row and cross-references it against code exports:
module: auth
version: 3
status: stable
files:
- - - - -
Handles authentication and session management. Validates bearer tokens,
manages session lifecycle, provides middleware for route protection.
1. 2.3.
- --
- --
CLI Reference
specsync [command] [flags]
Commands
| Command | Description |
|---|---|
check |
Validate all specs against source code (default). --fix auto-adds missing exports as stubs |
diff |
Show exports added/removed since a git ref (default: HEAD) |
coverage |
File and module coverage report |
report |
Per-module coverage report with stale and incomplete detection |
generate |
Scaffold specs for modules missing one (--provider for AI-powered content) |
scaffold <name> |
Scaffold spec + auto-detect source files + register in registry |
add-spec <name> |
Scaffold a single spec + companion files (requirements.md, tasks.md, context.md) |
deps |
Validate cross-module dependency graph (cycles, missing deps, undeclared imports) |
changelog <range> |
Generate changelog of spec changes between two git refs |
comment |
Post spec-sync check summary as a PR comment (same validation pipeline as check). --pr N to post, omit to print |
import <source> <id> |
Import specs from GitHub Issues, Jira, or Confluence |
wizard |
Interactive step-by-step guided spec creation |
resolve |
Verify depends_on references exist. --remote fetches registries from GitHub |
init-registry |
Generate specsync-registry.toml from existing specs |
score |
Quality-score spec files (0–100) with improvement suggestions |
compact |
Compact old changelog entries in spec files. --keep N (default: 10) |
archive-tasks |
Archive completed tasks from companion tasks.md files |
view |
View specs filtered by role (--role dev|qa|product|agent) |
merge |
Auto-resolve git merge conflicts in spec files |
new <name> |
Quick-create a minimal spec with auto-detected source files. --full includes companion files |
stale |
Identify specs that haven't been updated since their source files changed |
rules |
Display configured validation rules and built-in rule status |
migrate |
Upgrade from 3.x to 4.0.0 layout (.specsync/ directory, TOML config). --dry-run to preview, --no-backup to skip |
lifecycle promote <spec> |
Advance spec to next status (draft→review→active→stable) |
lifecycle demote <spec> |
Step back one status level |
lifecycle set <spec> <status> |
Set spec to any status (with transition validation) |
lifecycle status [spec] |
Show lifecycle status of one or all specs |
lifecycle history <spec> |
Show transition history (audit log) for a spec |
lifecycle guard <spec> [target] |
Dry-run guard evaluation — check if transition would pass |
lifecycle auto-promote |
Promote all specs that pass their transition guards. --dry-run to preview |
lifecycle enforce |
CI enforcement — validate lifecycle rules, exit non-zero on violations. --all for all checks |
issues |
Verify GitHub issue references in spec frontmatter. --create to create missing issues |
hooks |
Install/uninstall agent instructions and git hooks (install, uninstall, status) |
mcp |
Start MCP server for AI agent integration (Claude Code, Cursor, etc.) |
init |
Create default specsync.json |
watch |
Live validation on file changes (500ms debounce) |
Flags
| Flag | Description |
|---|---|
--strict |
Warnings become errors (recommended for CI) |
--require-coverage N |
Fail if file coverage < N% |
--root <path> |
Project root (default: cwd) |
--provider <name> |
AI provider: auto, anthropic, openai, or command. auto detects installed provider. Without --provider, generate uses templates only. |
--fix |
Auto-add undocumented exports as stub rows in spec Public API tables |
--force |
Skip hash cache and re-validate all specs |
--create-issues |
Create GitHub issues for specs with validation errors (on check) |
--dry-run |
Preview changes without writing files (on compact, archive-tasks, merge) |
--stale N |
Flag specs N+ commits behind their source files (on check) |
--exclude-status <s> |
Exclude specs with the given status. Repeatable |
--only-status <s> |
Only process specs with the given status. Repeatable |
--mermaid |
Output dependency graph as Mermaid diagram (on deps) |
--dot |
Output dependency graph as Graphviz DOT (on deps) |
--full |
Include companion files (on new) |
--json |
Structured JSON output |
Exit Codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
Errors, strict warnings, or coverage below threshold |
Cross-Project References
Specs can declare dependencies on modules in other repositories using owner/repo@module syntax in depends_on:
depends_on:
- specs/database/database.spec.md # Local reference
- corvid-labs/algochat@messaging # Cross-project reference
Registry
Each project publishes a specsync-registry.toml at its root to declare available spec modules:
[]
= "myapp"
[]
= "specs/auth/auth.spec.md"
= "specs/messaging/messaging.spec.md"
= "specs/db/database.spec.md"
Generate one automatically from existing specs:
Resolving References
Remote resolution fetches specsync-registry.toml from each referenced repo and validates that the module exists. Requests are grouped by repo to minimize HTTP calls.
Zero CI cost by default — specsync check validates local refs only (no network). Use --remote explicitly when you want cross-project verification.
Companion Files
When you run specsync generate or specsync add-spec, three companion files are created alongside each spec:
| File | Author | Validated? | Purpose |
|---|---|---|---|
{module}.spec.md |
Dev/Architect | Yes — against code | Technical contract |
tasks.md |
Anyone | No | Work coordination |
context.md |
Dev/Agent | No | Architecture notes |
requirements.md |
Product/Design | No | The ask, acceptance criteria |
All scaffolded by SpecSync, all human-filled. Only the spec gets bidirectional validation.
Convention: Requirements (user stories, acceptance criteria) must live in
requirements.md, not as inline## Requirementssections inside the spec. Specs define the technical contract; requirements capture product intent. Inline requirements in non-draft specs produce a warning prompting you to move them to the companion file.
requirements.md — Product requirements and acceptance criteria:
spec: auth.spec.md
-
-
tasks.md — Multi-role checkpoint tracking:
spec: auth.spec.md
-
- ---
context.md — Agent briefing document:
spec: auth.spec.md
These files are designed for team coordination and AI agent context — they give any contributor (human or AI) the full picture of where a module stands.
VS Code Extension
Install from the VS Code Marketplace or search "SpecSync" in the Extensions panel.
Features
| Feature | Description |
|---|---|
| Inline diagnostics | Errors and warnings mapped to spec files on save |
| CodeLens scores | Quality scores (0–100) displayed inline above spec files |
| Coverage report | Rich webview with file and LOC coverage |
| Scoring report | Per-spec quality breakdown with improvement suggestions |
| Status bar | Persistent pass/fail/error indicator |
| Validate-on-save | Automatic validation with 500ms debounce |
Commands (Command Palette)
SpecSync: Validate Specs— runspecsync checkSpecSync: Show Coverage— open coverage reportSpecSync: Score Spec Quality— open scoring reportSpecSync: Generate Missing Specs— scaffold specs for unspecced modulesSpecSync: Initialize Config— createspecsync.json
Settings
| Setting | Default | Description |
|---|---|---|
specsync.binaryPath |
specsync |
Path to the specsync binary |
specsync.validateOnSave |
true |
Run validation on file save |
specsync.showInlineScores |
true |
Show CodeLens quality scores |
The extension activates automatically in workspaces containing specsync.json, .specsync.toml, or a specs/ directory. Requires the specsync CLI binary to be installed and on your PATH (or configured via specsync.binaryPath).
GitHub Action
Available on the GitHub Marketplace. Auto-detects OS/arch, downloads the binary, runs validation.
Inputs
| Input | Default | Description |
|---|---|---|
version |
latest |
Release version to download |
strict |
false |
Treat warnings as errors |
require-coverage |
0 |
Minimum file coverage % |
root |
. |
Project root directory |
args |
'' |
Extra CLI arguments |
comment |
false |
Post spec drift results as a PR comment (requires pull_request event) |
token |
${{ github.token }} |
GitHub token for posting PR comments (needs write permissions) |
Workflow examples
Basic CI check:
name: Spec Check
on:
jobs:
specsync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: CorvidLabs/spec-sync@v4
with:
strict: 'true'
require-coverage: '100'
With PR comments:
name: Spec Check
on:
pull_request:
types:
jobs:
specsync:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: CorvidLabs/spec-sync@v4
with:
strict: 'true'
comment: 'true'
When comment: 'true' is set, SpecSync posts (or updates) a PR comment showing spec drift — added/removed exports since the base branch. The comment is automatically updated on subsequent pushes.
Configuration
Create specsync.json or .specsync.toml in your project root (or run specsync init):
| Option | Type | Default | Description |
|---|---|---|---|
specsDir |
string |
"specs" |
Directory containing *.spec.md files |
sourceDirs |
string[] |
["src"] |
Source directories for coverage analysis |
schemaDir |
string? |
— | SQL schema dir for db_tables validation |
schemaPattern |
string? |
CREATE TABLE regex |
Custom regex for table name extraction |
requiredSections |
string[] |
7 defaults | Markdown sections every spec must include |
excludeDirs |
string[] |
["__tests__"] |
Directories excluded from coverage |
excludePatterns |
string[] |
Common test globs | File patterns excluded from coverage |
sourceExtensions |
string[] |
All supported | Restrict to specific extensions (e.g., ["ts", "rs"]) |
aiCommand |
string? |
— | Custom command for AI generation (reads stdin prompt, writes stdout markdown) |
aiProvider |
string? |
— | AI provider (auto, claude, anthropic, openai, ollama, copilot, etc.) |
aiTimeout |
number? |
120 |
Seconds before AI command times out per module |
TOML alternative
# .specsync.toml
= "specs"
= ["src", "lib"]
= ["Purpose", "Public API", "Invariants", "Behavioral Examples", "Error Cases", "Dependencies", "Change Log"]
= 120
Config resolution order: .specsync/config.toml → .specsync/config.json → .specsync.toml → specsync.json → defaults with auto-detected source dirs.
Per-developer AI config
On multi-agent teams, each contributor may use a different AI provider. To avoid conflicts in the shared config, create a local override file:
# .specsync/config.local.toml (gitignored automatically)
= "ollama"
= "llama3"
This file is merged on top of the shared config and only supports ai_* keys. Alternatively, set the SPECSYNC_AI_COMMAND env var or pass --provider on the CLI.
Lifecycle Guards
Configure transition guards in specsync.json to enforce quality gates before specs can be promoted:
| Guard Option | Type | Description |
|---|---|---|
minScore |
number? |
Minimum spec quality score (0-100) required |
requireSections |
string[] |
Sections that must exist with non-empty content |
noStale |
bool? |
Spec must not be stale (source files ahead of spec) |
staleThreshold |
number? |
Max commits behind when noStale is true (default: 5) |
message |
string? |
Custom message shown when guard blocks transition |
Guard keys use "from→to" format (e.g., "review→active") or "*→to" for wildcard. ASCII arrows (->) also work.
When trackHistory is enabled (default: true), every status transition is recorded in the spec's frontmatter:
lifecycle_log:
- "2026-04-11: draft → review"
- "2026-04-12: review → active"
Use specsync lifecycle guard <spec> to dry-run guard evaluation without making changes.
Auto-Promote & CI Enforcement
Auto-promote scans all specs and promotes any whose next transition passes all configured guards:
Enforce validates lifecycle rules for CI pipelines (exits non-zero on violations):
Configure enforcement rules in specsync.json:
| Config Key | Type | Description |
|---|---|---|
maxAge |
object |
Maximum days a spec may stay in each status (e.g., "draft": 30) |
allowedStatuses |
string[] |
Restrict specs to these statuses only |
GitHub Action — add lifecycle-enforce: 'true' to the spec-sync action to enforce lifecycle rules in CI:
- uses: CorvidLabs/spec-sync@v4
with:
lifecycle-enforce: 'true'
Spec Generation
specsync generate scans your source directories, finds modules without spec files, and scaffolds *.spec.md files for each one.
Template mode (default)
Uses your custom template (specs/_template.spec.md) or the built-in default. Generates frontmatter + stubbed sections with TODOs.
AI mode (--provider)
Reads your source code, sends it to an LLM, and generates specs with real content — Purpose, Public API tables, Invariants, Error Cases, all filled in from the code. No manual filling required.
The AI command is resolved in order:
"aiCommand"inspecsync.jsonSPECSYNC_AI_COMMANDenvironment variableclaude -p --output-format text(default, requires Claude CLI)
Any command that reads a prompt from stdin and writes markdown to stdout works:
Set "aiTimeout" in specsync.json to control per-module timeout (default: 120 seconds).
Designed for AI agents
The generate command is the entry point for LLM-powered spec workflows:
# LLM fixes errors from JSON output # iterate until clean
Every output format is designed for machine consumption:
--jsonon any command → structured JSON, no ANSI codes- Exit code 0/1 → pass/fail, no parsing needed
- Spec files are plain markdown → any LLM can read and write them
- Public API tables use backtick-quoted names → unambiguous to extract
JSON output shapes
// specsync check --json
// specsync coverage --json
// specsync diff HEAD~3 --json
Auto-Fix & Diff
--fix: Keep specs in sync automatically
When --fix is used, any export found in code but missing from the spec gets appended as a stub row (| \name` | | | TODO |) to the Public API table. If no ## Public API` section exists, one is created. Already-documented exports are never duplicated.
This turns spec maintenance from manual table editing into a review-and-refine workflow — run --fix, then fill in the descriptions.
diff: Track API changes across commits
Shows exports added and removed per spec file since the given git ref. Useful for code review, release notes, and CI drift detection.
Architecture
src/
├── main.rs CLI entry + output formatting
├── ai.rs AI-powered spec generation (prompt builder + command runner)
├── archive.rs Task archival from companion tasks.md files
├── changelog.rs Changelog generation between git refs
├── comment.rs PR comment generation with spec links
├── compact.rs Changelog entry compaction in spec files
├── config.rs specsync.json / .specsync.toml loading
├── deps.rs Cross-module dependency graph validation
├── generator.rs Spec + companion file scaffolding
├── github.rs GitHub API integration (issues, PRs)
├── hash_cache.rs Incremental validation via content hashing
├── hooks.rs Agent instruction + git hook management
├── importer.rs External importers (GitHub Issues, Jira, Confluence)
├── lifecycle.rs Spec status transitions (promote, demote, set, status, history, guard, auto-promote, enforce)
├── manifest.rs Package manifest parsing (Cargo.toml, package.json, etc.)
├── mcp.rs MCP server for AI agent integration (JSON-RPC stdio)
├── merge.rs Auto-resolve merge conflicts in spec files
├── new.rs Quick-create minimal spec with source auto-detection
├── parser.rs Frontmatter + spec body parsing
├── rules.rs Display configured validation rules
├── registry.rs Registry loading, generation, and remote fetching
├── schema.rs SQL schema parsing for column validation
├── scoring.rs Spec quality scoring (0–100, weighted rubric)
├── stale.rs Staleness detection (spec vs source modification)
├── types.rs Data types + config schema
├── validator.rs Validation + coverage + cross-project ref detection
├── view.rs Role-filtered spec viewing (dev, qa, product, agent)
├── watch.rs File watcher (notify, 500ms debounce)
└── exports/
├── mod.rs Language dispatch
├── typescript.rs TS/JS exports
├── rust_lang.rs Rust pub items
├── go.rs Go uppercase identifiers
├── python.rs Python __all__ / top-level
├── swift.rs Swift public/open items
├── kotlin.rs Kotlin top-level
├── java.rs Java public items
├── csharp.rs C# public items
├── dart.rs Dart public items
├── php.rs PHP public items
└── ruby.rs Ruby public items
Design: Single binary, no runtime deps. Frontmatter parsed with regex (no YAML library). Language backends use regex, not ASTs — works without compilers installed. Release builds use LTO + strip + opt-level 3.
Contributing
- Fork, branch (
git checkout -b feat/my-feature), implement cargo test+cargo clippy- Open a PR
Adding a language
- Create
src/exports/yourlang.rs— returnVec<String>of exported names - Add variant to
Languageenum intypes.rs - Wire extension detection + dispatch in
src/exports/mod.rs - Add tests for common patterns