Story Design Coherence — manage dependency graphs between story design documents
Project description
sdcoh — Story Design Coherence
Manage dependency graphs between story design documents. Detect change impact and stale downstream files.
When writing novels with AI, you end up with dozens of interconnected design documents: character sheets, beat sheets, foreshadowing ledgers, style guides, briefs, and episode drafts. Change one, and you need to update five others. Forget one, and your story has inconsistencies.
sdcoh makes these dependencies explicit and trackable.
The Problem
You update the character sheet...
→ but forget to update the beat sheet
→ which means the brief is based on stale info
→ which means the AI writes the episode with wrong details
→ which means you spend an hour finding why the draft feels off
The Solution
$ sdcoh status
⚠️ Updates needed (3):
design:continuity last updated 3/15 ← episode:ep07 updated 3/28
design:expression-log last updated 3/10 ← episode:ep05 updated 3/27
brief:ep08 last updated 3/13 ← design:beat-sheet updated 3/25
✅ In sync (12)
Install
pip install sdcoh
Optional OpenViking integration:
pip install sdcoh[openviking]
Quick Start
1. Initialize your project
cd your-novel-project/
sdcoh init --name "My Novel" --alias my-novel
This creates sdcoh.yml and .sdcoh/ directory.
2. Declare dependencies in sdcoh.yml
Starting with v0.2, dependencies are declared centrally in sdcoh.yml using patterns. No frontmatter needed in your Markdown files.
rules:
- name: "design informs episodes"
from: "design/*.md"
to: "drafts/ep*.md"
relation: informs
- name: "brief feeds episode"
from: "briefs/{ep}-brief.md"
to: "drafts/{ep}.md"
relation: feeds
Edge semantic: from → to means "when from changes, to becomes stale."
3. Scan and check
sdcoh scan # Build the dependency graph
sdcoh graph # Visualize it
sdcoh validate # Check for broken references
sdcoh status # Find stale documents
4. Check impact before editing
$ sdcoh impact design/characters.md
Affected (4):
🟡 design:beat-sheet ← derives_from
🟡 design:foreshadowing ← references
🟡 brief:ep02 ← references
🟡 episode:ep01 ← implements
Project Config (sdcoh.yml)
project:
name: "My Novel"
alias: "my-novel"
# Directories to scan, each with a node type
scan:
- { path: "design/", type: "design" }
- { path: "drafts/", type: "episode" }
- { path: "briefs/", type: "brief" }
- { path: "reviews/", type: "review" }
# Node types with layer hierarchy (lower = upstream)
node_types:
research: { layer: -1 } # Most upstream
design: { layer: 0 }
brief: { layer: 1 }
episode: { layer: 2 }
review: { layer: 3 } # Most downstream
# Dependency rules: when `from` changes, `to` is stale
rules:
- name: "design informs episodes"
from: "design/*.md"
to: "drafts/ep*.md"
relation: informs
- name: "brief feeds episode"
from: "briefs/{ep}-brief.md"
to: "drafts/{ep}.md"
relation: feeds
# Optional: OpenViking semantic search integration
openviking:
enabled: false
endpoint: "http://localhost:1933"
auto_register: true
Node IDs are auto-generated as {type}:{basename} (e.g. design/characters.md → design:characters).
Default Directory Structure
novel-project/
├── sdcoh.yml # Project config
├── design/ # Character sheets, beat sheets, style guides, etc.
├── drafts/ # Episode manuscripts
├── briefs/ # Writing briefs for AI agents
├── reviews/ # Review results
├── research/ # Research materials
└── docs/ # Workflow documentation
Rule Syntax
Pattern tokens
| Token | Meaning |
|---|---|
* |
Any string (no /) |
? |
Single char (no /) |
{name} |
Named capture (non-greedy, no /) — bind once in from, substitute in to |
| literal | Matches exactly (regex metacharacters are escaped) |
Examples
Fan-out — one file to many:
- name: "design informs all episodes"
from: "design/*.md"
to: "drafts/ep*.md"
relation: informs
1-to-1 pairing — match by shared capture:
- name: "brief feeds episode"
from: "briefs/{ep}-brief.md"
to: "drafts/{ep}.md"
relation: feeds
# briefs/ep01-brief.md → drafts/ep01.md
Multiple variants — any suffix after captured prefix:
- name: "any brief revision feeds episode"
from: "briefs/{ep}-*.md"
to: "drafts/{ep}.md"
relation: feeds
# briefs/ep01-kubota-brief.md → drafts/ep01.md
# briefs/ep01-revision2-brief.md → drafts/ep01.md
Reverse flow — episodes update review logs:
- name: "episode updates expression log"
from: "drafts/ep*.md"
to: "design/expression-log.md"
relation: extracts_from
Validation
sdcoh catches common mistakes at scan time:
- Node ID collision — two files producing the same
{type}:{basename}(e.g. from nested subdirectories) - Undefined placeholder —
to: "drafts/{missing}.md"whenfromhas no{missing}capture - Relation conflict — two rules producing the same edge with different relations
- Self-loop — a file matching both sides is silently excluded
- Zero matches — a rule that produces no edges warns during
sdcoh scan
CLI Reference
| Command | Description |
|---|---|
sdcoh init |
Initialize project (sdcoh.yml + .sdcoh/) |
sdcoh scan |
Apply rules, build dependency graph |
sdcoh impact <path> |
Show what's affected by changing a file |
sdcoh graph |
Display dependency tree |
sdcoh validate |
Check for broken refs, cycles, orphans |
sdcoh status |
Find stale downstream documents |
Options
sdcoh scan --quiet # Minimal output (for hooks)
sdcoh scan --warn # List rules that produced no edges
sdcoh impact <path> --depth N # Limit traversal depth
sdcoh status --warn-only # Only output if warnings exist
sdcoh status --json # JSON output
Claude Code Integration
Install as Plugin
/plugin marketplace add ysttsu/sdcoh
/plugin install sdcoh@sdcoh
Skills
| Skill | Trigger | Action |
|---|---|---|
/sdcoh-scan |
"scan", "graph update" | sdcoh scan + sdcoh validate |
/sdcoh-impact |
"what's affected?" | sdcoh impact on recent file |
/sdcoh-status |
"stale check" | sdcoh status |
PostToolUse Hook (auto-scan on edit)
Add to .claude/settings.json:
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"if": "Write(*/design/*)|Edit(*/design/*)",
"hooks": [{
"type": "command",
"command": "sdcoh scan --quiet && sdcoh status --warn-only",
"timeout": 10
}]
}]
}
}
Background: Why This Exists
This tool was inspired by CoDD (Coherence-Driven Development), which manages coherence between software design documents. sdcoh adapts the same principle for fiction writing workflows, where AI-assisted novel projects can accumulate 20-30+ interconnected design documents.
Built as part of an AI-assisted novel writing workflow where the author acts as "director" and AI agents handle drafting. The dependency graph ensures that when a design decision changes, all downstream documents are flagged for review — preventing the subtle inconsistencies that plague long-form fiction.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sdcoh-0.2.0.tar.gz.
File metadata
- Download URL: sdcoh-0.2.0.tar.gz
- Upload date:
- Size: 34.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d0c4e2aceec24f4e02f940ae741824e776f9de0482195313c4af244a43d518a
|
|
| MD5 |
36824cf2bec37e9d090a1208af6ad722
|
|
| BLAKE2b-256 |
95d85183e0e7538a72638b37e46eaa5407481b1f4208e9ad92f0bf7e873f156a
|
File details
Details for the file sdcoh-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sdcoh-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6632f1219956f18e63f8a0f67cf11f8d3350d931f14f33d4d00e4832854989f4
|
|
| MD5 |
88ce5862df597e2247ef4022f303ce2a
|
|
| BLAKE2b-256 |
2849d6d542fdfe376633929fa54739162e12b8ea777bb128e2677f4f17adb01d
|