SpecSync
Bidirectional spec-to-code validation. Written in Rust. Single binary. 9 languages.
Quick Start • Spec Format • CLI • 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 |
| 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 |
.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 |
Install
GitHub Action (recommended)
- uses: CorvidLabs/spec-sync@v1
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
---
Required Sections
Every spec must include these ## sections (configurable in specsync.json):
Purpose, Public API, Invariants, Behavioral Examples, Error Cases, Dependencies, Change Log
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) |
coverage |
File and module coverage report |
generate |
Scaffold specs for modules missing one |
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) |
--json |
Structured JSON output |
Exit Codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
Errors, strict warnings, or coverage below threshold |
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 |
Workflow example
name: Spec Check
on:
jobs:
specsync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: CorvidLabs/spec-sync@v1
with:
strict: 'true'
require-coverage: '100'
Configuration
Create specsync.json 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"]) |
For AI Agents
--jsonoutputs structured results, no color codes to strip- Exit code 1 = needs fixing; 0 = all clear
specsync generatebootstraps specs for existing codebases- Spec files are plain markdown — any LLM can read and write them
- Public API tables use backtick-quoted names, unambiguous to parse
JSON shapes
// specsync check --json
// specsync coverage --json
Architecture
src/
├── main.rs CLI entry + output formatting
├── types.rs Data types + config schema
├── config.rs specsync.json loading
├── parser.rs Frontmatter + spec body parsing
├── validator.rs Validation + coverage computation
├── generator.rs Spec scaffolding
├── 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
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