Documentation Index Fetch the complete documentation index at: https://docs.sudocode.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
sudocode uses a 3-layer storage architecture that combines the best of human-editable files, version control, and fast database queries. This “distributed git database” approach makes context durable, queryable, and fully version-controlled alongside your code.
Core Philosophy: Context is code. Specifications, issues, and relationships are stored as version-controlled artifacts that travel with your codebase through git.
Git-Native Storage Philosophy
Traditional issue trackers store context in centralized databases:
GitHub Issues - Hosted database, accessed via API
Jira - Cloud or self-hosted database
Linear - Centralized SaaS database
Problem: Context is separate from code, doesn’t travel with branches, hard to query offline.
sudocode’s approach: Context is stored in git itself:
Version controlled alongside code
Branches include their context
Works offline
Queryable locally
Distributed by default
Context-as-Code Principle
Just as you version control your code, sudocode version controls your development context:
your-project/
├── src/ # Your code
├── tests/ # Your tests
└── .sudocode/ # Your context (specs, issues, relationships)
├── specs/
│ ├── auth-system.md
│ └── specs.jsonl # Source of truth
├── issues/
│ ├── ISSUE-001.md
│ └── issues.jsonl # Source of truth
└── sudocode.db # Query cache (gitignored)
Benefits:
Context travels with code through branches
Full git history of decisions and changes
Merge conflicts are git merge conflicts
Works offline, no API required
Fast local queries via SQLite
The 3-Layer Architecture
sudocode uses three storage layers, each serving a specific purpose:
Layer 1: Markdown Human interface
Editable files
.md format
YAML frontmatter
Git-tracked
Layer 2: JSONL Source of truth
Structured data
Line-delimited JSON
Git-friendly diffs
Append-only log
Layer 3: SQLite Query cache
Fast lookups
Relational queries
Graph traversal
Gitignored, rebuilt
Why Three Layers?
Each layer optimizes for different needs:
Need Layer Why
Human editing Markdown Familiar format, readable diffs Git tracking JSONL Structured, line-based diffs Fast queries SQLite Relational queries, indexes Relationships SQLite Graph traversal, joins Offline work All layers Local-first design Distribution JSONL + Markdown Git push/pull
Layer 1: Markdown Files
Purpose
Markdown files provide a human-editable interface to specs and issues. Developers can edit context using their favorite text editor, just like editing code.
Location
.sudocode/
├── specs/
│ ├── authentication-system.md
│ ├── oauth-integration.md
│ └── api-design.md
└── issues/
├── ISSUE-001.md
├── ISSUE-002.md
└── ISSUE-003.md
Markdown files use YAML frontmatter for metadata plus markdown body for content:
Example spec file (.sudocode/specs/authentication-system.md):
---
id : SPEC-005
title : OAuth 2.0 Authentication System
priority : 0
status : approved
tags :
- auth
- oauth
- security
created_at : '2025-10-29T08:00:00Z'
updated_at : '2025-10-29T14:30:00Z'
---
# OAuth 2.0 Authentication System
## Overview
Implement OAuth 2.0 authentication to allow users to sign in
with Google and GitHub accounts.
## Requirements
- Support Google OAuth 2.0
- Support GitHub OAuth 2.0
- Secure token storage
- Token refresh capability
## Architecture
### Components
- OAuth middleware (Express)
- Token storage (Redis)
- Session management
...
Example issue file (.sudocode/issues/ISSUE-042.md):
---
id : ISSUE-042
title : Implement OAuth token exchange
status : in_progress
priority : 1
tags :
- auth
- oauth
created_at : '2025-10-29T10:00:00Z'
updated_at : '2025-10-29T15:00:00Z'
---
# Implement OAuth token exchange
Exchange authorization codes for access/refresh tokens.
## Tasks
- [ ] Implement token exchange endpoint
- [ ] Set up Redis token storage
- [ ] Encrypt tokens at rest
- [ ] Handle token expiration
Implements [[ SPEC-005 ]]
Frontmatter Fields
Specs:
id - Unique identifier (SPEC-001, SPEC-002, etc.)
title - Human-readable title
priority - 0-4 (0=highest)
status - draft, review, approved, deprecated
tags - Array of tags
created_at - ISO 8601 timestamp
updated_at - ISO 8601 timestamp
parent_id - Parent spec ID (optional)
Issues:
id - Unique identifier (ISSUE-001, ISSUE-002, etc.)
title - Human-readable title
status - open, in_progress, blocked, closed
priority - 0-4 (0=highest)
tags - Array of tags
assignee - Assigned user (optional)
created_at - ISO 8601 timestamp
updated_at - ISO 8601 timestamp
closed_at - ISO 8601 timestamp (if closed)
parent_id - Parent issue ID (optional)
When to Edit Markdown
Manual editing:
Creating new specs
Updating spec content
Refining requirements
Adding cross-references with [[SPEC-ID]] syntax
Auto-generated:
CLI operations sync back to markdown
sudocode sync --to-markdown regenerates from database
Layer 2: JSONL Files (Source of Truth)
Purpose
JSONL (JSON Lines) files are the source of truth for all sudocode data. These files are:
Git-tracked (committed to repository)
Line-delimited (one JSON object per line)
Merge-friendly (line-based diffs)
Complete (includes all metadata, relationships, tags)
Location
.sudocode/
├── specs/
│ └── specs.jsonl # All specs, one per line
└── issues/
└── issues.jsonl # All issues, one per line
Each line is a complete JSON object representing one entity:
Example specs.jsonl :
{ "id" : "SPEC-001" , "uuid" : "a1b2c3..." , "title" : "Authentication System" , "content" : "# Authentication System \n\n ..." , "priority" : 0 , "status" : "approved" , "archived" : 0 , "created_at" : "2025-10-29T08:00:00Z" , "updated_at" : "2025-10-29T09:00:00Z" , "parent_id" : null , "file_path" : "specs/authentication-system.md" , "relationships" :[], "tags" :[ "auth" , "security" ]}
{ "id" : "SPEC-002" , "uuid" : "d4e5f6..." , "title" : "OAuth Integration" , "content" : "# OAuth Integration \n\n ..." , "priority" : 1 , "status" : "draft" , "archived" : 0 , "created_at" : "2025-10-29T08:30:00Z" , "updated_at" : "2025-10-29T10:00:00Z" , "parent_id" : "SPEC-001" , "file_path" : "specs/oauth-integration.md" , "relationships" :[{ "from" : "SPEC-002" , "from_type" : "spec" , "to" : "SPEC-001" , "to_type" : "spec" , "type" : "references" }], "tags" :[ "auth" , "oauth" ]}
Example issues.jsonl :
{ "id" : "ISSUE-001" , "uuid" : "g7h8i9..." , "title" : "Configure OAuth providers" , "content" : "Set up OAuth providers..." , "status" : "closed" , "priority" : 0 , "assignee" : null , "archived" : 0 , "created_at" : "2025-10-29T10:00:00Z" , "updated_at" : "2025-10-29T14:00:00Z" , "closed_at" : "2025-10-29T14:00:00Z" , "parent_id" : null , "relationships" :[{ "from" : "ISSUE-001" , "from_type" : "issue" , "to" : "SPEC-005" , "to_type" : "spec" , "type" : "implements" }], "tags" :[ "auth" , "setup" ], "feedback" :[]}
{ "id" : "ISSUE-002" , "uuid" : "j0k1l2..." , "title" : "Implement token exchange" , "content" : "Exchange codes for tokens..." , "status" : "in_progress" , "priority" : 1 , "assignee" : "alice" , "archived" : 0 , "created_at" : "2025-10-29T10:30:00Z" , "updated_at" : "2025-10-29T15:00:00Z" , "closed_at" : null , "parent_id" : null , "relationships" :[{ "from" : "ISSUE-002" , "from_type" : "issue" , "to" : "SPEC-005" , "to_type" : "spec" , "type" : "implements" },{ "from" : "ISSUE-001" , "from_type" : "issue" , "to" : "ISSUE-002" , "to_type" : "issue" , "type" : "blocks" }], "tags" :[ "auth" , "tokens" ], "feedback" :[{ "id" : "FEEDBACK-001" , "issue_id" : "ISSUE-002" , "spec_id" : "SPEC-005" , "feedback_type" : "request" , "content" : "Token expiration not specified..." , "anchor" :{ "type" : "line" , "line" : 23 }, "dismissed" : 0 }]}
JSONL Structure
Spec JSONL object:
{
"id" : "SPEC-001" ,
"uuid" : "unique-uuid" ,
"title" : "Spec title" ,
"content" : "Full markdown content" ,
"priority" : 0 ,
"status" : "approved" ,
"archived" : 0 ,
"created_at" : "ISO timestamp" ,
"updated_at" : "ISO timestamp" ,
"parent_id" : null ,
"file_path" : "specs/filename.md" ,
"relationships" : [
{
"from" : "SPEC-001" ,
"from_type" : "spec" ,
"to" : "SPEC-002" ,
"to_type" : "spec" ,
"type" : "references"
}
],
"tags" : [ "tag1" , "tag2" ]
}
Issue JSONL object:
{
"id" : "ISSUE-001" ,
"uuid" : "unique-uuid" ,
"title" : "Issue title" ,
"content" : "Full content" ,
"status" : "open" ,
"priority" : 1 ,
"assignee" : null ,
"archived" : 0 ,
"created_at" : "ISO timestamp" ,
"updated_at" : "ISO timestamp" ,
"closed_at" : null ,
"parent_id" : null ,
"relationships" : [ ... ],
"tags" : [ "tag1" ],
"feedback" : [
{
"id" : "FEEDBACK-001" ,
"issue_id" : "ISSUE-001" ,
"spec_id" : "SPEC-005" ,
"feedback_type" : "request" ,
"content" : "Feedback content" ,
"anchor" : { "type" : "line" , "line" : 23 },
"dismissed" : 0
}
]
}
Why JSONL?
Git-friendly diffs:
# Adding a new spec is one line
+{"id":"SPEC-003","title":"New Spec",...}
# Updating a spec is one changed line
-{"id":"SPEC-001","title":"Old Title",...}
+{"id":"SPEC-001","title":"New Title",...}
Append-only log:
New entities are appended
Updates replace entire line
Git shows clear line-by-line changes
Complete data:
All relationships embedded
All tags embedded
All feedback embedded
Single file per entity type
When JSONL is Updated
Automatic export (debounced):
After any CLI command that modifies data
Debounce: 5 seconds (batches rapid changes)
Triggered by: sudocode issue create, sudocode spec update, etc.
Manual export:
sudocode export --output .sudocode/
After sync:
sudocode sync # Syncs markdown → database → JSONL
Layer 3: SQLite Database (Query Cache)
Purpose
SQLite provides fast queries and relational operations :
Complex filters (status, priority, tags)
Graph queries (find blockers, find ready work)
Relationship traversal
Full-text search
Aggregations and statistics
Location
.sudocode/
└── sudocode.db # Gitignored, rebuilt from JSONL
Never commit sudocode.db to git! It’s a cache that should be rebuilt locally from JSONL files. Add .sudocode/sudocode.db to your .gitignore.
Schema
The database uses the following tables:
Core entity tables:
CREATE TABLE specs (
id TEXT PRIMARY KEY ,
uuid TEXT NOT NULL UNIQUE ,
title TEXT NOT NULL ,
file_path TEXT NOT NULL ,
content TEXT NOT NULL ,
priority INTEGER NOT NULL DEFAULT 2 ,
archived INTEGER NOT NULL DEFAULT 0 ,
archived_at DATETIME ,
created_at DATETIME NOT NULL ,
updated_at DATETIME NOT NULL ,
parent_id TEXT ,
FOREIGN KEY (parent_id) REFERENCES specs(id)
);
CREATE TABLE issues (
id TEXT PRIMARY KEY ,
uuid TEXT NOT NULL UNIQUE ,
title TEXT NOT NULL ,
content TEXT NOT NULL ,
status TEXT NOT NULL DEFAULT 'open' ,
priority INTEGER NOT NULL DEFAULT 2 ,
assignee TEXT ,
archived INTEGER NOT NULL DEFAULT 0 ,
archived_at DATETIME ,
created_at DATETIME NOT NULL ,
updated_at DATETIME NOT NULL ,
closed_at DATETIME ,
parent_id TEXT ,
FOREIGN KEY (parent_id) REFERENCES issues(id)
);
Relationship table:
CREATE TABLE relationships (
from_id TEXT NOT NULL ,
from_type TEXT NOT NULL ,
to_id TEXT NOT NULL ,
to_type TEXT NOT NULL ,
relationship_type TEXT NOT NULL ,
created_at DATETIME NOT NULL ,
metadata TEXT ,
PRIMARY KEY (from_id, from_type, to_id, to_type, relationship_type)
);
Tags table:
CREATE TABLE tags (
entity_id TEXT NOT NULL ,
entity_type TEXT NOT NULL ,
tag TEXT NOT NULL ,
PRIMARY KEY (entity_id, entity_type, tag)
);
Feedback table:
CREATE TABLE feedback (
id TEXT PRIMARY KEY ,
issue_id TEXT NOT NULL ,
spec_id TEXT NOT NULL ,
feedback_type TEXT NOT NULL ,
content TEXT NOT NULL ,
agent TEXT ,
anchor TEXT ,
anchor_status TEXT NOT NULL DEFAULT 'valid' ,
original_anchor TEXT ,
dismissed INTEGER NOT NULL DEFAULT 0 ,
created_at DATETIME NOT NULL ,
updated_at DATETIME NOT NULL ,
FOREIGN KEY (issue_id) REFERENCES issues(id),
FOREIGN KEY (spec_id) REFERENCES specs(id)
);
Events table (audit log):
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_id TEXT NOT NULL ,
entity_type TEXT NOT NULL ,
event_type TEXT NOT NULL ,
actor TEXT NOT NULL ,
old_value TEXT ,
new_value TEXT ,
comment TEXT ,
created_at DATETIME NOT NULL ,
git_commit_sha TEXT ,
source TEXT
);
View: ready_issues (unblocked work):
CREATE VIEW ready_issues AS
SELECT i. *
FROM issues i
WHERE i . status = 'open'
AND i . archived = 0
AND NOT EXISTS (
SELECT 1 FROM relationships r
JOIN issues blocker ON r . to_id = blocker . id
WHERE r . from_id = i . id
AND r . from_type = 'issue'
AND r . to_type = 'issue'
AND r . relationship_type = 'blocks'
AND blocker . status IN ( 'open' , 'in_progress' , 'blocked' )
)
ORDER BY priority ASC , created_at DESC ;
View: blocked_issues (blocked work):
CREATE VIEW blocked_issues AS
SELECT i. * , blocker . id as blocker_id, blocker . title as blocker_title
FROM issues i
JOIN relationships r ON r . from_id = blocker . id
JOIN issues blocker ON r . to_id = blocker . id
WHERE r . relationship_type = 'blocks'
AND blocker . status IN ( 'open' , 'in_progress' , 'blocked' )
AND i . archived = 0
ORDER BY i . priority ASC ;
Why SQLite?
Fast local queries:
# Find all P0 open issues
sudocode issue list --priority 0 --status open
# Find issues ready to work on
sudocode ready
# Find what's blocking work
sudocode blocked
Relationship traversal:
# Find all issues implementing SPEC-005
sudocode issue list --implements SPEC-005
# Find all blockers for ISSUE-042
sudocode show ISSUE-042 # Shows incoming 'blocks' relationships
Full-text search:
# Search for "OAuth" in all issues
sudocode issue list --search OAuth
Performance:
Indexed lookups: O(log n)
Complex joins: Optimized by SQLite
View queries: Precomputed logic
Works offline: No API calls
When Database is Rebuilt
After git pull:
git pull
sudocode sync # Detects JSONL changes, imports to database
Manual import:
sudocode import --input .sudocode/
Fresh clone:
git clone your-repo
cd your-repo
sudocode sync # Builds database from JSONL
Data Flow
Bidirectional Sync
Data flows between layers depending on the operation:
┌─────────────┐
│ Markdown │ ← Human edits
│ .md files │
└──────┬──────┘
│
│ Parse frontmatter + content
↓
┌─────────────┐
│ SQLite │ ← CLI operations
│ sudocode.db │
└──────┬──────┘
│
│ Export (debounced)
↓
┌─────────────┐
│ JSONL │ ← Git tracked (source of truth)
│ *.jsonl │
└─────────────┘
Direction 1: Markdown → Database → JSONL
Triggered by: Manual markdown edits, sudocode sync --from-markdown
Parse markdown file
Read frontmatter (YAML)
Extract content (markdown body)
Parse cross-references [[SPEC-ID]]
Update database
Upsert spec/issue record
Create relationships from cross-references
Update tags
Update timestamps
Export to JSONL (debounced)
Wait 5 seconds for more changes
Query all entities from database
Write complete JSONL files
Include embedded relationships and tags
Git commit (manual)
Commit markdown + JSONL changes together
Context travels with code
Example:
# 1. Human edits markdown
vim .sudocode/specs/oauth-integration.md
# 2. Sync to database
sudocode sync --from-markdown
# Output:
# Syncing from markdown to database...
# ✓ updated spec SPEC-002
# Exported to JSONL
# 3. Commit changes
git add .sudocode/
git commit -m "Update OAuth spec with security requirements"
Direction 2: Database → JSONL → Markdown
Triggered by: CLI operations, sudocode sync --to-markdown
CLI modifies database
sudocode issue create → INSERT into issues table
sudocode spec update → UPDATE specs table
sudocode link → INSERT into relationships table
Export to JSONL (auto, debounced)
Wait 5 seconds after last change
Query all entities
Write complete JSONL files
Sync to markdown (optional)
sudocode sync --to-markdown regenerates markdown files
Frontmatter updated from database
Content preserved
Git commit (manual)
Commit JSONL + markdown changes
Example:
# 1. Create issue via CLI
sudocode issue create "Implement token refresh" \
--priority 1 \
--tags auth,tokens
# Output:
# Created ISSUE-053
# Exported to JSONL (debounced)
# 2. Sync to markdown (optional)
sudocode sync --to-markdown
# Output:
# Synced 1 entity to markdown
# ✓ created issue ISSUE-053 → ISSUE-053.md
# 3. Commit changes
git add .sudocode/
git commit -m "Create token refresh issue (ISSUE-053)"
Direction 3: JSONL → Database (After Git Pull)
Triggered by: git pull, sudocode sync, sudocode import
Git pull updates JSONL
Teammate committed changes
JSONL files updated
Local markdown may be stale
Detect changes
Compare JSONL modification time vs database
JSONL is newer → import needed
Import to database
Parse JSONL files line by line
Upsert each entity (insert or update)
Rebuild relationships table
Rebuild tags table
Handle feedback anchors
Sync to markdown (optional)
Update markdown files from database
Preserve content, update frontmatter
Create new markdown files if needed
Example:
# 1. Pull changes
git pull
# Output:
# Updating abc123..def456
# .sudocode/issues/issues.jsonl | 2 ++
# 1 file changed, 2 insertions(+)
# 2. Sync database
sudocode sync
# Output:
# Detecting sync direction...
# Issues JSONL is newer (2025-10-29 15:00 > 14:30)
# → Syncing FROM JSONL TO database
#
# Importing from JSONL...
# ✓ updated issue ISSUE-042
# ✓ created issue ISSUE-053
# Syncing to markdown...
# ✓ updated ISSUE-042.md
# ✓ created ISSUE-053.md
#
# ✓ Synced 2 entities
# 3. Now database and markdown are up to date
Auto-Sync Mechanisms
File Watching
sudocode can watch for file changes and auto-sync:
What it does:
Watches .sudocode/specs/ and .sudocode/issues/ directories
Detects markdown file changes
Debounces changes (2 seconds)
Auto-syncs to database
Auto-exports to JSONL
Use case: Active development with frequent spec edits
Debouncing
To avoid excessive writes, sudocode debounces exports:
Export debounce: 5 seconds
Waits 5 seconds after last database change
Batches multiple rapid changes
Writes JSONL once
File watch debounce: 2 seconds
Waits 2 seconds after last file change
Prevents sync on every keystroke
Syncs once after editing
Manual Sync
For explicit control:
# Sync from markdown to database
sudocode sync --from-markdown
# Sync from database to markdown
sudocode sync --to-markdown
# Auto-detect direction
sudocode sync
Auto-detection logic:
Compare markdown file modification times
Compare JSONL file modification times
Determine which is newer
Sync in that direction
Git Distribution
Team Collaboration Workflow
JSONL files enable seamless team collaboration through git:
Developer A creates spec
sudocode spec create "New Feature" --priority 0
# Creates SPEC-010
# Exports to specs.jsonl
git add .sudocode/
git commit -m "Add new feature spec (SPEC-010)"
git push
Developer B pulls changes
git pull
# .sudocode/specs/specs.jsonl updated
sudocode sync
# Imports SPEC-010 to local database
# Creates specs/new-feature.md
Developer B creates issues
sudocode issue create "Implement feature foundation" \
--implements SPEC-010
# Creates ISSUE-055
# Links to SPEC-010
git add .sudocode/
git commit -m "Create implementation issues for SPEC-010"
git push
Developer A pulls and sees new work
git pull
sudocode sync
sudocode ready
# Shows ISSUE-055 is ready to work on
Merge Conflicts
JSONL conflicts:
<<<<<<< HEAD
{"id":"SPEC-001","title":"Authentication System","priority":0,...}
=======
{"id":"SPEC-001","title":"Auth System","priority":1,...}
>>>>>>> feature-branch
Resolution:
Manual: Choose one version or merge fields
AI-assisted: Ask AI to merge based on semantics
Import after resolution:
# After resolving conflict
sudocode import --input .sudocode/
sudocode sync --to-markdown
Branching Strategy
Context travels with code branches:
# Create feature branch
git checkout -b feature/oauth-integration
# Create spec in branch
sudocode spec create "OAuth Integration" --priority 0
# Spec only exists in this branch
git commit -am "Add OAuth spec"
# Switch to main - spec is gone
git checkout main
sudocode spec list # SPEC-005 not found
# Switch back - spec returns
git checkout feature/oauth-integration
sudocode spec list # SPEC-005 exists
Benefits:
Experimental specs in feature branches
Merge specs with code when merging branch
Context and code always in sync
Best Practices
Do’s
Always commit .sudocode/ changes alongside related code: git add .sudocode/ src/
git commit -m "Implement OAuth (ISSUE-042)
- Added OAuth token exchange
- Updated SPEC-005 with learned details
"
Benefits:
Context travels with implementation
Code review includes design decisions
Git history is complete
✓ Run sync after git pull
Always sync database after pulling: Or use a git hook: # .git/hooks/post-merge
#!/bin/sh
if [ -f .sudocode/specs/specs.jsonl ]; then
sudocode sync
fi
✓ Use watch mode during active development
For frequent spec editing: # Terminal 1: Watch and auto-sync
sudocode sync --watch
# Terminal 2: Edit specs
vim .sudocode/specs/auth-spec.md
Changes sync automatically.
Always add to .gitignore: .sudocode/sudocode.db
.sudocode/sudocode.db-shm
.sudocode/sudocode.db-wal
Database is rebuilt from JSONL, never commit it.
✓ Use branches for experimental specs
Create specs in feature branches: git checkout -b experiment/new-approach
sudocode spec create "Experimental Approach"
# Test the idea
# If good: merge branch
# If bad: delete branch (spec goes away)
Don’ts
✗ Don't commit sudocode.db
Wrong: git add .sudocode/sudocode.db
Right: # Add to .gitignore
echo ".sudocode/sudocode.db*" >> .gitignore
Database is a cache, not source of truth.
✗ Don't manually edit JSONL files
Wrong: vim .sudocode/specs/specs.jsonl # Manual editing
Right: # Edit markdown or use CLI
vim .sudocode/specs/auth-spec.md
sudocode sync --from-markdown
JSONL is auto-generated, edits will be overwritten.
✗ Don't skip sync after git pull
Wrong: git pull
# Continue working without sync
sudocode ready # Stale data!
Right: git pull
sudocode sync # Update database
sudocode ready # Fresh data
✗ Don't force-resolve merge conflicts
Wrong: # During merge conflict
git checkout --theirs .sudocode/specs.jsonl
# Loses your changes!
Right: # Resolve conflicts manually or with AI
# Then import
sudocode import --input .sudocode/
Troubleshooting
Database out of sync after git pull
Symptoms: sudocode list shows stale dataCause: Pulled JSONL changes not imported to databaseSolution: sudocode sync
# Or force import
sudocode import --input .sudocode/
Markdown files missing after git pull
Symptoms: JSONL has entities but no markdown filesCause: Markdown files not synced from databaseSolution: sudocode sync --to-markdown
Regenerates all markdown files from database.
JSONL changes not committed
Symptoms: CLI changes work locally but teammates don’t see themCause: JSONL files not committed to gitSolution: git status
# Shows .sudocode/issues/issues.jsonl modified
git add .sudocode/
git commit -m "Update issues"
git push
Database corrupted or locked
Symptoms: Error: database is locked or database disk image is malformedCause: Concurrent access or corruptionSolution: # Delete database (it's a cache)
rm .sudocode/sudocode.db *
# Rebuild from JSONL
sudocode import --input .sudocode/
JSONL is source of truth, database can always be rebuilt.
Symptoms: Git merge conflict in specs.jsonl or issues.jsonlCause: Two branches modified same entitySolution: Option 1: Manual resolution # Edit JSONL file, choose correct version
vim .sudocode/specs/specs.jsonl
# Resolve conflict markers
git add .sudocode/specs/specs.jsonl
sudocode import --input .sudocode/
Option 2: AI-assisted # Ask AI to merge based on semantics
# "Please merge these two versions of SPEC-001, keeping both changes"
# AI provides merged JSON
# Update JSONL manually
sudocode import --input .sudocode/
Architecture Diagrams
3-Layer Architecture
┌─────────────────────────────────────────────────────────┐
│ Layer 1: Markdown │
│ Human-editable files │
│ │
│ .sudocode/specs/auth-system.md │
│ .sudocode/issues/ISSUE-042.md │
│ │
│ YAML frontmatter + Markdown body │
│ Cross-references: [[SPEC-001]] │
└───────────────────────┬─────────────────────────────────┘
│
│ Parse / Generate
↓
┌─────────────────────────────────────────────────────────┐
│ Layer 3: SQLite │
│ Query cache (local) │
│ │
│ sudocode.db (gitignored) │
│ │
│ Tables: specs, issues, relationships, │
│ tags, feedback, events │
│ Views: ready_issues, blocked_issues │
│ │
│ Fast queries, joins, graph traversal │
└───────────────────────┬─────────────────────────────────┘
│
│ Export / Import
↓
┌─────────────────────────────────────────────────────────┐
│ Layer 2: JSONL │
│ Source of truth (git-tracked) │
│ │
│ .sudocode/specs/specs.jsonl │
│ .sudocode/issues/issues.jsonl │
│ │
│ One JSON object per line │
│ Embedded relationships and tags │
│ Git-friendly line-based diffs │
│ │
│ ✓ Committed to git │
│ ✓ Distributed via git push/pull │
└─────────────────────────────────────────────────────────┘
Data Flow
User Actions:
┌────────────────┐ ┌────────────────┐
│ Edit markdown │ │ CLI commands │
│ (vim, VSCode)│ │ (issue create) │
└───────┬────────┘ └───────┬────────┘
│ │
│ ↓
│ ┌───────────────┐
│ │ SQLite │
│ │ Database │
│ └───────┬───────┘
│ │
↓ │
┌────────────────┐ Export (debounced)
│ Parse YAML + │ │
│ Markdown │ ↓
└───────┬────────┘ ┌──────────────┐
│ │ JSONL │◄── Git tracked
│ │ (Source of │ (push/pull)
└──────────────────► Truth) │
└──────────────┘
│
Import after
git pull
│
↓
┌──────────────┐
│ SQLite │
│ Rebuilt │
└──────────────┘
Sync Commands Manual sync operations
Export Command Export to JSONL
Import Command Import from JSONL
Specs Concept Specification structure
Issues Concept Issue structure
Relationships Linking entities
Next Steps
Initialize sudocode
cd your-project
sudocode init
Verify .gitignore
echo ".sudocode/sudocode.db*" >> .gitignore
Create first spec
sudocode spec create "Your Feature" --priority 0
Observe:
Markdown file created
Database updated
JSONL exported
Commit to git
git add .sudocode/
git commit -m "Add feature spec"
JSONL travels with code!
Set up sync hook (optional)
# .git/hooks/post-merge
#!/bin/sh
if [ -f .sudocode/specs/specs.jsonl ]; then
sudocode sync --from-jsonl
fi
chmod +x .git/hooks/post-merge
Auto-sync after git pull.
Spec-Driven Development Learn how to use the storage model in practice