Skip to main content

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:
NeedLayerWhy
Human editingMarkdownFamiliar format, readable diffs
Git trackingJSONLStructured, line-based diffs
Fast queriesSQLiteRelational queries, indexes
RelationshipsSQLiteGraph traversal, joins
Offline workAll layersLocal-first design
DistributionJSONL + MarkdownGit 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

Format

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

Format

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
1

Parse markdown file

  • Read frontmatter (YAML)
  • Extract content (markdown body)
  • Parse cross-references [[SPEC-ID]]
2

Update database

  • Upsert spec/issue record
  • Create relationships from cross-references
  • Update tags
  • Update timestamps
3

Export to JSONL (debounced)

  • Wait 5 seconds for more changes
  • Query all entities from database
  • Write complete JSONL files
  • Include embedded relationships and tags
4

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
1

CLI modifies database

  • sudocode issue create → INSERT into issues table
  • sudocode spec update → UPDATE specs table
  • sudocode link → INSERT into relationships table
2

Export to JSONL (auto, debounced)

  • Wait 5 seconds after last change
  • Query all entities
  • Write complete JSONL files
3

Sync to markdown (optional)

  • sudocode sync --to-markdown regenerates markdown files
  • Frontmatter updated from database
  • Content preserved
4

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
1

Git pull updates JSONL

  • Teammate committed changes
  • JSONL files updated
  • Local markdown may be stale
2

Detect changes

  • Compare JSONL modification time vs database
  • JSONL is newer → import needed
3

Import to database

  • Parse JSONL files line by line
  • Upsert each entity (insert or update)
  • Rebuild relationships table
  • Rebuild tags table
  • Handle feedback anchors
4

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:
sudocode sync --watch
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:
  1. Compare markdown file modification times
  2. Compare JSONL file modification times
  3. Determine which is newer
  4. Sync in that direction

Git Distribution

Team Collaboration Workflow

JSONL files enable seamless team collaboration through git:
1

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
2

Developer B pulls changes

git pull
# .sudocode/specs/specs.jsonl updated
sudocode sync
# Imports SPEC-010 to local database
# Creates specs/new-feature.md
3

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
4

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:
  1. Manual: Choose one version or merge fields
  2. AI-assisted: Ask AI to merge based on semantics
  3. 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
Always sync database after pulling:
git pull
sudocode sync
Or use a git hook:
# .git/hooks/post-merge
#!/bin/sh
if [ -f .sudocode/specs/specs.jsonl ]; then
  sudocode sync
fi
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.
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

Wrong:
git add .sudocode/sudocode.db
Right:
# Add to .gitignore
echo ".sudocode/sudocode.db*" >> .gitignore
Database is a cache, not source of truth.
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.
Wrong:
git pull
# Continue working without sync
sudocode ready  # Stale data!
Right:
git pull
sudocode sync  # Update database
sudocode ready  # Fresh data
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

Symptoms: sudocode list shows stale dataCause: Pulled JSONL changes not imported to databaseSolution:
sudocode sync
# Or force import
sudocode import --input .sudocode/
Symptoms: JSONL has entities but no markdown filesCause: Markdown files not synced from databaseSolution:
sudocode sync --to-markdown
Regenerates all markdown files from database.
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
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    │
                           └──────────────┘

Next Steps

1

Initialize sudocode

cd your-project
sudocode init
2

Verify .gitignore

echo ".sudocode/sudocode.db*" >> .gitignore
3

Create first spec

sudocode spec create "Your Feature" --priority 0
Observe:
  • Markdown file created
  • Database updated
  • JSONL exported
4

Commit to git

git add .sudocode/
git commit -m "Add feature spec"
JSONL travels with code!
5

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