Bidirectional spec-to-code validation with cross-project references, dependency graphs, and AI-powered generation. Written in Rust. Single binary. 12 languages. VS Code extension.
5-Minute Start β’ Spec Format β’ CLI β’ VS Code Extension β’ Cross-Project Refs β’ GitHub Action β’ Config β’ Docs Site
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 |
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 |
| YAML | Top-level mapping keys (anchors, aliases supported) | β |
- uses: CorvidLabs/spec-sync@v4
with:
strict: 'true'
require-coverage: '100'cargo install specsyncDownload from GitHub Releases:
# macOS (Apple Silicon)
curl -sL https://github.com/CorvidLabs/spec-sync/releases/latest/download/specsync-macos-aarch64.tar.gz | tar xz
sudo mv specsync-macos-aarch64 /usr/local/bin/specsync
# macOS (Intel)
curl -sL https://github.com/CorvidLabs/spec-sync/releases/latest/download/specsync-macos-x86_64.tar.gz | tar xz
sudo mv specsync-macos-x86_64 /usr/local/bin/specsync
# Linux (x86_64)
curl -sL https://github.com/CorvidLabs/spec-sync/releases/latest/download/specsync-linux-x86_64.tar.gz | tar xz
sudo mv specsync-linux-x86_64 /usr/local/bin/specsync
# Linux (aarch64)
curl -sL https://github.com/CorvidLabs/spec-sync/releases/latest/download/specsync-linux-aarch64.tar.gz | tar xz
sudo mv specsync-linux-aarch64 /usr/local/bin/specsyncWindows: download specsync-windows-x86_64.exe.zip from the releases page.
cargo install --git https://github.com/CorvidLabs/spec-syncZero to a green spec check, on a fresh project. Copy-paste the whole block.
# 1. Install (one of the install methods above β we'll assume `cargo install specsync`)
cargo install specsync
# 2. Make a new project + a single Rust file
mkdir hello-spec && cd hello-spec
cargo init --lib --quiet
cat > src/lib.rs <<'EOF'
//! A trivial greeter.
/// Returns a greeting for `name`.
pub fn greet(name: &str) -> String {
format!("hello, {name}!")
}
EOF
# 3. Initialize SpecSync (creates .specsync/config.toml + a registry)
specsync init
# 4. Scaffold a spec for the module. SpecSync auto-detects `src/lib.rs`
# and generates a stub spec for the `greet` function it found.
specsync new greeter
# 5. Run the validator. The new spec has all required sections + matches code.
specsync checkExpected output:
β greeter (v1, draft) β 1 source file, 7/7 sections
1 spec checked, 0 errors, 0 warnings
That's it. The spec is the contract; if you remove pub fn greet from the source, specsync check will fail. If you add a new public function and don't list it in the spec, you get a warning. Drift gets caught at CI time, not in code review.
Next steps:
- Add a
CorvidLabs/spec-sync@v4GitHub Action to your CI β it runsspecsync checkand posts results to PRs. - Use
specsync wizardfor an interactive spec-creation experience instead ofnew. - Use
specsync generate --provider autoto AI-generate richer specs from existing code. - See
examples/quickstart/for the same flow as a committed reference.
For the full list of commands, see CLI Reference below.
All commands at a glance. Run specsync <command> --help for details.
specsync migrate # Upgrade from 3.x to 4.0.0 (.specsync/ layout)
specsync migrate --dry-run # Preview migration without changes
specsync init # Create specsync.json config
specsync check # Validate specs against code
specsync check --fix # Auto-add undocumented exports as stubs
specsync diff # Show exports added/removed since HEAD
specsync diff HEAD~5 # Compare against a specific ref
specsync coverage # Show file/module coverage
specsync report # Per-module coverage with stale detection
specsync generate # Scaffold specs for unspecced modules
specsync generate --provider auto # AI-powered specs (auto-detect provider)
specsync scaffold auth # Scaffold spec + auto-detect source files
specsync add-spec auth # Add a single spec + companion files
specsync deps # Validate cross-module dependency graph
specsync changelog v3.3.0..v3.4.0 # Generate changelog between git refs
specsync comment --pr 42 # Post spec check summary as PR comment
specsync import github 123 # Import spec from GitHub issue
specsync wizard # Interactive guided spec creation
specsync init-registry # Generate specsync-registry.toml
specsync resolve # Verify spec cross-references
specsync resolve --remote # Verify cross-project refs via GitHub
specsync score # Quality-score your spec files (0β100)
specsync compact --keep 10 # Compact old changelog entries in specs
specsync archive-tasks # Archive completed tasks from tasks.md
specsync view --role dev # View specs filtered by role
specsync merge # Auto-resolve merge conflicts in specs
specsync new auth # Quick-create a minimal spec (auto-detects sources)
specsync stale # Find specs that haven't kept up with code changes
specsync rules # Show configured validation rules
specsync lifecycle status # Show lifecycle status of all specs
specsync lifecycle promote auth # Advance auth spec to next status
specsync lifecycle history auth # View transition history for a spec
specsync lifecycle guard auth # Dry-run: check if guards would pass
specsync lifecycle auto-promote # Promote all specs that pass guards
specsync lifecycle enforce --all # CI: validate lifecycle rules
specsync hooks install # Install agent instructions + git hooks
specsync hooks status # Check what's installed
specsync mcp # Start MCP server for AI agent integration
specsync watch # Re-validate on every file changeSpecs are markdown files (*.spec.md) with YAML frontmatter in your specs directory.
---
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)
---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.
SpecSync extracts the first backtick-quoted name per row and cross-references it against code exports:
## Public API
| Function | Parameters | Returns | Description |
|----------|-----------|---------|-------------|
| `authenticate` | `(token: string)` | `User \| null` | Validates bearer token |
| `refreshSession` | `(sessionId: string)` | `Session` | Extends session TTL |Full spec example
---
module: auth
version: 3
status: stable
files:
- src/auth/service.ts
- src/auth/middleware.ts
db_tables:
- users
- sessions
depends_on:
- specs/database/database.spec.md
---
# Auth
## Purpose
Handles authentication and session management. Validates bearer tokens,
manages session lifecycle, provides middleware for route protection.
## Public API
### Exported Functions
| Function | Parameters | Returns | Description |
|----------|-----------|---------|-------------|
| `authenticate` | `(token: string)` | `User \| null` | Validates a token |
| `refreshSession` | `(sessionId: string)` | `Session` | Extends session TTL |
### Exported Types
| Type | Description |
|------|-------------|
| `User` | Authenticated user object |
| `Session` | Active session record |
## Invariants
1. Sessions expire after 24 hours
2. Failed auth attempts rate-limited to 5/minute
3. Tokens validated cryptographically, never by string comparison
## Behavioral Examples
### Scenario: Valid token
- **Given** a valid JWT token
- **When** `authenticate()` is called
- **Then** returns the corresponding User object
### Scenario: Expired token
- **Given** an expired JWT token
- **When** `authenticate()` is called
- **Then** returns null and logs a warning
## Error Cases
| Condition | Behavior |
|-----------|----------|
| Expired token | Returns null, logs warning |
| Malformed token | Returns null |
| DB unavailable | Throws `ServiceUnavailableError` |
## Dependencies
| Module | Usage |
|--------|-------|
| database | `query()` for user lookups |
| crypto | `verifyJwt()` for token validation |
## Change Log
| Date | Change |
|------|--------|
| 2026-03-18 | Initial spec |specsync [command] [flags]
| 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, testing.md, and design.md if enabled) |
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) |
| 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 |
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
Errors, strict warnings, or coverage below threshold |
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 referenceEach project publishes a specsync-registry.toml at its root to declare available spec modules:
[registry]
name = "myapp"
[specs]
auth = "specs/auth/auth.spec.md"
messaging = "specs/messaging/messaging.spec.md"
database = "specs/db/database.spec.md"Generate one automatically from existing specs:
specsync init-registry # Uses project folder name
specsync init-registry --name myapp # Custom registry namespecsync resolve # Verify local refs exist
specsync resolve --remote # Also verify cross-project refs via GitHubRemote 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.
When you run specsync generate or specsync add-spec, four companion files are created alongside each spec, plus one opt-in:
| 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 |
testing.md |
QA/Dev | No | Test strategy, edge cases, manual checklists |
design.md (opt-in) |
Design/Frontend | No | Layout, component hierarchy, design tokens |
design.md is opt-in β enable it in .specsync/config.toml:
[companions]
design = trueAll 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
---
## User Stories
- As a [role], I want [feature] so that [benefit]
## Acceptance Criteria
- [ ] <!-- TODO: define acceptance criteria -->
## Constraints
<!-- Non-functional requirements, performance targets, compliance needs -->
## Out of Scope
<!-- Explicitly excluded from this module's requirements -->tasks.md β Multi-role checkpoint tracking:
---
spec: auth.spec.md
---
## Tasks
- [ ] <!-- Implementation checklist -->
## Gaps
<!-- Uncovered areas, missing edge cases -->
## Review Sign-offs
- **Product**: pending
- **QA**: pending
- **Design**: n/a
- **Dev**: pendingcontext.md β Agent briefing document:
---
spec: auth.spec.md
---
## Key Decisions
<!-- Architectural or design decisions -->
## Files to Read First
<!-- Most important files for understanding this module -->
## Current Status
<!-- What's done, in progress, blocked -->
## Notes
<!-- Free-form notes, links, context -->testing.md β Test strategy and QA checklist:
---
spec: auth.spec.md
---
## Automated Testing
<!-- Test file locations and what they cover -->
## Manual QA Checklist
- [ ] <!-- Manual verification steps -->
## Edge Cases
<!-- Boundary conditions, race conditions, unusual inputs -->design.md (opt-in) β Design and layout documentation:
---
spec: auth.spec.md
---
## Layout
<!-- Page/component layout, responsive breakpoints, positioning -->
## Components
<!-- Component tree, props, slots -->
## Tokens
<!-- Design token overrides from global design system -->
## Assets
<!-- Icons, images, illustrations needed -->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.
SpecSync extracts symbols from YAML files (.yml, .yaml) including GitHub Actions workflows, Docker Compose files, and Kubernetes manifests.
What gets extracted:
- Top-level keys β any key at column 0 (e.g.,
name,on,jobs) - Nested children of well-known parents β
jobs.test,services.web,volumes.pgdata, etc. Parents:jobs,services,volumes,networks,secrets,stages,steps,targets,outputs,inputs,permissions,deployments - YAML anchors β
&anchor-namedefinitions
Indentation: Any consistent indentation (2-space, 4-space, or tabs) is supported for nested key extraction.
YAML supports multi-document files separated by --- (common in Kubernetes manifests). SpecSync handles these correctly:
# Multi-document YAML (e.g., k8s manifests)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
---
apiVersion: v1
kind: Service
metadata:
name: my-svc- Top-level keys from all documents are extracted (
apiVersion,kind,metadatafrom both) - The
---separator is not confused with spec frontmatter delimiters β frontmatter parsing only applies to.spec.mdfiles - Duplicate keys across documents are deduplicated in the symbol list
Note: SpecSync's YAML extractor uses regex-based parsing, not a full YAML parser. This means it works without any YAML library dependency but does not interpret YAML semantics like merge keys (
<<: *alias) beyond anchor extraction.
Install from the VS Code Marketplace or search "SpecSync" in the Extensions panel.
code --install-extension corvidlabs.specsync| 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 |
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
| 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).
Available on the GitHub Marketplace. Auto-detects OS/arch, downloads the binary, runs validation.
| 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) |
Basic CI check:
name: Spec Check
on: [push, pull_request]
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: [opened, synchronize]
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.
Create specsync.json or .specsync.toml in your project root (or run specsync init):
{
"specsDir": "specs",
"sourceDirs": ["src"],
"schemaDir": "db/migrations",
"requiredSections": ["Purpose", "Public API", "Invariants", "Behavioral Examples", "Error Cases", "Dependencies", "Change Log"],
"excludeDirs": ["__tests__"],
"excludePatterns": ["**/__tests__/**", "**/*.test.ts", "**/*.spec.ts"],
"sourceExtensions": [],
"aiTimeout": 120
}| 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 |
# .specsync.toml
specs_dir = "specs"
source_dirs = ["src", "lib"]
required_sections = ["Purpose", "Public API", "Invariants", "Behavioral Examples", "Error Cases", "Dependencies", "Change Log"]
ai_timeout = 120Config resolution order: .specsync/config.toml β .specsync/config.json β .specsync.toml β specsync.json β defaults with auto-detected source dirs.
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)
ai_provider = "ollama"
ai_model = "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.
Configure transition guards in specsync.json to enforce quality gates before specs can be promoted:
{
"lifecycle": {
"trackHistory": true,
"guards": {
"reviewβactive": {
"minScore": 70,
"requireSections": ["Public API", "Invariants"]
},
"activeβstable": {
"minScore": 85,
"noStale": true,
"requireSections": ["Public API", "Behavioral Examples", "Error Cases"]
},
"*βstable": {
"minScore": 85,
"message": "Stable specs require high quality scores"
}
}
}
}| 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 scans all specs and promotes any whose next transition passes all configured guards:
specsync lifecycle auto-promote # promote eligible specs
specsync lifecycle auto-promote --dry-run # preview without modifyingEnforce validates lifecycle rules for CI pipelines (exits non-zero on violations):
specsync lifecycle enforce --all # run all checks
specsync lifecycle enforce --require-status # every spec needs a status field
specsync lifecycle enforce --max-age # flag stale statuses
specsync lifecycle enforce --allowed # check allowed statusesConfigure enforcement rules in specsync.json:
{
"lifecycle": {
"maxAge": {
"draft": 30,
"review": 14
},
"allowedStatuses": ["draft", "review", "active", "stable"]
}
}| 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'specsync generate scans your source directories, finds modules without spec files, and scaffolds *.spec.md files for each one.
specsync generate # Scaffold template specs for all unspecced modules
specsync generate --provider auto # Use AI to generate filled-in specs from source code
specsync coverage # See what's still missingUses your custom template (specs/_template.spec.md) or the built-in default. Generates frontmatter + stubbed sections with TODOs.
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:
{ "aiCommand": "claude -p --output-format text" }
{ "aiCommand": "ollama run llama3" }Set "aiTimeout" in specsync.json to control per-module timeout (default: 120 seconds).
The generate command is the entry point for LLM-powered spec workflows:
specsync generate --provider auto # AI writes specs from source code
specsync check --fix # auto-add any missing exports as stubs
specsync check --json # validate, get structured feedback
# LLM fixes errors from JSON output # iterate until clean
specsync check --strict --require-coverage 100 # enforce full coverage in CIEvery 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
// specsync check --json
{ "passed": false, "errors": ["..."], "warnings": ["..."], "specs_checked": 12 }
// specsync coverage --json
{ "file_coverage": 85.33, "files_covered": 23, "files_total": 27, "loc_coverage": 79.12, "loc_covered": 4200, "loc_total": 5308, "modules": [...] }
// specsync diff HEAD~3 --json
{ "added": ["newFunction", "NewType"], "removed": ["oldHelper"], "spec": "specs/auth/auth.spec.md" }specsync check --fix # Add undocumented exports as stub rows
specsync check --fix --json # Same, with structured JSON outputWhen --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.
specsync diff # Changes since HEAD (staged + unstaged)
specsync diff HEAD~5 # Changes since 5 commits ago
specsync diff v2.1.0 # Changes since a tagShows exports added and removed per spec file since the given git ref. Useful for code review, release notes, and CI drift detection.
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
βββ yaml.rs YAML top-level keys
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.
- Fork, branch (
git checkout -b feat/my-feature), implement cargo test+cargo clippy- Open a PR
- 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
MIT Β© CorvidLabs