Bidirectional Spec Synchronization
Automatic synchronization between SpecWeave specs and external tools (GitHub, JIRA, Azure DevOps).
Overview
SpecWeave implements bidirectional sync with different sync directions for different types of data:
| Data Type | Sync Direction | Source of Truth | What Syncs |
|---|---|---|---|
| CONTENT | SpecWeave → External Tool | .specweave/docs/internal/specs/ | Title, Description, User Stories, Acceptance Criteria |
| STATUS | External Tool → SpecWeave | GitHub/JIRA/ADO | State (Open/Closed), Progress, Assignees |
| COMMITS | SpecWeave → External Tool | Git history | Commit links, PR links, implementation updates |
Key Principle: Specs are the permanent source of truth for WHAT we're building. External tools manage HOW we track it.
Architecture: Why Bidirectional?
The Problem
Traditional approaches force you to choose:
- Option A: External tool (JIRA) is source of truth → Developers must copy changes to docs manually
- Option B: Docs (SpecWeave) is source of truth → PMs must copy changes to JIRA manually
Both approaches fail! One system becomes stale.
The Solution: Bidirectional Sync
SpecWeave syncs different data types in different directions:
┌─────────────────────────────────────────────────────────────┐
│ SpecWeave Specs (.specweave/docs/internal/specs/) │
│ Source of Truth: CONTENT (title, description, user stories)│
└─────────────────┬───────────────────────────────────────────┘
│
│ CONTENT SYNC (SpecWeave → External)
│ - Create new work items
│ - Update title/description when spec changes
│ - Sync user stories and acceptance criteria
│
▼
┌─────────────────────────────────────────────────────────────┐
│ External Tools (GitHub Issues, JIRA Epics, ADO Features) │
│ Source of Truth: STATUS (state, progress, assignments) │
└─────────────────┬───────────────────────────────────────────┘
│
│ STATUS SYNC (External → SpecWeave)
│ - Read issue state (Open/Closed)
│ - Read progress percentage
│ - Read assignees
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SpecWeave Implementation (.specweave/increments/) │
│ Source of Truth: IMPLEMENTATION (code, commits, PRs) │
└─────────────────┬───────────────────────────────────────────┘
│
│ COMMIT SYNC (SpecWeave → External)
│ - Post commit links to work items
│ - Post PR links when merged
│ - Update progress comments
│
▼
┌─────────────────────────────────────────────────────────────┐
│ External Tools - Comments Section │
└─────────────────────────────────────────────────────────────┘
Result: Best of both worlds!
- ✅ Specs stay complete and up-to-date (SpecWeave manages content)
- ✅ PMs/stakeholders track progress in their preferred tool (GitHub/JIRA/ADO manages status)
- ✅ Implementation history linked to work items (commit comments)
Content Sync (SpecWeave → External Tool)
When It Runs
Automatic (via hooks):
- After creating new increment with
/specweave:increment - When spec.md is created in
.specweave/docs/internal/specs/
Manual (CLI):
- When spec content is manually updated
- Run:
node dist/cli/commands/sync-spec-content.js --spec <path> --provider github|jira|ado
What It Does
Creates new work items if spec doesn't have external link:
# User creates spec
vim .specweave/docs/internal/specs/spec-001-user-auth.md
# Hook runs automatically
# → Creates GitHub issue #456
# → Adds link to spec.md: **GitHub Project**: https://github.com/org/repo/issues/456
Updates existing work items when spec content changes:
# User updates spec (adds user story)
vim .specweave/docs/internal/specs/spec-001-user-auth.md
# Hook detects change (title, description, or user story count)
# → Updates GitHub issue #456 with new content
# → Does NOT change issue state (Open/Closed)
What It Syncs
Title:
# Spec # GitHub Issue
title: "User Authentication" → Title: "[SPEC-001] User Authentication"
Description:
# Spec # GitHub Issue Body
Add user authentication → ## Summary
system with OAuth support. Add user authentication system with OAuth support.
User Stories:
# Spec # GitHub Issue Body
**US-001**: Basic Login → ## User Stories
**Acceptance Criteria**: ### US-001: Basic Login
- AC-US1-01: Login with **Acceptance Criteria:**
email/password - [ ] AC-US1-01: Login with email/password
What It Does NOT Sync
❌ State/Status:
- GitHub: Open/Closed
- JIRA: To Do/In Progress/Done
- ADO: New/Active/Resolved/Closed
❌ Progress Percentage (managed by task completion)
❌ Assignees (managed in external tool)
❌ Labels/Tags (except initial specweave label)
Status Sync (External Tool → SpecWeave)
When It Runs
Automatic (via hooks):
- After task completion (post-task-completion hook)
- When syncing increment progress
Manual (CLI):
- Run:
/specweave-github:sync <increment-id>
What It Does
Reads status from external tool:
# GitHub issue #456 is "Closed"
# SpecWeave reads state and marks increment as complete
Does NOT update external tool state:
# ❌ WRONG: Completing tasks in SpecWeave does NOT close GitHub issue
# ✅ CORRECT: Close issue in GitHub → SpecWeave reads closure
Why This Direction?
Rationale: External tools (GitHub/JIRA/ADO) are the workflow management systems. They handle:
- Kanban boards
- Sprint planning
- Stakeholder visibility
- Team collaboration
SpecWeave focuses on specification and implementation, not workflow management.
Commit Sync (SpecWeave → External Tool)
When It Runs
Automatic (via hooks):
- After task completion (detects new commits)
- When increment is marked complete
What It Does
Posts commit links to work items:
## GitHub Issue Comment
### User Story: US-001 (Login)
**Commits**:
- [abc123f](https://github.com/org/repo/commit/abc123f): Add AuthService with JWT
- [def456g](https://github.com/org/repo/commit/def456g): Add login API endpoint
**PR**: [#789](https://github.com/org/repo/pull/789): Implement user authentication
---
🤖 Auto-posted by SpecWeave
Links user stories to commits (via AC-IDs):
# Task references AC-US1-01, AC-US1-02
# Commits for this task → posted under "US-001" section in comment
Configuration
Enable/Disable Sync
.specweave/config.json:
{
"sync": {
"enabled": true,
"settings": {
"syncSpecContent": true, // Content sync (SpecWeave → External)
"syncCommitLinks": true, // Commit sync (SpecWeave → External)
"syncDirection": "bidirectional" // Read status from external
},
"activeProfile": "github-default",
"profiles": {
"github-default": {
"provider": "github",
"config": {
"owner": "myorg",
"repo": "myrepo"
}
}
}
}
}
Per-Provider Settings
GitHub:
{
"sync": {
"profiles": {
"github-main": {
"provider": "github",
"config": {
"owner": "myorg",
"repo": "main-app"
}
}
}
}
}
JIRA:
{
"sync": {
"profiles": {
"jira-dev": {
"provider": "jira",
"config": {
"domain": "mycompany.atlassian.net",
"projectKey": "PROJ"
}
}
}
}
}
Environment Variables (JIRA):
export JIRA_EMAIL="user@company.com"
export JIRA_API_TOKEN="your-api-token"
Azure DevOps:
{
"sync": {
"profiles": {
"ado-backend": {
"provider": "ado",
"config": {
"organization": "myorg",
"project": "backend-services"
}
}
}
}
}
Environment Variables (ADO):
export AZURE_DEVOPS_PAT="your-personal-access-token"
Workflow Examples
Example 1: Create New Spec → Auto-Create Work Item
# 1. PM creates spec
/specweave:increment "User authentication with OAuth"
# PM agent generates:
# - .specweave/docs/internal/specs/spec-001-user-auth.md
# - .specweave/increments/0001-user-auth/spec.md (subset)
# 2. post-increment-planning hook fires
# → Detects spec-001-user-auth.md is new
# → Creates GitHub issue #456
# → Adds link to spec-001-user-auth.md
# 3. Developer starts work
/specweave:do
# 4. As tasks complete, commits are posted to issue #456
# → Comment: "US-001 implemented in commits abc123f, def456g"
Example 2: Update Spec → Auto-Update Work Item
# 1. PM updates spec (adds new user story)
vim .specweave/docs/internal/specs/spec-001-user-auth.md
# Add:
# **US-004**: Password Reset
# **Acceptance Criteria**:
# - AC-US4-01: User can request password reset link
# 2. Manual sync (automatic in future)
node dist/cli/commands/sync-spec-content.js \
--spec .specweave/docs/internal/specs/spec-001-user-auth.md \
--provider github
# Output:
# 🔄 Syncing spec content to github...
# Spec: spec-001-user-auth.md
#
# 🔄 Checking for changes in issue #456
# 📝 Changes detected:
# - user stories: 3 → 4
#
# ✅ Updated issue #456
# URL: https://github.com/myorg/myrepo/issues/456
# 3. GitHub issue #456 now shows US-004
Example 3: Close Work Item → Read Status
# 1. PM closes GitHub issue #456 (all work complete)
# 2. Developer syncs status
/specweave-github:sync 0001
# Output:
# 🔄 Syncing increment 0001-user-auth with GitHub...
# ✅ Increment marked complete (issue #456 closed)
# 3. SpecWeave marks increment as complete
Best Practices
DO ✅
- Keep specs up-to-date: Treat
.specweave/docs/internal/specs/as source of truth for requirements - Let external tools manage workflow: Close issues in GitHub/JIRA/ADO, not SpecWeave
- Review sync results: Check comments in external tools after task completion
- Use AC-IDs for traceability: Link tasks to user stories via AC-US{story}-{number}
DON'T ❌
- Don't manually update specs to match external tools: Content flows SpecWeave → External
- Don't manually close issues in SpecWeave: Status flows External → SpecWeave
- Don't skip sync hooks: Hooks ensure specs and external tools stay in sync
- Don't duplicate specs: One spec in
.specweave/docs/internal/specs/, referenced by increments
Troubleshooting
"Spec not syncing to GitHub"
Check:
- Sync enabled in config:
config.sync.enabled = true - Spec content sync enabled:
config.sync.settings.syncSpecContent = true - Active profile configured:
config.sync.activeProfileset - Provider credentials valid (GitHub token, JIRA API token, ADO PAT)
Manual sync:
node dist/cli/commands/sync-spec-content.js \
--spec .specweave/docs/internal/specs/spec-001.md \
--provider github \
--verbose
"Changes not detected"
Possible causes:
- Spec content identical to external tool (no changes)
- Whitespace differences (normalized during comparison)
- User story count unchanged
Force update:
# Manually update spec to trigger change detection
# Add a comment or minor edit, then sync
"Multiple work items created"
Cause: Hook ran multiple times or manual creation
Fix:
# Check metadata.json for external link
cat .specweave/increments/0001-user-auth/metadata.json
# Should show:
# {
# "github": {
# "issue": 456,
# "url": "https://github.com/org/repo/issues/456"
# }
# }
# If duplicate, close extra issue manually in GitHub
Technical Implementation
Change Detection Algorithm
function detectContentChanges(localSpec, externalContent) {
const changes = [];
// 1. Title change (exact match)
if (localSpec.title !== externalContent.title) {
changes.push(`title: "${externalContent.title}" → "${localSpec.title}"`);
}
// 2. Description change (normalized whitespace)
const normalize = (str) => str.replace(/\s+/g, ' ').trim();
if (normalize(localSpec.description) !== normalize(externalContent.description)) {
changes.push('description updated');
}
// 3. User story count change
if (localSpec.userStories.length !== externalContent.userStoryCount) {
changes.push(`user stories: ${externalContent.userStoryCount} → ${localSpec.userStories.length}`);
}
return {
hasChanges: changes.length > 0,
changes
};
}
Provider-Specific Formatting
GitHub (Markdown):
## User Stories
### US-001: Basic Login
**Acceptance Criteria:**
- [ ] AC-US1-01: User can log in with email/password
- [ ] AC-US1-02: Invalid credentials show error
JIRA (JIRA Markup):
h2. User Stories
h3. US-001: Basic Login
*Acceptance Criteria:*
* (x) AC-US1-01: User can log in with email/password
* (/) AC-US1-02: Invalid credentials show error
Azure DevOps (HTML):
<h2>User Stories</h2>
<h3>US-001: Basic Login</h3>
<p><strong>Acceptance Criteria:</strong></p>
<ul>
<li>☐ AC-US1-01: User can log in with email/password</li>
<li>☑ AC-US1-02: Invalid credentials show error</li>
</ul>
Summary
Bidirectional sync = Different sync directions for different data types:
- CONTENT (Title, Description, User Stories): SpecWeave → External Tool
- STATUS (Open/Closed, Progress): External Tool → SpecWeave
- COMMITS (Links, PRs): SpecWeave → External Tool
Result: Specs stay complete, external tools manage workflow, implementation is traceable.
For more details: See Spec Commit Sync Guide