docs(discussions): archive decisions restructure deliberation
Three-agent analysis (Si, Qatux, Tyre) evaluating approaches to scale DECISIONS.md. All independently recommended hybrid domain-split markdown + SQLite index. Tyre sharpened scope: 7 domains not 11, 2 tables not 3, pre-commit hook for sync drift prevention (Phase 2). Also adds D-031 to Round 18 in discussions index. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,4 +21,4 @@ Historical discussion rounds from the Commonwealth game design process.
|
||||
| 15 | README / Steam Page Placeholder | - | [round-15](round-15-readme-steam-page.md) |
|
||||
| 16 | Factions — Building a Fuller Narrative Picture | AWAITING INPUT | [complete](round-16-factions-complete.md), [lore](round-16-factions.md), [mechanics](round-16-faction-mechanics.md), [notes](round-16-session-notes.md) |
|
||||
| 17 | Content Architecture | D-023, D-024, D-025, D-026, D-027, D-028, D-029 | [round-17](round-17-content-architecture.md) |
|
||||
| 18 | v0.1 Gap Analysis Workshop | D-030 | [round-18](round-18-v01-gap-analysis-workshop.md) |
|
||||
| 18 | v0.1 Gap Analysis Workshop | D-030, D-031 | [round-18](round-18-v01-gap-analysis-workshop.md) |
|
||||
|
||||
@@ -0,0 +1,935 @@
|
||||
# Decision Architecture Restructure — Librarian's Analysis
|
||||
**Author:** Qatux (Documenter)
|
||||
**Date:** 2026-02-11
|
||||
**Status:** Proposal for team review
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
DECISIONS.md has grown to 474 lines (47KB) with 31 confirmed decisions, 11 open questions, and 10 rejected alternatives. At current growth rates (~7 decisions per workshop round), we'll reach 1000+ lines by Round 25 — likely by end of Q1 2026. The current single-file approach creates three problems:
|
||||
|
||||
1. **Context window waste:** Agents read all 474 lines for session context but typically need only 5-8 decisions relevant to their domain
|
||||
2. **Maintenance burden:** Every decision addition requires re-indexing 47KB in Qdrant, updating briefings across the board
|
||||
3. **Discovery friction:** Finding decisions requires either linear scan of the full file or Qdrant semantic search with no domain filtering
|
||||
|
||||
I recommend a **hybrid domain-split with DB index** approach: split decisions into domain files (`decisions/architecture.md`, `decisions/content.md`, etc.), maintain a SQLite decision index for cross-referencing and ticket linkage, and use Qdrant for semantic discovery. This reduces per-agent context load by ~70-80%, improves maintainability, and preserves all current workflows.
|
||||
|
||||
**Migration effort:** ~4 hours. **Risk:** Low — git history preserved, all IDs stable, rollback straightforward.
|
||||
|
||||
---
|
||||
|
||||
## 1. Problems with Current Approach at Scale
|
||||
|
||||
### 1.1 Context Window Inefficiency
|
||||
|
||||
From my maintenance perspective, this is the most immediate problem. Current workflow:
|
||||
|
||||
- Agent session starts → reads briefing → briefing says "read DECISIONS.md" → agent reads all 474 lines
|
||||
- Tyre needs D-010, D-020, D-026, D-030 (architecture decisions) — 4 decisions, ~80 relevant lines
|
||||
- Tyre gets 474 lines, 394 of which are irrelevant to architecture work
|
||||
|
||||
At 1000 lines (projected Round 25), this becomes 920 wasted lines per session. Multiply by 8 agents × multiple rounds per week, and we're burning thousands of tokens on irrelevant context.
|
||||
|
||||
**Briefings partially solve this:** I maintain per-agent briefings that excerpt key decisions. But briefings are summaries, not sources of truth. When an agent needs detail, they read DECISIONS.md. And briefings don't eliminate the problem — they duplicate it. I now maintain:
|
||||
- DECISIONS.md (source of truth, 474 lines)
|
||||
- 8 agent briefings (each excerpting 5-10 decisions, ~40-80 lines per briefing)
|
||||
- Total maintenance surface: 474 + (8 × 60 average) = ~950 lines across 9 files
|
||||
|
||||
When D-032 arrives, I update DECISIONS.md, re-index it in Qdrant, and update 2-4 relevant briefings. That's 3-5 file edits per decision.
|
||||
|
||||
### 1.2 Discovery Friction
|
||||
|
||||
How do agents find decisions? Three current methods:
|
||||
|
||||
1. **Read DECISIONS.md linearly** — Works for small files. At 474 lines, agents skim. At 1000 lines, they give up.
|
||||
2. **Qdrant semantic search** — `qdrant-search "chunk-based maps"` finds D-012. Works well! But no domain filtering. A search for "NPC generation" returns matches from D-024 (content), D-026 (simulation), D-028 (dialogue), and potentially D-008 (procedural generation). Agent must filter manually.
|
||||
3. **Ask Qatux** — I retrieve by ID or topic. This works but creates a bottleneck — every agent query waits for me.
|
||||
|
||||
The ideal discovery workflow:
|
||||
- Agent knows the domain (architecture, content, UI, etc.)
|
||||
- Agent searches within domain semantically or scans domain file (~80-120 lines)
|
||||
- Agent finds decision, reads detail, cross-references dependencies
|
||||
|
||||
Current structure supports none of this except the last step.
|
||||
|
||||
### 1.3 Cross-Reference Complexity
|
||||
|
||||
Decisions reference each other frequently:
|
||||
- D-002 superseded by D-005
|
||||
- D-006 superseded by D-027
|
||||
- D-010 referenced by D-011, D-017, D-020, D-030
|
||||
- D-024 references D-017 (perception modes as pattern)
|
||||
- D-027 supersedes D-006, references D-009, D-010
|
||||
|
||||
In a single file, cross-references are implicit — scroll up/down. But they're not machine-readable. The ticket database has `decision_ref TEXT`, which links tickets to decisions. But decisions don't link to tickets, to related decisions, or to discussion rounds in a structured way.
|
||||
|
||||
**What I maintain manually:**
|
||||
- "Supersedes: D-NNN" in decision rationale (freeform text)
|
||||
- "Resolves: Q-NNN" in decision metadata (freeform text)
|
||||
- Briefing cross-references (freeform text, duplicated per agent)
|
||||
|
||||
None of this is queryable. I can't ask "which decisions depend on D-010?" without grepping the file.
|
||||
|
||||
### 1.4 Superseded Decision Cruft
|
||||
|
||||
D-002 and D-006 are superseded. They appear in DECISIONS.md as:
|
||||
|
||||
```markdown
|
||||
### D-002: SUPERSEDED by D-005
|
||||
```
|
||||
|
||||
No rationale, no original text. The history is in git, but not in the document. From a librarian perspective, this is information loss — I can't answer "why was D-002 rejected?" without `git log`. And I can't direct agents to compare D-002 vs D-005 because D-002 is a tombstone.
|
||||
|
||||
For the record: I archived the original decisions in git history (commit `6a3c1f2`, 2026-02-08). But that's invisible to agents who don't read git logs.
|
||||
|
||||
### 1.5 Qdrant Re-indexing Overhead
|
||||
|
||||
Every time DECISIONS.md changes:
|
||||
1. Edit the file (add D-032, update Q-009 status, etc.)
|
||||
2. Commit to git
|
||||
3. Re-index: `qdrant-index docs/DECISIONS.md`
|
||||
4. Qdrant chunks the 47KB file into ~15-20 semantic chunks
|
||||
5. All chunks re-embedded via ollama (nomic-embed-text)
|
||||
6. ~30-45 seconds per index operation
|
||||
|
||||
At current rates (2-7 decisions per round, ~3 rounds per week), that's 6-21 re-index operations per week. Not painful yet. But at scale:
|
||||
- 1000-line DECISIONS.md → ~40-50 chunks → 60-90 seconds to re-index
|
||||
- More frequent updates → more re-indexing friction
|
||||
- Agents waiting for "has Qatux indexed the new decisions yet?"
|
||||
|
||||
**Alternative:** If decisions are split into domain files, only the changed domain file re-indexes. D-032 (architecture) → re-index `decisions/architecture.md` (80 lines, ~3-5 chunks, 10 seconds). 6x faster, no cross-domain noise.
|
||||
|
||||
### 1.6 Ticket Linkage Underutilized
|
||||
|
||||
The ticket schema has `decision_ref TEXT` (line 17 of `schema.sql`). Currently 59/273 tickets (21.6%) use it. Why so low?
|
||||
|
||||
From maintenance experience: agents forget. When creating tickets, they don't always remember to link the decision. And I don't have a workflow to audit "which decisions lack linked tickets?" or "which tickets reference nonexistent decisions?"
|
||||
|
||||
A decision index table would enable:
|
||||
- `SELECT * FROM decisions WHERE id NOT IN (SELECT DISTINCT decision_ref FROM tickets WHERE decision_ref LIKE 'D-%')` — decisions with no linked tickets
|
||||
- `SELECT * FROM tickets WHERE decision_ref NOT IN (SELECT id FROM decisions)` — tickets referencing nonexistent decisions
|
||||
- Automated cross-referencing in briefings: "D-020 is referenced by tickets #45, #67, #89"
|
||||
|
||||
---
|
||||
|
||||
## 2. Options Considered
|
||||
|
||||
### 2.1 Status Quo (Single File)
|
||||
|
||||
**Keep DECISIONS.md as-is, accept the scaling costs.**
|
||||
|
||||
**Pros:**
|
||||
- Zero migration effort
|
||||
- Familiar to all agents
|
||||
- Git history continuous
|
||||
- Simple mental model
|
||||
|
||||
**Cons:**
|
||||
- Context window waste grows linearly with decision count
|
||||
- Discovery via linear scan or Qdrant only (no domain filtering)
|
||||
- Re-indexing overhead grows with file size
|
||||
- Cross-references remain manual and unstructured
|
||||
- Superseded decisions are tombstones
|
||||
- No automated ticket/decision linkage auditing
|
||||
|
||||
**My assessment:** This is the path of least resistance, but it fails in 6 months. By Round 30 (~Q2 2026), DECISIONS.md will be 1200+ lines and agents will stop reading it. We'll rely entirely on briefings, and briefings will be stale. I'll spend more time updating briefings than documenting decisions.
|
||||
|
||||
**Verdict:** Reject. The problem is real and worsening.
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Split by Domain (Multiple Markdown Files)
|
||||
|
||||
**Split DECISIONS.md into domain files:** `decisions/architecture.md`, `decisions/content.md`, `decisions/process.md`, etc. Each file contains decisions relevant to that domain. Agent briefings point to specific domain files.
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
docs/decisions/
|
||||
architecture.md # D-010, D-012, D-020, D-030 (client-server, engine, testability)
|
||||
content.md # D-023, D-024, D-025, D-028, D-029 (NPCs, dialogue, templates)
|
||||
camera-perception.md # D-011, D-015, D-016, D-017, D-018, D-019 (fog, camera, sound)
|
||||
scope.md # D-001, D-003, D-005, D-006, D-007, D-014, D-027 (game concept, prototype)
|
||||
process.md # D-022 (workflow)
|
||||
meta.md # D-021 (title), D-004 (team)
|
||||
world.md # Future: world map, time system, wormholes (D-031, D-013)
|
||||
combat.md # Future: D-008, combat mechanics
|
||||
questions.md # All Q-NNN entries
|
||||
rejected.md # All R-NNN entries
|
||||
```
|
||||
|
||||
**Discovery workflow:**
|
||||
1. Agent knows domain (e.g., Tyre working on architecture)
|
||||
2. Agent reads `decisions/architecture.md` (~80-120 lines)
|
||||
3. Agent finds D-020, sees cross-reference to D-010
|
||||
4. Agent opens same file (D-010 is architecture), reads detail
|
||||
|
||||
**Briefing updates:**
|
||||
- Tyre's briefing: "Read `docs/decisions/architecture.md` for relevant decisions"
|
||||
- Paula's briefing: "Read `docs/decisions/content.md` for NPC/dialogue decisions"
|
||||
- Gestalt's briefing: "Read `docs/decisions/scope.md` and `docs/decisions/content.md`"
|
||||
|
||||
**Pros:**
|
||||
- **Context efficiency:** Agents read 80-150 lines (relevant domain) instead of 474 (everything). ~70-80% reduction.
|
||||
- **Qdrant efficiency:** Re-index only changed domain file. 6x faster, cleaner search results (domain-filtered).
|
||||
- **Discovery:** Domain files are scannable. "I need architecture decisions" → read one file.
|
||||
- **Maintainability:** I update one domain file per decision, not a monolithic file. Git diffs are cleaner (architecture changes don't mix with content changes).
|
||||
- **Briefing simplicity:** Point to 1-2 domain files instead of "read all of DECISIONS.md and filter mentally."
|
||||
|
||||
**Cons:**
|
||||
- **Cross-domain references harder:** D-024 (content) references D-017 (perception). Agent must open two files. (Mitigated by hyperlinks in markdown.)
|
||||
- **Chronological view lost:** No single "what did we decide in order?" file. (Mitigated by discussion round archive in `docs/discussions/README.md`, which already tracks chronology.)
|
||||
- **Migration effort:** ~2 hours to split, update briefings, re-index. Not trivial but not expensive.
|
||||
- **Domain boundaries unclear for some decisions:** Where does D-013 (insert/POI system) go? Camera-perception (UI), or world (navigation)? I'd need to define domain taxonomy.
|
||||
|
||||
**My assessment:** This solves the context window problem immediately and the Qdrant re-indexing problem partially. It's a 70% solution — good, but not complete. Cross-referencing and ticket linkage remain manual.
|
||||
|
||||
**Verdict:** Strong candidate. Improvements over status quo are significant and immediate.
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Move to Database (SQLite)
|
||||
|
||||
**Replace DECISIONS.md with a SQLite table.** Decisions become rows, cross-references are foreign keys, tickets link via `decision_ref`.
|
||||
|
||||
**Schema sketch:**
|
||||
```sql
|
||||
CREATE TABLE decisions (
|
||||
id TEXT PRIMARY KEY, -- 'D-001', 'Q-001', 'R-001'
|
||||
type TEXT NOT NULL CHECK(type IN ('decision', 'question', 'rejected')),
|
||||
title TEXT NOT NULL,
|
||||
date TEXT NOT NULL,
|
||||
decision TEXT, -- The actual decision text
|
||||
rationale TEXT,
|
||||
raised_by TEXT,
|
||||
dissent TEXT,
|
||||
status TEXT DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'resolved')),
|
||||
superseded_by TEXT REFERENCES decisions(id),
|
||||
resolves TEXT REFERENCES decisions(id), -- e.g., D-031 resolves Q-009
|
||||
domain TEXT, -- 'architecture', 'content', 'process', etc.
|
||||
round INTEGER, -- Round number where decided
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE decision_refs (
|
||||
source_id TEXT NOT NULL REFERENCES decisions(id),
|
||||
target_id TEXT NOT NULL REFERENCES decisions(id),
|
||||
ref_type TEXT NOT NULL CHECK(ref_type IN ('supersedes', 'references', 'resolves', 'depends_on')),
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX idx_decisions_status ON decisions(status);
|
||||
```
|
||||
|
||||
**Workflow:**
|
||||
- I document decisions by inserting rows: `sqlite-exec "INSERT INTO decisions (id, type, title, date, decision, rationale, raised_by, domain, round) VALUES (...)"`
|
||||
- Agents query: `sqlite-query "SELECT * FROM decisions WHERE domain='architecture' AND status='active'"`
|
||||
- Tickets link via existing `decision_ref` field (already in schema)
|
||||
- Cross-references queryable: `SELECT target_id FROM decision_refs WHERE source_id='D-010'`
|
||||
|
||||
**Export to markdown:** Generate `decisions/architecture.md` from DB on demand or via git hook:
|
||||
```bash
|
||||
sqlite-query "SELECT * FROM decisions WHERE domain='architecture'" | format-as-markdown > decisions/architecture.md
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- **Structured cross-references:** Foreign keys, queryable. "Which decisions reference D-010?" is a JOIN, not a grep.
|
||||
- **Ticket integration:** `decision_ref` in tickets already exists. Now decisions table exists too. Bidirectional: tickets→decisions and decisions→tickets.
|
||||
- **Status tracking:** Superseded decisions remain queryable with full text, just marked `status='superseded'`. No tombstones.
|
||||
- **Domain filtering:** `WHERE domain='architecture'` — instant.
|
||||
- **Audit queries:** "Decisions without linked tickets", "decisions modified in last 7 days", "decisions from Round 17", etc.
|
||||
- **Qdrant still works:** Export markdown from DB, index markdown. Qdrant becomes a semantic layer over structured data.
|
||||
|
||||
**Cons:**
|
||||
- **Not human-readable in native form:** Agents can't `cat decisions.db`. They must query. (Mitigated by markdown export.)
|
||||
- **Git diffs unusable:** Binary SQLite file. Can't see "what changed in D-020?" in a commit. (Mitigated by exporting markdown to git, treating DB as source of truth and markdown as build artifact.)
|
||||
- **Write friction:** Adding a decision requires SQL INSERT, not markdown edit. Higher ceremony for me. (Mitigated by wrapper script: `decision-add --id D-032 --title "Foo" --domain architecture --decision "We decided X" --rationale "Because Y"`)
|
||||
- **Learning curve for agents:** Agents must learn to query the DB instead of reading markdown. (Mitigated by keeping markdown exports in git as read-only artifacts.)
|
||||
- **Migration complexity:** Parsing 31 decisions + 11 questions + 10 rejected from DECISIONS.md into structured rows. ~3-4 hours.
|
||||
|
||||
**My assessment:** This is the most powerful solution long-term, but the steepest upfront cost. The cross-reference and audit capabilities are compelling — this is what a librarian wants. But it trades away the simplicity of "just edit markdown" for structured data discipline. And git history becomes opaque (binary DB vs diffable markdown).
|
||||
|
||||
**Hybrid mitigation:** DB as source of truth, markdown exports to git. Best of both worlds? Possibly. But now I'm maintaining two formats — insert into DB, export to markdown, commit markdown. That's 3 steps per decision instead of 1.
|
||||
|
||||
**Verdict:** Powerful but heavy. Overkill for current scale (31 decisions). Revisit at 100+ decisions.
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Hybrid: Domain Files + DB Index
|
||||
|
||||
**Combine 2.2 and 2.3:** Decisions live in domain markdown files (human-readable, git-diffable). A SQLite index table tracks metadata (ID, domain, status, supersedes, round, tags) for cross-referencing and querying. Markdown is source of truth for content; DB is source of truth for relationships.
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
docs/decisions/
|
||||
architecture.md # D-010, D-012, D-020, D-030 (full text)
|
||||
content.md # D-023, D-024, D-025, D-028, D-029 (full text)
|
||||
...
|
||||
index.db # SQLite index (see schema below)
|
||||
```
|
||||
|
||||
**Index schema:**
|
||||
```sql
|
||||
CREATE TABLE decision_index (
|
||||
id TEXT PRIMARY KEY, -- 'D-001', 'Q-001', 'R-001'
|
||||
type TEXT NOT NULL CHECK(type IN ('decision', 'question', 'rejected')),
|
||||
title TEXT NOT NULL,
|
||||
domain TEXT NOT NULL, -- 'architecture', 'content', etc.
|
||||
file_path TEXT NOT NULL, -- 'docs/decisions/architecture.md'
|
||||
status TEXT DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'resolved')),
|
||||
round INTEGER, -- Round number
|
||||
date TEXT NOT NULL,
|
||||
tags TEXT, -- JSON array: ["multiplayer", "client-server"]
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE decision_refs (
|
||||
source_id TEXT NOT NULL REFERENCES decision_index(id),
|
||||
target_id TEXT NOT NULL REFERENCES decision_index(id),
|
||||
ref_type TEXT NOT NULL CHECK(ref_type IN ('supersedes', 'references', 'resolves', 'depends_on')),
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_dindex_domain ON decision_index(domain);
|
||||
CREATE INDEX idx_dindex_type ON decision_index(type);
|
||||
CREATE INDEX idx_dindex_status ON decision_index(status);
|
||||
```
|
||||
|
||||
**Workflow:**
|
||||
|
||||
1. **Adding a decision:**
|
||||
- I edit `docs/decisions/architecture.md`, add D-032 in full markdown
|
||||
- I insert metadata into DB: `sqlite-exec "INSERT INTO decision_index (id, type, title, domain, file_path, round, date) VALUES ('D-032', 'decision', 'Foo system', 'architecture', 'docs/decisions/architecture.md', 19, '2026-02-12')"`
|
||||
- I add cross-refs: `sqlite-exec "INSERT INTO decision_refs (source_id, target_id, ref_type) VALUES ('D-032', 'D-010', 'references')"`
|
||||
- I commit both markdown and DB: `git add docs/decisions/architecture.md docs/decisions/index.db && git commit`
|
||||
|
||||
2. **Agent discovery:**
|
||||
- Tyre queries: `sqlite-query "SELECT id, title, file_path FROM decision_index WHERE domain='architecture' AND status='active'"` → gets list of IDs + file paths
|
||||
- Tyre reads `docs/decisions/architecture.md` (80 lines, all relevant)
|
||||
- Or: Tyre knows domain, just reads the file directly (no query needed)
|
||||
|
||||
3. **Cross-reference queries:**
|
||||
- "Which decisions reference D-010?" → `sqlite-query "SELECT source_id FROM decision_refs WHERE target_id='D-010'"`
|
||||
- "Which tickets reference D-010?" → `sqlite-query "SELECT id, title FROM tickets WHERE decision_ref='D-010'"`
|
||||
|
||||
4. **Qdrant indexing:**
|
||||
- Re-index changed domain file: `qdrant-index docs/decisions/architecture.md` (fast, domain-scoped)
|
||||
- Qdrant semantic search finds D-032, returns ID, agent queries index for file path
|
||||
|
||||
5. **Superseded decisions:**
|
||||
- D-002 remains in `docs/decisions/scope.md` with full text (archived, not deleted)
|
||||
- Index marks: `status='superseded', superseded_by='D-005'`
|
||||
- Queries can filter: `WHERE status='active'` or include superseded for historical context
|
||||
|
||||
**Pros:**
|
||||
- **Preserves markdown readability:** Agents read domain files, see full decision text, context, rationale
|
||||
- **Git diffs clean:** Changes to D-020 show up as markdown diff in `architecture.md`
|
||||
- **Structured cross-references:** Foreign keys in DB, queryable
|
||||
- **Ticket integration:** Bidirectional — tickets link to decisions (existing), decisions link to tickets (new, via index)
|
||||
- **Context efficiency:** Agents read 80-150 lines (domain file) instead of 474 (monolithic)
|
||||
- **Qdrant efficiency:** Re-index domain file only (fast, domain-scoped)
|
||||
- **Audit queries:** Leverage SQLite for "decisions without tickets", "decisions modified in Round 17", etc.
|
||||
- **Superseded decisions preserved:** Full text in markdown, status in DB
|
||||
- **Low migration cost:** Split markdown (~2 hours) + seed DB index (~1 hour) = ~3 hours total
|
||||
|
||||
**Cons:**
|
||||
- **Dual maintenance:** Markdown (content) + DB (metadata). I must keep them in sync.
|
||||
- **Index can drift:** If I edit markdown without updating DB, index becomes stale. (Mitigated by linting: `decision-lint` script checks markdown IDs match DB IDs.)
|
||||
- **Slightly higher ceremony:** Each decision is 2 operations (edit markdown + insert into DB). But wrapper script can streamline: `decision-add D-032 --domain architecture --title "Foo" --markdown "$(cat)"` → inserts markdown template + DB row.
|
||||
|
||||
**My assessment:** This is the sweet spot. It preserves the human-friendliness of markdown (git diffs, agent readability) while adding the structure I need for cross-references and auditing. The dual-maintenance concern is real but manageable — I'm already maintaining DECISIONS.md + 8 briefings (9 files), so maintaining 6 domain files + 1 DB index (7 entities) is actually simpler.
|
||||
|
||||
**Verdict:** Recommended approach.
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Tag-Based (Single File + Front Matter)
|
||||
|
||||
**Keep DECISIONS.md as single file, add YAML front matter to each decision with domain tags.** Qdrant indexes with tags, agents query by tag.
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
### D-020: Engine and architecture selection
|
||||
<!--
|
||||
tags: [architecture, engine, multiplayer, client-server]
|
||||
domain: architecture
|
||||
round: 13
|
||||
status: active
|
||||
-->
|
||||
- **Date:** 2026-02-09
|
||||
- **Decision:** Godot client + Rust simulation via subprocess/IPC
|
||||
...
|
||||
```
|
||||
|
||||
**Discovery:**
|
||||
- Agent queries Qdrant: `qdrant-search "client-server architecture" --filter domain:architecture`
|
||||
- Or: Agent uses grep: `grep -A 30 "domain: architecture" DECISIONS.md`
|
||||
|
||||
**Pros:**
|
||||
- Single file (simple mental model)
|
||||
- Tags add structure without splitting files
|
||||
- Qdrant can filter by tags (if we enhance the indexer to parse front matter)
|
||||
- Git history continuous
|
||||
|
||||
**Cons:**
|
||||
- **Still 474+ lines per read:** Doesn't solve context window problem
|
||||
- **Front matter is noise:** Each decision grows by 5-8 lines of YAML
|
||||
- **Tag discipline required:** I must tag every decision correctly (more ceremony)
|
||||
- **Cross-references still manual:** Tags don't create queryable relationships
|
||||
- **Qdrant filter support unclear:** Current `qdrant-connector.py` doesn't parse front matter. Would need to enhance.
|
||||
|
||||
**My assessment:** This adds complexity (front matter) without solving the core problem (context window bloat). Tags are useful metadata, but they don't reduce the read burden. And implementing tag-based filtering in Qdrant requires custom indexer logic.
|
||||
|
||||
**Verdict:** Reject. Adds work, solves little.
|
||||
|
||||
---
|
||||
|
||||
## 3. Recommended Approach: Hybrid Domain Files + DB Index
|
||||
|
||||
**For the record:** I recommend **Option 2.4 — Hybrid domain files with SQLite index.**
|
||||
|
||||
### 3.1 Why This Approach
|
||||
|
||||
1. **Context efficiency:** Agents read 80-150 lines (domain file) instead of 474 (monolithic). Projected 70-80% reduction in context waste.
|
||||
|
||||
2. **Maintainability:** I update one domain file per decision, not a 474-line monolith. Git diffs are cleaner. Merge conflicts less likely (agents working in different domains don't conflict).
|
||||
|
||||
3. **Discovery:** Three pathways, all improved:
|
||||
- **Domain-aware scan:** Agent knows domain, reads domain file (~2 minutes)
|
||||
- **Semantic search:** Qdrant indexes domain files, returns results with file path metadata → agent reads file
|
||||
- **Structured query:** Agent queries index by domain/status/round → gets list of IDs + file paths → reads file
|
||||
|
||||
4. **Cross-references:** SQLite index enables queries like "which decisions reference D-010?" and "which tickets implement D-020?" Previously impossible without manual grep.
|
||||
|
||||
5. **Superseded decisions preserved:** Full text remains in markdown (archival integrity), status tracked in DB (queryable). No more tombstones.
|
||||
|
||||
6. **Qdrant efficiency:** Re-index only changed domain file. 6x faster, cleaner search results.
|
||||
|
||||
7. **Ticket integration:** Existing `decision_ref` field in tickets table links to decisions. New index table enables reverse lookup: decisions→tickets.
|
||||
|
||||
8. **Audit capabilities:** "Decisions from Round 17", "decisions without linked tickets", "decisions modified in last 7 days" — all queryable.
|
||||
|
||||
9. **Low migration cost:** ~3-4 hours to split markdown, seed index DB, update briefings, re-index in Qdrant. One-time cost.
|
||||
|
||||
10. **Rollback path:** If this fails, the domain markdown files can be concatenated back into a single file. Git history preserved. Index DB can be discarded.
|
||||
|
||||
### 3.2 Domain Taxonomy
|
||||
|
||||
I propose the following domain split (subject to team review):
|
||||
|
||||
| Domain | File | Decisions (Current) | Projected Growth |
|
||||
|--------|------|---------------------|------------------|
|
||||
| **architecture** | `decisions/architecture.md` | D-010, D-012, D-020, D-030 | 10-15 total (engine, networking, testability, performance) |
|
||||
| **content** | `decisions/content.md` | D-023, D-024, D-025, D-028, D-029 | 20-30 total (NPCs, dialogue, templates, storyteller) |
|
||||
| **camera-perception** | `decisions/camera-perception.md` | D-011, D-015, D-016, D-017, D-018, D-019 | 8-12 total (fog, vision, sound, monologue) |
|
||||
| **scope** | `decisions/scope.md` | D-001, D-003, D-005, D-006, D-007, D-014, D-027 | 12-18 total (game concept, pillars, prototype scope) |
|
||||
| **world** | `decisions/world.md` | D-013, D-031 | 12-20 total (time, maps, wormholes, POIs, navigation) |
|
||||
| **combat** | `decisions/combat.md` | D-008 | 8-12 total (combat mechanics, z-levels, hubris wall) |
|
||||
| **process** | `decisions/process.md` | D-022 | 5-8 total (workflow, tooling, team process) |
|
||||
| **meta** | `decisions/meta.md` | D-004, D-021 | 3-5 total (team, title, licensing) |
|
||||
| **multiplayer** | `decisions/multiplayer.md` | D-009 | 6-10 total (multiplayer design, networking) |
|
||||
| **questions** | `decisions/questions.md` | Q-001 through Q-011 | Variable (questions resolve into decisions) |
|
||||
| **rejected** | `decisions/rejected.md` | R-001 through R-010 | 15-25 total (rejected alternatives, rationale) |
|
||||
|
||||
**Rationale for groupings:**
|
||||
- **architecture** = technical foundation (engine, client-server, testability)
|
||||
- **content** = NPC/dialogue/template systems (the "what" of the game)
|
||||
- **camera-perception** = player viewport and information channels (fog, sound, monologue)
|
||||
- **scope** = game concept and prototype definition (the "what are we building" decisions)
|
||||
- **world** = spatial and temporal systems (maps, time, navigation)
|
||||
- **combat** = action pillar (separate from world/perception because it's a distinct system)
|
||||
- **process** = how the team works (workflow, tooling, documentation)
|
||||
- **meta** = project-level decisions (title, team, licensing)
|
||||
- **multiplayer** = separate from architecture because it's a design domain, not just technical
|
||||
|
||||
**Edge cases:**
|
||||
- D-026 (simulation tiers) — could be architecture or content. I'd put it in **architecture** (performance budgets) with cross-reference note in **content**.
|
||||
- D-013 (insert/POI system) — could be camera-perception or world. I'd put it in **world** (navigation) with cross-reference in **camera-perception**.
|
||||
|
||||
**Flexibility:** Domain boundaries can shift. If **content** grows to 200 lines, we split it: `decisions/content-npcs.md`, `decisions/content-dialogue.md`. The index DB makes this trivial — update `file_path` column, move text.
|
||||
|
||||
### 3.3 Index DB Schema (Final)
|
||||
|
||||
```sql
|
||||
-- Decision metadata index
|
||||
CREATE TABLE decision_index (
|
||||
id TEXT PRIMARY KEY, -- 'D-001', 'Q-001', 'R-001'
|
||||
type TEXT NOT NULL CHECK(type IN ('decision', 'question', 'rejected')),
|
||||
title TEXT NOT NULL,
|
||||
domain TEXT NOT NULL, -- 'architecture', 'content', 'scope', etc.
|
||||
file_path TEXT NOT NULL, -- 'docs/decisions/architecture.md'
|
||||
status TEXT DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'resolved', 'open')),
|
||||
round INTEGER, -- Round number where decided/raised
|
||||
date TEXT NOT NULL, -- YYYY-MM-DD
|
||||
tags TEXT, -- JSON array: ["multiplayer", "client-server"] (optional)
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Cross-references between decisions
|
||||
CREATE TABLE decision_refs (
|
||||
source_id TEXT NOT NULL REFERENCES decision_index(id),
|
||||
target_id TEXT NOT NULL REFERENCES decision_index(id),
|
||||
ref_type TEXT NOT NULL CHECK(ref_type IN ('supersedes', 'references', 'resolves', 'depends_on')),
|
||||
note TEXT, -- Optional context, e.g., "CombatCapability component pattern"
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX idx_dindex_domain ON decision_index(domain);
|
||||
CREATE INDEX idx_dindex_type ON decision_index(type);
|
||||
CREATE INDEX idx_dindex_status ON decision_index(status);
|
||||
CREATE INDEX idx_dindex_round ON decision_index(round);
|
||||
CREATE INDEX idx_drefs_source ON decision_refs(source_id);
|
||||
CREATE INDEX idx_drefs_target ON decision_refs(target_id);
|
||||
```
|
||||
|
||||
**Why this schema:**
|
||||
- `decision_index` table = lightweight metadata (no full decision text, which stays in markdown)
|
||||
- `file_path` column = bridge to markdown source of truth
|
||||
- `tags` = optional JSON array for future filtering (not MVP, but nice-to-have)
|
||||
- `decision_refs` table = structured cross-references, queryable bidirectionally
|
||||
- `ref_type` enum = semantic meaning (supersedes vs references vs resolves)
|
||||
|
||||
**Storage size:** ~500 bytes per decision row + ~100 bytes per cross-reference. 31 decisions + 20 cross-refs ≈ 18KB. At 100 decisions, ~60KB. Negligible.
|
||||
|
||||
### 3.4 Workflow Changes
|
||||
|
||||
#### For me (Qatux):
|
||||
|
||||
**Adding a new decision (manual process, MVP):**
|
||||
1. Determine domain (architecture, content, etc.)
|
||||
2. Edit `docs/decisions/{domain}.md`, add decision in full markdown format (title, date, decision, rationale, etc.)
|
||||
3. Insert into index DB:
|
||||
```bash
|
||||
db/connectors/sqlite-exec "INSERT INTO decision_index (id, type, title, domain, file_path, round, date, status) VALUES ('D-032', 'decision', 'Foo system', 'architecture', 'docs/decisions/architecture.md', 19, '2026-02-12', 'active')"
|
||||
```
|
||||
4. If cross-references exist, insert into `decision_refs`:
|
||||
```bash
|
||||
db/connectors/sqlite-exec "INSERT INTO decision_refs (source_id, target_id, ref_type) VALUES ('D-032', 'D-010', 'references')"
|
||||
```
|
||||
5. Re-index domain file in Qdrant:
|
||||
```bash
|
||||
db/connectors/qdrant-index docs/decisions/architecture.md
|
||||
```
|
||||
6. Update relevant agent briefings (point to domain file if not already)
|
||||
7. Commit:
|
||||
```bash
|
||||
git add docs/decisions/architecture.md db/commonwealth.db docs/briefings/*.md
|
||||
git commit -m "docs(decisions): add D-032 Foo system (architecture)"
|
||||
```
|
||||
|
||||
**Later: Streamline with wrapper script** (future enhancement, not MVP):
|
||||
```bash
|
||||
decision-add D-032 \
|
||||
--domain architecture \
|
||||
--title "Foo system" \
|
||||
--decision "We will use X because Y" \
|
||||
--rationale "Rationale here" \
|
||||
--raised-by "Tyre" \
|
||||
--round 19 \
|
||||
--references D-010
|
||||
```
|
||||
Script generates markdown, inserts into DB, re-indexes Qdrant, prompts for briefing updates.
|
||||
|
||||
#### For agents:
|
||||
|
||||
**Discovery:**
|
||||
- **If domain known:** Read `docs/decisions/{domain}.md` directly (80-150 lines). Example: Tyre reads `docs/decisions/architecture.md` before architecture work.
|
||||
- **If searching semantically:** Use Qdrant: `qdrant-search "client-server architecture"` → returns D-020 with file path → read `docs/decisions/architecture.md`
|
||||
- **If querying by metadata:** Use SQLite: `sqlite-query "SELECT id, title, file_path FROM decision_index WHERE domain='content' AND status='active'"` → get list → read file
|
||||
|
||||
**Cross-references:**
|
||||
- Markdown hyperlinks work: `See [D-010](architecture.md#d-010)` (relative link within domain file) or `See [D-024](content.md#d-024)` (cross-domain link)
|
||||
- Or query index: `sqlite-query "SELECT target_id, ref_type FROM decision_refs WHERE source_id='D-032'"`
|
||||
|
||||
**Ticket linkage:**
|
||||
- Existing: Tickets reference decisions via `decision_ref` column
|
||||
- New: Query which tickets implement a decision: `sqlite-query "SELECT id, title, status FROM tickets WHERE decision_ref='D-020'"`
|
||||
- New: Query which decisions have no linked tickets: `sqlite-query "SELECT id, title FROM decision_index WHERE type='decision' AND id NOT IN (SELECT DISTINCT decision_ref FROM tickets WHERE decision_ref LIKE 'D-%')"`
|
||||
|
||||
### 3.5 Briefing Update Strategy
|
||||
|
||||
Current briefings duplicate key decisions as freeform text. New approach:
|
||||
|
||||
**Per-agent briefing structure:**
|
||||
- Section: "Decisions Relevant to Your Role"
|
||||
- Content: "See `docs/decisions/{domain}.md` for {domain} decisions. Key decisions: D-010 (client-server), D-020 (engine), D-030 (testability)."
|
||||
- Optionally: 1-2 sentence summary per key decision (but NOT full text — that's in the domain file)
|
||||
|
||||
**Example (Tyre's briefing, updated):**
|
||||
```markdown
|
||||
## Decisions Relevant to Your Role
|
||||
See `docs/decisions/architecture.md` for technical architecture decisions. Key decisions:
|
||||
- **D-010:** Four architectural principles (client-server, information boundaries, deterministic sim)
|
||||
- **D-020:** Godot client + Rust/bevy_ecs server via subprocess/IPC, MessagePack serialization
|
||||
- **D-030:** Testability architecture (hybrid test org, gdUnit4, deterministic replay)
|
||||
|
||||
See `docs/decisions/camera-perception.md` for perception/rendering decisions. Key decisions:
|
||||
- **D-011:** LOS shadowcasting for fog of war
|
||||
- **D-017:** Perception modes as observer queries (pattern for all future systems)
|
||||
|
||||
See `docs/decisions/content.md` for NPC/simulation decisions. Key decisions:
|
||||
- **D-024:** CombatCapability as ECS component (pattern extends to future capabilities)
|
||||
- **D-026:** Simulation tier budgets (Active: 30-80 NPCs, Background: 500-2K)
|
||||
```
|
||||
|
||||
**Benefit:** Briefing is now 15-25 lines of pointers, not 80-120 lines of duplicated decision text. When D-032 is added to `architecture.md`, I update Tyre's briefing with one line: "D-032: Foo system." That's it.
|
||||
|
||||
### 3.6 Superseded Decisions
|
||||
|
||||
**Current problem:** D-002 and D-006 are tombstones — no detail visible in DECISIONS.md.
|
||||
|
||||
**New approach:**
|
||||
1. D-002 remains in `docs/decisions/scope.md` with full original text, marked:
|
||||
```markdown
|
||||
### D-002: Dynasty grand strategy concept [SUPERSEDED]
|
||||
- **Date:** 2026-02-08
|
||||
- **Decision:** [Original text here]
|
||||
- **Superseded by:** D-005 (first-person single-character concept)
|
||||
- **Rationale:** [Why we moved away from this]
|
||||
```
|
||||
2. Index DB marks: `status='superseded'`
|
||||
3. Index DB links: `INSERT INTO decision_refs (source_id='D-005', target_id='D-002', ref_type='supersedes')`
|
||||
4. Queries default to active: `WHERE status='active'`
|
||||
5. Historical queries include superseded: `WHERE status IN ('active', 'superseded')`
|
||||
|
||||
**Benefit:** Agents can read the original decision, understand why it was superseded, compare with the replacement. Git history no longer required. Archival integrity preserved.
|
||||
|
||||
---
|
||||
|
||||
## 4. Migration Path
|
||||
|
||||
### Phase 1: Split Markdown (2 hours)
|
||||
|
||||
1. Create `docs/decisions/` directory
|
||||
2. Split current DECISIONS.md into domain files:
|
||||
- `architecture.md` ← D-010, D-012, D-020, D-030
|
||||
- `content.md` ← D-023, D-024, D-025, D-028, D-029
|
||||
- `camera-perception.md` ← D-011, D-015, D-016, D-017, D-018, D-019
|
||||
- `scope.md` ← D-001, D-003, D-005, D-006, D-007, D-014, D-027
|
||||
- `world.md` ← D-013, D-031
|
||||
- `combat.md` ← D-008
|
||||
- `process.md` ← D-022
|
||||
- `meta.md` ← D-004, D-021
|
||||
- `multiplayer.md` ← D-009
|
||||
- `questions.md` ← Q-001 through Q-011
|
||||
- `rejected.md` ← R-001 through R-010
|
||||
3. Add markdown header to each file:
|
||||
```markdown
|
||||
# {Domain} Decisions
|
||||
|
||||
This file contains decisions related to {domain}. For the full decision archive, see [decisions README](README.md).
|
||||
|
||||
Last updated: 2026-02-11
|
||||
```
|
||||
4. Add relative hyperlinks for cross-references:
|
||||
- D-024 references D-017 → `See [D-017](camera-perception.md#d-017-perception-modes-as-character-build-system)`
|
||||
5. Create `docs/decisions/README.md`:
|
||||
- Index of all domain files
|
||||
- Link to discussion rounds (`docs/discussions/README.md`)
|
||||
- Explanation of structure
|
||||
6. Archive current DECISIONS.md to `docs/decisions/archive/DECISIONS-monolithic.md` (preserve git history)
|
||||
7. Replace root `DECISIONS.md` with redirect file:
|
||||
```markdown
|
||||
# Decisions Archive
|
||||
|
||||
Decisions have been reorganized by domain. See:
|
||||
- [docs/decisions/README.md](docs/decisions/README.md) for the domain index
|
||||
- Individual domain files in `docs/decisions/`
|
||||
|
||||
This file archived 2026-02-11. See `docs/decisions/archive/DECISIONS-monolithic.md` for the original single-file format.
|
||||
```
|
||||
|
||||
### Phase 2: Seed Index DB (1.5 hours)
|
||||
|
||||
1. Create `db/schema-decisions.sql`:
|
||||
```sql
|
||||
-- Decision index schema (see Section 3.3 for full schema)
|
||||
CREATE TABLE decision_index (...);
|
||||
CREATE TABLE decision_refs (...);
|
||||
CREATE INDEX ...;
|
||||
```
|
||||
2. Apply schema to `db/commonwealth.db`:
|
||||
```bash
|
||||
sqlite3 db/commonwealth.db < db/schema-decisions.sql
|
||||
```
|
||||
3. Seed `decision_index` table with 31 decisions + 11 questions + 10 rejected:
|
||||
- Write script: `db/scripts/seed-decision-index.py` (reads domain files, extracts metadata, inserts into DB)
|
||||
- Or manual SQL inserts (tedious but doable for 52 entries)
|
||||
4. Seed `decision_refs` table with known cross-references:
|
||||
- D-005 supersedes D-002
|
||||
- D-027 supersedes D-006
|
||||
- D-031 resolves Q-009
|
||||
- D-024 references D-017
|
||||
- D-030 references D-010
|
||||
- (Scan decision text for "See D-NNN" and "References D-NNN" patterns)
|
||||
5. Validate: `sqlite-query "SELECT COUNT(*) FROM decision_index"` → should return 52
|
||||
|
||||
### Phase 3: Update Briefings (1 hour)
|
||||
|
||||
1. Update all 8 agent briefings:
|
||||
- Replace "Read DECISIONS.md" with "See docs/decisions/{domain}.md"
|
||||
- Condense decision references to 1-2 lines per decision (ID + title only, not full text)
|
||||
2. Update my briefing (`docs/briefings/qatux.md`):
|
||||
- Document new workflow (add decision → edit domain file + insert into DB)
|
||||
- Update priorities (maintain domain files, not monolithic file)
|
||||
|
||||
### Phase 4: Re-index Qdrant (30 minutes)
|
||||
|
||||
1. Remove old DECISIONS.md from Qdrant:
|
||||
```bash
|
||||
db/connectors/qdrant-connector.py delete-file docs/DECISIONS.md
|
||||
```
|
||||
2. Index all domain files:
|
||||
```bash
|
||||
for file in docs/decisions/*.md; do
|
||||
db/connectors/qdrant-index "$file"
|
||||
done
|
||||
```
|
||||
3. Verify: `db/connectors/qdrant-count` → should see ~45-55 chunks (11 domain files × 4-5 chunks each)
|
||||
|
||||
### Phase 5: Commit and Announce (15 minutes)
|
||||
|
||||
1. Commit all changes:
|
||||
```bash
|
||||
git add docs/decisions/ docs/briefings/ db/commonwealth.db db/schema-decisions.sql
|
||||
git commit -m "docs(decisions): restructure into domain files with SQLite index
|
||||
|
||||
- Split DECISIONS.md into 11 domain files (architecture, content, scope, etc.)
|
||||
- Add decision_index and decision_refs tables to commonwealth.db
|
||||
- Update agent briefings to reference domain files
|
||||
- Re-index domain files in Qdrant
|
||||
- Archive original DECISIONS.md to docs/decisions/archive/
|
||||
|
||||
Rationale: Reduce per-agent context load from 474 lines to ~80-150 lines (domain-specific).
|
||||
Enables structured cross-references, ticket linkage queries, and faster Qdrant re-indexing.
|
||||
|
||||
See docs/decisions/README.md for new structure."
|
||||
```
|
||||
2. Announce to team (create discussion round or brief in next team session):
|
||||
- Explain new structure
|
||||
- Update CLAUDE.md with new instructions (if needed)
|
||||
- Demonstrate query examples
|
||||
|
||||
### Phase 6: Validation and Rollback Plan (if needed)
|
||||
|
||||
**Validation checklist:**
|
||||
- [ ] All 31 decisions, 11 questions, 10 rejected alternatives present in domain files
|
||||
- [ ] All cross-references converted to hyperlinks
|
||||
- [ ] Index DB contains 52 rows in `decision_index`
|
||||
- [ ] Known cross-references in `decision_refs` (at least 5-8)
|
||||
- [ ] All agent briefings updated
|
||||
- [ ] Qdrant contains ~45-55 chunks from domain files
|
||||
- [ ] Qdrant search for "client-server" returns D-010 and D-020
|
||||
- [ ] SQLite query `SELECT * FROM decision_index WHERE domain='architecture'` returns 4 rows
|
||||
|
||||
**Rollback plan (if validation fails):**
|
||||
1. Restore `docs/DECISIONS.md` from `docs/decisions/archive/DECISIONS-monolithic.md`
|
||||
2. Delete `docs/decisions/` directory (except archive)
|
||||
3. Drop tables: `sqlite-exec "DROP TABLE decision_refs; DROP TABLE decision_index;"`
|
||||
4. Revert briefings: `git checkout HEAD~1 docs/briefings/`
|
||||
5. Re-index old DECISIONS.md: `qdrant-index docs/DECISIONS.md`
|
||||
6. Total rollback time: ~15 minutes
|
||||
|
||||
**Low risk:** All data preserved in git history and archive. No information loss.
|
||||
|
||||
---
|
||||
|
||||
## 5. How This Works with My Workflow
|
||||
|
||||
### 5.1 Documentation Workflow
|
||||
|
||||
**Current (monolithic DECISIONS.md):**
|
||||
1. Discussion round produces decision (e.g., D-032)
|
||||
2. I edit DECISIONS.md (scroll to end of CONFIRMED DECISIONS section, insert D-032)
|
||||
3. I commit DECISIONS.md
|
||||
4. I re-index DECISIONS.md in Qdrant (~30-45 seconds)
|
||||
5. I update 2-4 relevant agent briefings (duplicate D-032 summary in each)
|
||||
6. I commit briefings
|
||||
7. **Total:** 3 file edits (DECISIONS.md + 2-4 briefings), 2 commits, 1 Qdrant re-index
|
||||
|
||||
**New (domain files + index DB):**
|
||||
1. Discussion round produces decision (e.g., D-032, domain = architecture)
|
||||
2. I edit `docs/decisions/architecture.md` (add D-032 in markdown)
|
||||
3. I insert into index DB: `sqlite-exec "INSERT INTO decision_index (...) VALUES (...)"`
|
||||
4. I insert cross-refs (if any): `sqlite-exec "INSERT INTO decision_refs (...) VALUES (...)"`
|
||||
5. I re-index domain file in Qdrant (~10 seconds): `qdrant-index docs/decisions/architecture.md`
|
||||
6. I update Tyre's briefing (add one line: "D-032: Foo system")
|
||||
7. I commit: `git add docs/decisions/architecture.md db/commonwealth.db docs/briefings/tyre.md && git commit`
|
||||
8. **Total:** 3 file edits (domain file + DB + 1 briefing), 1 commit, 1 Qdrant re-index
|
||||
|
||||
**Comparison:**
|
||||
- File edits: same (3)
|
||||
- Commits: reduced (1 vs 2)
|
||||
- Qdrant re-index time: 3x faster (10s vs 30-45s)
|
||||
- Briefing updates: reduced (1 vs 2-4) — because briefings point to domain files, not duplicate full text
|
||||
- Context lines read by agents next session: 80-150 (domain) vs 474+ (monolithic)
|
||||
|
||||
**My assessment:** Slightly more ceremony (DB insert), but net time savings due to faster re-indexing and fewer briefing updates. And agents benefit massively (70-80% context reduction).
|
||||
|
||||
### 5.2 Qdrant Indexing
|
||||
|
||||
**Current:** Re-index 47KB DECISIONS.md → ~15-20 chunks → 30-45 seconds.
|
||||
|
||||
**New:** Re-index 5-8KB domain file → ~3-5 chunks → 10 seconds.
|
||||
|
||||
**Benefit:** 3x faster per change. Over time, this compounds — 20 decision additions per month × 3x speedup = ~10 minutes saved per month. Not huge, but appreciated.
|
||||
|
||||
**Search quality:** Domain-scoped chunks reduce noise. A search for "NPC generation" returns chunks from `content.md` only, not mixed with architecture/combat/etc. Agent gets cleaner, more relevant results.
|
||||
|
||||
### 5.3 Briefing Maintenance
|
||||
|
||||
**Current:** Briefings duplicate 5-10 decision summaries each (40-80 lines per briefing). When D-032 is added, I update 2-4 briefings with full 8-10 line decision excerpt.
|
||||
|
||||
**New:** Briefings point to domain files with 1-line references. When D-032 is added, I update 1 briefing (Tyre's) with one line: "D-032: Foo system." If another agent needs it later, I add one line to their briefing too. But most decisions don't require broad briefing updates — only domain-relevant agents care.
|
||||
|
||||
**Time saved:** ~15-20 minutes per workshop round (fewer briefing edits, shorter diffs).
|
||||
|
||||
### 5.4 Cross-Reference Retrieval
|
||||
|
||||
**Current:** Agent asks me "which decisions reference D-010?" → I grep DECISIONS.md manually → tell agent.
|
||||
|
||||
**New:** Agent queries directly: `sqlite-query "SELECT source_id, ref_type FROM decision_refs WHERE target_id='D-010'"` → gets answer in 50ms. Or I query and respond. Either way, structured query beats manual grep.
|
||||
|
||||
**Benefit:** I'm no longer a bottleneck for cross-reference questions. Agents can self-serve.
|
||||
|
||||
---
|
||||
|
||||
## 6. Future Enhancements (Not MVP)
|
||||
|
||||
These are nice-to-haves, not required for initial migration:
|
||||
|
||||
### 6.1 Wrapper Script for Adding Decisions
|
||||
|
||||
`decision-add` CLI tool:
|
||||
```bash
|
||||
decision-add D-032 \
|
||||
--domain architecture \
|
||||
--title "Foo system" \
|
||||
--decision "We will use X because Y" \
|
||||
--rationale "Rationale here" \
|
||||
--raised-by "Tyre" \
|
||||
--round 19 \
|
||||
--references D-010 \
|
||||
--dissent "None"
|
||||
```
|
||||
|
||||
Generates markdown template, inserts into DB, re-indexes Qdrant, optionally updates briefings. Reduces ceremony from 7 steps to 1 command.
|
||||
|
||||
**Effort:** ~3-4 hours to build. **Priority:** Medium. Nice quality-of-life improvement, but not blocking.
|
||||
|
||||
### 6.2 Decision Linter
|
||||
|
||||
`decision-lint` script:
|
||||
- Reads all markdown files in `docs/decisions/`
|
||||
- Extracts decision IDs (D-NNN, Q-NNN, R-NNN)
|
||||
- Compares with `decision_index` table
|
||||
- Reports mismatches:
|
||||
- IDs in markdown but not in DB
|
||||
- IDs in DB but not in markdown
|
||||
- Cross-references to nonexistent decisions
|
||||
- Tickets referencing nonexistent decisions
|
||||
|
||||
Run as git pre-commit hook or CI check. Prevents index drift.
|
||||
|
||||
**Effort:** ~2-3 hours. **Priority:** Medium-high. Prevents maintenance errors.
|
||||
|
||||
### 6.3 Auto-Generated Domain README
|
||||
|
||||
Script generates `docs/decisions/README.md` from index DB:
|
||||
- Lists all domains with decision counts
|
||||
- Links to domain files
|
||||
- Summary stats (31 decisions, 11 open questions, 10 rejected, etc.)
|
||||
- Recent changes (last 5 decisions added)
|
||||
|
||||
**Effort:** ~1-2 hours. **Priority:** Low. Nice-to-have, not critical.
|
||||
|
||||
### 6.4 Decision Timeline View
|
||||
|
||||
Generate chronological view from index DB:
|
||||
```bash
|
||||
decision-timeline --round 17
|
||||
```
|
||||
Output:
|
||||
```
|
||||
Round 17 (2026-02-10): Content Architecture
|
||||
- D-023: Three-tier content model (content)
|
||||
- D-024: NPC generation model (content)
|
||||
- D-025: Social site / functional cluster (content)
|
||||
- D-026: Simulation tiers (architecture)
|
||||
- D-027: Vertical slice (scope)
|
||||
- D-028: Dialogue architecture (content)
|
||||
- D-029: Population entanglement (content)
|
||||
```
|
||||
|
||||
Restores the "what did we decide in order?" view lost by splitting into domain files.
|
||||
|
||||
**Effort:** ~1 hour. **Priority:** Low. Discussions archive already provides this (see `docs/discussions/README.md`).
|
||||
|
||||
### 6.5 Decision-Ticket Linkage Report
|
||||
|
||||
Generate report: "Which decisions have no linked tickets?"
|
||||
```bash
|
||||
decision-ticket-report
|
||||
```
|
||||
Output:
|
||||
```
|
||||
Decisions without linked tickets:
|
||||
- D-013: Insert/POI navigation system
|
||||
- D-016: Internal monologue
|
||||
- D-018: Three-range sound model
|
||||
- D-021: Official title
|
||||
- D-029: Population entanglement ratio
|
||||
|
||||
Total: 5 / 31 decisions (16%) lack implementation tickets.
|
||||
```
|
||||
|
||||
Helps identify decisions that haven't been actioned yet.
|
||||
|
||||
**Effort:** ~30 minutes (simple SQL query + formatting). **Priority:** Medium. Useful for planning.
|
||||
|
||||
---
|
||||
|
||||
## 7. Open Questions for Team Review
|
||||
|
||||
Before proceeding with migration, I recommend team review on:
|
||||
|
||||
1. **Domain taxonomy:** Does the proposed 11-domain split (architecture, content, camera-perception, scope, world, combat, process, meta, multiplayer, questions, rejected) make sense? Should any domains be merged or split differently?
|
||||
|
||||
2. **Cross-domain decisions:** Some decisions span domains (e.g., D-026 simulation tiers touches both architecture and content). How should we handle this? Options:
|
||||
- Put in primary domain (architecture) with cross-reference note in content file
|
||||
- Duplicate in both files (violates DRY, but improves discoverability)
|
||||
- Create "cross-domain" file for decisions that don't fit cleanly (13th file, not ideal)
|
||||
|
||||
3. **Wrapper script priority:** Should I build `decision-add` wrapper (6.1) before or after migration? Before = smoother migration. After = faster to production.
|
||||
|
||||
4. **Linter as pre-commit hook:** Should `decision-lint` (6.2) block commits if index/markdown drift detected? Or just warn?
|
||||
|
||||
5. **Ticket-decision linkage:** Should I audit and link all 31 decisions to existing tickets during migration? Or defer as ongoing maintenance?
|
||||
|
||||
---
|
||||
|
||||
## 8. Conclusion
|
||||
|
||||
The hybrid domain-split + DB index approach solves the immediate problem (context window bloat) and the long-term problems (cross-referencing, ticket linkage, discoverability, maintenance burden) without sacrificing the human-friendliness of markdown or the auditability of git history.
|
||||
|
||||
**My recommendation:** Proceed with migration. Estimated effort: ~4 hours (split markdown, seed DB, update briefings, re-index Qdrant). Risk: low (rollback path clear, no data loss). Benefit: 70-80% reduction in agent context load, 3x faster Qdrant re-indexing, structured cross-references, and a foundation for future scaling to 100+ decisions.
|
||||
|
||||
**For the record:** This is the right long-term structure. We should implement it now, before DECISIONS.md reaches 1000 lines and the migration becomes painful.
|
||||
|
||||
**Next steps (pending team approval):**
|
||||
1. Review this analysis with Team Leader (Jeroen)
|
||||
2. Finalize domain taxonomy (adjust if needed)
|
||||
3. Execute migration (Phase 1-6, ~4 hours)
|
||||
4. Validate and announce
|
||||
5. Build wrapper script and linter (Phase 6.1-6.2, future enhancement)
|
||||
|
||||
---
|
||||
|
||||
**Document ends.**
|
||||
For questions or clarifications, ask Qatux.
|
||||
@@ -0,0 +1,797 @@
|
||||
# DECISIONS.md Restructure Analysis — Project Management Perspective
|
||||
|
||||
**Author:** Si (Project Manager / Scrum Master)
|
||||
**Date:** 2026-02-11
|
||||
**Status:** Analysis for Team Leader review
|
||||
**Context:** DECISIONS.md has grown to 474 lines (31 confirmed, 11 open questions, 10 rejected alternatives) and projects to 1000+ lines within 6-12 months at current velocity
|
||||
|
||||
---
|
||||
|
||||
## 1. Problems With Current Approach at Scale
|
||||
|
||||
### 1.1 Context Window Tax
|
||||
Every agent reads DECISIONS.md at session start. At 474 lines, that's acceptable. At 1000+ lines, agents burn 15-20% of context budget on decisions they don't need. Example: Tyre reads D-028 (dialogue architecture) but only needs D-010, D-020, D-030. Paula reads D-012 (chunk-based maps) but only needs D-005, D-013, D-016, D-024, D-028.
|
||||
|
||||
Briefings already duplicate key decisions per agent, creating a two-tier system where DECISIONS.md is "source of truth" but briefings are "what agents actually read." This pattern breaks down as duplication overhead increases.
|
||||
|
||||
### 1.2 Supersession Creates Noise
|
||||
D-002 superseded by D-005. D-006 superseded by D-027. At 100+ decisions, 20-30% will be superseded. Agents reading chronologically hit dead decisions before finding current ones. Cross-references compound: D-027 supersedes D-006, which was already context for D-005.
|
||||
|
||||
Current format requires agents to read superseded decisions to understand why they were superseded. Linear document structure doesn't support "latest version of this decision thread."
|
||||
|
||||
### 1.3 Discovery Requires Full Read
|
||||
Agent starting work on dialogue system must read all 474 lines to find D-028. Grep for "dialogue" works if you know what to search for. Semantic search via Qdrant works but requires the document to be indexed and assumes the agent knows to search rather than read.
|
||||
|
||||
No decision taxonomy. "Architecture" decisions (D-010, D-020, D-030) mixed with "content" decisions (D-024, D-028, D-029) mixed with "scope" decisions (D-006, D-027). An agent asking "what are the architecture decisions?" must read everything.
|
||||
|
||||
### 1.4 Ticket-to-Decision Linkage is One-Way
|
||||
Tickets have `decision_ref` pointing to decisions. Decisions don't list which tickets implement them. Traceability breaks: I can see ticket #138 implements D-010 principle 2, but I can't see all tickets implementing D-010 without querying the database.
|
||||
|
||||
When planning a sprint to implement D-028, I must:
|
||||
1. Read D-028
|
||||
2. Query tickets WHERE decision_ref='D-028'
|
||||
3. Manually check for tickets implementing dependencies (D-024, D-025)
|
||||
4. Cross-reference with agent briefings to see who cares about D-028
|
||||
|
||||
This workflow doesn't scale.
|
||||
|
||||
### 1.5 Decision Domains Aren't Explicit
|
||||
Decisions implicitly cluster into domains:
|
||||
- **Architecture:** D-010, D-020, D-030 (Tyre's domain)
|
||||
- **Content:** D-023, D-024, D-025, D-028, D-029 (Gestalt/Paula/Mellanie)
|
||||
- **Perception/Camera:** D-011, D-015, D-017, D-019 (Tyre/Gestalt)
|
||||
- **Scope/Planning:** D-006, D-014, D-027 (Team Leader/Si)
|
||||
- **Process:** D-022 (meta)
|
||||
|
||||
These domains aren't surfaced in DECISIONS.md. An agent working on perception systems must read all decisions to find the 5 they need.
|
||||
|
||||
### 1.6 Velocity Projection
|
||||
Current rate: ~10 decisions every 2-3 rounds. Rounds every 2-4 days. Projection:
|
||||
- 3 months: 60-80 decisions
|
||||
- 6 months: 100-120 decisions
|
||||
- 12 months: 150-200 decisions
|
||||
|
||||
At 150 decisions × 15 lines average = 2250 lines. Unreadable. Linear format breaks.
|
||||
|
||||
---
|
||||
|
||||
## 2. Options Analysis
|
||||
|
||||
### Option A: Split by Domain (Multiple Files)
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
decisions/
|
||||
architecture.md # D-010, D-020, D-030
|
||||
content.md # D-023, D-024, D-025, D-028, D-029
|
||||
perception.md # D-011, D-015, D-017, D-019
|
||||
scope.md # D-006, D-014, D-027
|
||||
process.md # D-022
|
||||
questions.md # Q-001 through Q-011
|
||||
rejected.md # R-001 through R-010
|
||||
```
|
||||
|
||||
**How it intersects with ticketing:**
|
||||
- Tickets still have `decision_ref` field (unchanged)
|
||||
- Domain files map to agent specializations (Tyre reads architecture.md, Paula reads content.md)
|
||||
- Sprint planning: "This sprint implements perception systems" → read perception.md + query tickets WHERE decision_ref IN (D-011, D-015, D-017, D-019)
|
||||
|
||||
**Workflow for Si creating tickets:**
|
||||
1. Decision made in workshop → documented in domain file
|
||||
2. Create initiative ticket with decision_ref
|
||||
3. Break into epics/stories with same decision_ref
|
||||
4. Agent reads their domain file at session start (~50-100 lines vs 474+)
|
||||
|
||||
**Pros:**
|
||||
- Context-efficient: agents read only their domains
|
||||
- Git-friendly: changes to content.md don't trigger diffs in architecture.md
|
||||
- Natural fit for agent specialization
|
||||
- Scales to 200+ decisions without file size explosion
|
||||
|
||||
**Cons:**
|
||||
- Cross-domain decisions require taxonomy judgment (where does D-013 POI system go? Content or perception?)
|
||||
- Cross-references span files ("D-028 depends on D-024" requires reading two files)
|
||||
- No single source for "all current decisions"
|
||||
- Discovery across domains still requires knowing which file to read
|
||||
|
||||
**Migration path:**
|
||||
1. Create decisions/ directory
|
||||
2. Split DECISIONS.md by domain (one-time editorial pass)
|
||||
3. Update CLAUDE.md to point to decisions/ directory
|
||||
4. Update agent briefings to reference specific domain files
|
||||
5. Archive DECISIONS.md as decisions/archive/DECISIONS-monolith-2026-02-11.md
|
||||
|
||||
---
|
||||
|
||||
### Option B: Move to Database (SQLite)
|
||||
|
||||
**Structure:**
|
||||
```sql
|
||||
CREATE TABLE decisions (
|
||||
id TEXT PRIMARY KEY, -- 'D-031', 'Q-009', 'R-010'
|
||||
type TEXT CHECK(type IN ('confirmed', 'question', 'rejected')),
|
||||
title TEXT NOT NULL,
|
||||
decision TEXT NOT NULL, -- Full decision content
|
||||
rationale TEXT,
|
||||
raised_by TEXT,
|
||||
date TEXT,
|
||||
supersedes TEXT, -- 'D-006'
|
||||
superseded_by TEXT, -- 'D-027'
|
||||
domain TEXT, -- 'architecture', 'content', 'perception'
|
||||
status TEXT DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'resolved'))
|
||||
);
|
||||
|
||||
CREATE TABLE decision_tags (
|
||||
decision_id TEXT REFERENCES decisions(id),
|
||||
tag TEXT, -- 'architecture', 'multiplayer', 'v0.1'
|
||||
PRIMARY KEY (decision_id, tag)
|
||||
);
|
||||
|
||||
CREATE TABLE decision_refs (
|
||||
decision_id TEXT REFERENCES decisions(id),
|
||||
references TEXT, -- 'D-010', 'D-024'
|
||||
PRIMARY KEY (decision_id, references)
|
||||
);
|
||||
```
|
||||
|
||||
**How it intersects with ticketing:**
|
||||
- Decisions and tickets in same database
|
||||
- Query: "Show me all tickets implementing architecture decisions"
|
||||
```sql
|
||||
SELECT t.* FROM tickets t
|
||||
JOIN decisions d ON t.decision_ref = d.id
|
||||
WHERE d.domain = 'architecture' AND d.status = 'active'
|
||||
```
|
||||
- Bidirectional traceability: "Show me all decisions with no implementing tickets"
|
||||
- Dependency graph: "Show me all decisions D-028 depends on (direct + transitive)"
|
||||
|
||||
**Workflow for Si creating tickets:**
|
||||
1. Decision made in workshop
|
||||
2. Insert into decisions table with domain tags
|
||||
3. Create ticket with decision_ref
|
||||
4. Query: "What decisions have tickets in backlog?" to prioritize sprints
|
||||
|
||||
**Pros:**
|
||||
- Queryable: agents can ask "show me active architecture decisions" via connector
|
||||
- Bidirectional traceability: decision → tickets and tickets → decision
|
||||
- Supersession handled via status field (filter WHERE status='active')
|
||||
- No context window tax: agents query for what they need
|
||||
- Cross-reference graph is queryable (transitive dependencies)
|
||||
- Integrates with existing ticket workflow (same database)
|
||||
|
||||
**Cons:**
|
||||
- Not git-friendly: decisions are data, not documents
|
||||
- Diff tracking requires custom tooling (export snapshots to git?)
|
||||
- Loses markdown formatting richness (code blocks, lists, tables)
|
||||
- Requires CLI wrapper for agent access (sqlite-query "SELECT...")
|
||||
- No semantic search (Qdrant can't index database rows)
|
||||
- Human readability suffers (agents prefer reading markdown to SQL output)
|
||||
|
||||
**Migration path:**
|
||||
1. Extend schema.sql with decisions tables
|
||||
2. Write migration script to parse DECISIONS.md → INSERT statements
|
||||
3. Update /ticket skill to query both tickets and decisions
|
||||
4. Export active decisions to decisions/current.md on every change (git tracking)
|
||||
5. Agents query database for work, read exported markdown for context
|
||||
|
||||
---
|
||||
|
||||
### Option C: Hybrid — Domain Files + Database View Layer
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
decisions/
|
||||
architecture.md # Source of truth (git-tracked)
|
||||
content.md
|
||||
perception.md
|
||||
scope.md
|
||||
process.md
|
||||
questions.md
|
||||
rejected.md
|
||||
|
||||
db/
|
||||
commonwealth.db # SQLite with decisions view table
|
||||
|
||||
connectors/
|
||||
decisions-sync.py # Parses *.md → syncs to DB
|
||||
```
|
||||
|
||||
**decisions table schema:**
|
||||
```sql
|
||||
-- View layer, synced from markdown files
|
||||
CREATE TABLE decisions (
|
||||
id TEXT PRIMARY KEY,
|
||||
domain TEXT, -- derived from source file
|
||||
title TEXT,
|
||||
file_path TEXT, -- 'decisions/architecture.md'
|
||||
line_number INTEGER, -- position in source file
|
||||
status TEXT,
|
||||
supersedes TEXT,
|
||||
superseded_by TEXT,
|
||||
indexed_at TEXT
|
||||
);
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Domain markdown files are source of truth (human-editable, git-tracked)
|
||||
- Sync script parses files and populates decisions table
|
||||
- Agents read markdown for context (50-100 lines per domain)
|
||||
- Agents query database for relationships (tickets implementing X, dependencies of Y)
|
||||
- Qdrant indexes markdown files (semantic search works)
|
||||
|
||||
**How it intersects with ticketing:**
|
||||
- Best of both: readable source + queryable relationships
|
||||
- Query: "Show me all tickets implementing content decisions"
|
||||
```sql
|
||||
SELECT t.* FROM tickets t
|
||||
JOIN decisions d ON t.decision_ref = d.id
|
||||
WHERE d.domain = 'content' AND d.status = 'active'
|
||||
```
|
||||
- Sprint planning: Read content.md for context, query DB for ticket coverage
|
||||
|
||||
**Workflow for Si creating tickets:**
|
||||
1. Decision documented in domain markdown file
|
||||
2. Run decisions-sync to update database
|
||||
3. Create ticket with decision_ref
|
||||
4. Query database for sprint planning (decision coverage, dependencies)
|
||||
5. Agents read markdown for session context (efficient)
|
||||
|
||||
**Pros:**
|
||||
- Git-friendly: decisions are markdown (diffable, versionable)
|
||||
- Context-efficient: agents read domain files (~50-100 lines)
|
||||
- Queryable: database view enables relationship queries
|
||||
- Semantic search: Qdrant indexes markdown files
|
||||
- Human-readable source: markdown is the truth
|
||||
- No loss of formatting: code blocks, tables, links preserved
|
||||
- Bidirectional traceability via database queries
|
||||
|
||||
**Cons:**
|
||||
- Requires sync script (new tooling, must run on decision changes)
|
||||
- Sync can drift if markdown is edited without re-sync
|
||||
- Additional complexity vs pure markdown or pure database
|
||||
- Sync script is a new failure point
|
||||
|
||||
**Migration path:**
|
||||
1. Split DECISIONS.md by domain (Option A structure)
|
||||
2. Write decisions-sync.py to parse domain files
|
||||
3. Extend schema.sql with decisions view table
|
||||
4. Add sync to pre-commit hook or manual workflow
|
||||
5. Update /ticket skill to query decisions table for context
|
||||
|
||||
---
|
||||
|
||||
### Option D: Keep Monolith, Add Index and Views
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
DECISIONS.md # Monolith (unchanged)
|
||||
DECISIONS-INDEX.md # Auto-generated index by domain
|
||||
DECISIONS-ACTIVE.md # Auto-generated view (active decisions only)
|
||||
```
|
||||
|
||||
**Index format:**
|
||||
```markdown
|
||||
# Decisions Index
|
||||
|
||||
## Architecture
|
||||
- [D-010: Multiplayer-ready architectural baseline](DECISIONS.md#d-010)
|
||||
- [D-020: Engine selection — Godot + Rust](DECISIONS.md#d-020)
|
||||
- [D-030: Testability architecture](DECISIONS.md#d-030)
|
||||
|
||||
## Content
|
||||
- [D-023: Three-tier content model](DECISIONS.md#d-023)
|
||||
- [D-024: NPC generation model](DECISIONS.md#d-024)
|
||||
...
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- DECISIONS.md remains single source of truth
|
||||
- Script generates index grouped by domain (lightweight taxonomy)
|
||||
- Script generates DECISIONS-ACTIVE.md (filters superseded decisions)
|
||||
- Agents read index to find relevant decisions, jump to DECISIONS.md section
|
||||
|
||||
**Pros:**
|
||||
- Minimal change to current workflow
|
||||
- Git-friendly (generated files can be committed or .gitignored)
|
||||
- Index improves discovery without restructuring source
|
||||
|
||||
**Cons:**
|
||||
- Doesn't solve context window tax (agents still read full DECISIONS.md for detail)
|
||||
- Index generation requires taxonomy metadata in decisions (manual tagging)
|
||||
- No query capability (database relationships still missing)
|
||||
- Scales poorly beyond 200 decisions (index helps but monolith remains)
|
||||
|
||||
**Migration path:**
|
||||
1. Add domain tags to each decision (one-time editorial pass)
|
||||
2. Write index-generator script
|
||||
3. Add to pre-commit hook or manual workflow
|
||||
4. Agents read index first, DECISIONS.md for detail
|
||||
|
||||
---
|
||||
|
||||
## 3. Recommended Approach: Option C (Hybrid)
|
||||
|
||||
### 3.1 Rationale
|
||||
|
||||
The hybrid approach intersects optimally with how Si manages tickets and plans sprints:
|
||||
|
||||
**For sprint planning:**
|
||||
- Read domain file (content.md) to understand decisions in scope
|
||||
- Query database: "Show me all tickets implementing D-024, D-025, D-028"
|
||||
- Query database: "What decisions are implemented? What's in backlog?"
|
||||
- Create sprint plan with decision coverage metrics
|
||||
|
||||
**For ticket creation:**
|
||||
- Workshop produces decision → document in domain file
|
||||
- Sync to database
|
||||
- Create initiative ticket with decision_ref
|
||||
- Break into epics/stories (same decision_ref)
|
||||
- Database now shows decision → ticket graph
|
||||
|
||||
**For dependency tracking:**
|
||||
- Decision cross-references stored in markdown (human-readable)
|
||||
- Sync script parses references → decision_refs table
|
||||
- Query: "What decisions must be implemented before D-028?"
|
||||
- Query: "What tickets are blocked by incomplete D-024 implementation?"
|
||||
|
||||
**For agent coordination:**
|
||||
- Agents read their domain files at session start (50-100 lines, not 474)
|
||||
- Briefings reference domain files: "Read decisions/architecture.md for D-010, D-020, D-030"
|
||||
- Query capability means agents can ask "what decisions changed since last session?"
|
||||
|
||||
**For Team Leader:**
|
||||
- Git diffs show decision changes in readable markdown
|
||||
- Database view shows project-level metrics (decisions implemented, coverage %)
|
||||
- Semantic search via Qdrant works (indexes markdown files)
|
||||
|
||||
### 3.2 Why Not the Alternatives
|
||||
|
||||
**Why not Option A (domain files only)?**
|
||||
- Loses queryable relationships. Sprint planning requires manual cross-file reading.
|
||||
- No bidirectional traceability (decision → tickets requires manual search).
|
||||
|
||||
**Why not Option B (database only)?**
|
||||
- Not git-friendly. Decisions are design artifacts, not just data.
|
||||
- Loses markdown formatting richness.
|
||||
- No semantic search (Qdrant can't index SQL rows).
|
||||
- Agents must query for context vs. reading curated documents.
|
||||
|
||||
**Why not Option D (monolith + index)?**
|
||||
- Doesn't solve context window tax (index helps discovery but agents still read full file).
|
||||
- No query capability for ticket relationships.
|
||||
- Scales poorly beyond 200 decisions.
|
||||
|
||||
### 3.3 What Makes Hybrid Optimal for Si's Workflow
|
||||
|
||||
As Project Manager, my core workflows are:
|
||||
1. **Sprint planning:** Need to see decision coverage and ticket implementation status
|
||||
2. **Ticket creation:** Need to trace decisions → initiatives → epics → stories
|
||||
3. **Dependency tracking:** Need to understand decision dependencies and ticket blockers
|
||||
4. **Team coordination:** Need to point agents to relevant context efficiently
|
||||
5. **Status reporting:** Need to show progress metrics (decisions implemented, backlog burn-down)
|
||||
|
||||
Hybrid approach supports all five:
|
||||
1. Database query shows decision coverage per sprint
|
||||
2. decision_ref linkage works unchanged, database view adds reverse lookup
|
||||
3. decision_refs table enables dependency graph queries
|
||||
4. Domain files give agents efficient context (read content.md, not all 474 lines)
|
||||
5. Database enables metrics queries (% decisions with tickets, % tickets in backlog)
|
||||
|
||||
Pure markdown (Option A) fails on 1, 2, 5. Pure database (Option B) fails on 4. Monolith+index (Option D) fails on 1, 2, 3, 5.
|
||||
|
||||
---
|
||||
|
||||
## 4. Detailed Proposal: Hybrid Implementation
|
||||
|
||||
### 4.1 Directory Structure
|
||||
|
||||
```
|
||||
decisions/
|
||||
README.md # Overview, explains domain taxonomy
|
||||
architecture.md # D-010, D-020, D-030, etc.
|
||||
content.md # D-023, D-024, D-025, D-028, D-029
|
||||
perception.md # D-011, D-015, D-017, D-019
|
||||
scope-planning.md # D-006, D-014, D-027
|
||||
process-meta.md # D-022
|
||||
questions.md # Q-001 through Q-011 (active questions)
|
||||
rejected.md # R-001 through R-010 (rejected alternatives)
|
||||
archive/
|
||||
DECISIONS-monolith.md # Original monolith (historical reference)
|
||||
|
||||
db/
|
||||
schema.sql # Extended with decisions tables
|
||||
connectors/
|
||||
decisions-sync.py # Parses decisions/*.md → syncs to DB
|
||||
```
|
||||
|
||||
### 4.2 Domain Taxonomy
|
||||
|
||||
**Architecture** — Technical foundation decisions that constrain implementation
|
||||
- Infrastructure (client-server, ECS, serialization)
|
||||
- Engine and tooling choices
|
||||
- Performance budgets
|
||||
- Testability and determinism
|
||||
- Examples: D-010, D-020, D-030
|
||||
|
||||
**Content** — How narrative, NPCs, and world content are created
|
||||
- Content tiers and templates
|
||||
- NPC generation models
|
||||
- Dialogue systems
|
||||
- Population and entanglement
|
||||
- Examples: D-023, D-024, D-025, D-028, D-029
|
||||
|
||||
**Perception** — How the player observes and interacts with the world
|
||||
- Camera and viewport
|
||||
- Fog and line-of-sight
|
||||
- Perception modes
|
||||
- Sound propagation
|
||||
- Examples: D-011, D-015, D-017, D-018, D-019
|
||||
|
||||
**Scope/Planning** — What we're building and when
|
||||
- Prototype scope
|
||||
- Vertical slice definition
|
||||
- Map specifications
|
||||
- Milestone goals
|
||||
- Examples: D-006, D-014, D-027
|
||||
|
||||
**Process/Meta** — How the team works
|
||||
- Workflow decisions
|
||||
- Documentation practices
|
||||
- Collaboration protocols
|
||||
- Examples: D-022
|
||||
|
||||
**Core/Foundation** — High-level project direction (doesn't fit other domains)
|
||||
- Project concept
|
||||
- Design pillars
|
||||
- Multiplayer philosophy
|
||||
- Examples: D-001, D-003, D-005, D-007, D-008, D-009
|
||||
|
||||
### 4.3 Database Schema Extension
|
||||
|
||||
```sql
|
||||
-- decisions table (view layer, synced from markdown)
|
||||
CREATE TABLE decisions (
|
||||
id TEXT PRIMARY KEY, -- 'D-031', 'Q-009', 'R-010'
|
||||
type TEXT NOT NULL CHECK(type IN ('confirmed', 'question', 'rejected')),
|
||||
domain TEXT NOT NULL, -- 'architecture', 'content', etc.
|
||||
title TEXT NOT NULL,
|
||||
status TEXT DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'resolved')),
|
||||
supersedes TEXT, -- 'D-006'
|
||||
superseded_by TEXT, -- 'D-027'
|
||||
date TEXT,
|
||||
raised_by TEXT,
|
||||
file_path TEXT NOT NULL, -- 'decisions/architecture.md'
|
||||
line_start INTEGER, -- line number in source file
|
||||
line_end INTEGER,
|
||||
synced_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- decision cross-references (parsed from "See D-XXX" mentions)
|
||||
CREATE TABLE decision_refs (
|
||||
decision_id TEXT NOT NULL REFERENCES decisions(id),
|
||||
references TEXT NOT NULL, -- 'D-010', 'D-024'
|
||||
ref_type TEXT DEFAULT 'depends-on' CHECK(ref_type IN ('depends-on', 'related', 'supersedes')),
|
||||
PRIMARY KEY (decision_id, references)
|
||||
);
|
||||
|
||||
-- decision tags (flexible taxonomy)
|
||||
CREATE TABLE decision_tags (
|
||||
decision_id TEXT NOT NULL REFERENCES decisions(id),
|
||||
tag TEXT NOT NULL, -- 'v0.1', 'multiplayer', 'critical'
|
||||
PRIMARY KEY (decision_id, tag)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX idx_decisions_status ON decisions(status);
|
||||
CREATE INDEX idx_decision_refs_references ON decision_refs(references);
|
||||
```
|
||||
|
||||
### 4.4 Sync Script Specification
|
||||
|
||||
**Input:** decisions/*.md files
|
||||
**Output:** Populated decisions, decision_refs, decision_tags tables
|
||||
**Trigger:** Manual (`make decisions-sync`) or pre-commit hook
|
||||
|
||||
**Parsing logic:**
|
||||
```python
|
||||
# decisions-sync.py pseudo-code
|
||||
|
||||
for file in glob('decisions/*.md'):
|
||||
domain = derive_domain_from_filename(file) # architecture.md → 'architecture'
|
||||
|
||||
for decision_block in parse_markdown_sections(file):
|
||||
# Extract from heading: "### D-031: Time system — game clock and day phases"
|
||||
id = extract_id(decision_block.heading) # 'D-031'
|
||||
title = extract_title(decision_block.heading)
|
||||
|
||||
# Extract from content
|
||||
type = infer_type(id) # D-xxx → confirmed, Q-xxx → question, R-xxx → rejected
|
||||
date = extract_field(decision_block, 'Date:')
|
||||
raised_by = extract_field(decision_block, 'Raised by:')
|
||||
supersedes = extract_field(decision_block, 'Supersedes:')
|
||||
superseded_by = extract_field(decision_block, 'Superseded by:')
|
||||
|
||||
# Infer status
|
||||
if superseded_by:
|
||||
status = 'superseded'
|
||||
elif type == 'question' and has_resolution_link(decision_block):
|
||||
status = 'resolved'
|
||||
else:
|
||||
status = 'active'
|
||||
|
||||
# Store decision
|
||||
upsert_decision(id, type, domain, title, status, supersedes, superseded_by,
|
||||
date, raised_by, file, line_start, line_end)
|
||||
|
||||
# Parse cross-references (mentions of D-XXX, Q-XXX in content)
|
||||
refs = extract_decision_mentions(decision_block.content)
|
||||
for ref in refs:
|
||||
upsert_decision_ref(id, ref, infer_ref_type(ref, supersedes))
|
||||
|
||||
# Parse tags (could be explicit metadata or inferred)
|
||||
tags = extract_tags(decision_block) # e.g., from "Tags: v0.1, critical"
|
||||
for tag in tags:
|
||||
upsert_decision_tag(id, tag)
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- Warn if decision references non-existent decision
|
||||
- Warn if superseded decision is still marked active
|
||||
- Warn if decision has no domain file match
|
||||
|
||||
### 4.5 Workflow Changes
|
||||
|
||||
#### For Qatux (documenting decisions)
|
||||
1. Workshop produces decision
|
||||
2. Determine domain (architecture/content/perception/scope/process/core)
|
||||
3. Add decision to appropriate decisions/domain.md file
|
||||
4. Run `make decisions-sync` (or let pre-commit hook do it)
|
||||
5. Decision now queryable via database
|
||||
|
||||
#### For Si (creating tickets)
|
||||
1. Read domain file for context (e.g., decisions/content.md)
|
||||
2. Query: `SELECT * FROM tickets WHERE decision_ref='D-028'` to see existing coverage
|
||||
3. Create initiative ticket with decision_ref='D-028'
|
||||
4. Break into epics/stories with same decision_ref
|
||||
5. Query: `SELECT d.id, d.title, COUNT(t.id) as ticket_count FROM decisions d LEFT JOIN tickets t ON d.id = t.decision_ref WHERE d.domain='content' GROUP BY d.id` to see coverage
|
||||
|
||||
#### For agents (reading context)
|
||||
1. Briefing says "Read decisions/architecture.md for D-010, D-020, D-030"
|
||||
2. Agent reads 50-100 lines (not 474)
|
||||
3. If agent needs cross-domain context, briefing lists additional files
|
||||
4. Agent can query: `db/connectors/sqlite-query "SELECT * FROM decisions WHERE id='D-028'"` for on-demand lookup
|
||||
|
||||
#### For Team Leader (tracking progress)
|
||||
1. Query: "How many decisions have implementing tickets?"
|
||||
```sql
|
||||
SELECT d.domain,
|
||||
COUNT(DISTINCT d.id) as total_decisions,
|
||||
COUNT(DISTINCT t.decision_ref) as decisions_with_tickets,
|
||||
COUNT(t.id) as total_tickets
|
||||
FROM decisions d
|
||||
LEFT JOIN tickets t ON d.id = t.decision_ref
|
||||
WHERE d.status = 'active' AND d.type = 'confirmed'
|
||||
GROUP BY d.domain
|
||||
```
|
||||
2. Review git diffs on decisions/*.md to see what changed
|
||||
3. Semantic search via Qdrant: "What decisions relate to multiplayer?"
|
||||
|
||||
---
|
||||
|
||||
## 5. Migration Path
|
||||
|
||||
### Phase 1: Split Monolith (Week 1)
|
||||
1. Create decisions/ directory
|
||||
2. Create domain files with README explaining taxonomy
|
||||
3. Perform one-time editorial pass to split DECISIONS.md by domain
|
||||
- Architecture: D-010, D-020, D-030
|
||||
- Content: D-023, D-024, D-025, D-028, D-029
|
||||
- Perception: D-011, D-015, D-017, D-018, D-019
|
||||
- Scope: D-006, D-014, D-027
|
||||
- Process: D-022
|
||||
- Core: D-001, D-003, D-005, D-007, D-008, D-009
|
||||
- Questions: Q-001 through Q-011
|
||||
- Rejected: R-001 through R-010
|
||||
4. Archive DECISIONS.md to decisions/archive/DECISIONS-monolith-2026-02-11.md
|
||||
5. Update CLAUDE.md to reference decisions/ directory
|
||||
|
||||
**Validation:** Agents can read domain files and find all decisions that were in monolith
|
||||
|
||||
### Phase 2: Build Sync Infrastructure (Week 2)
|
||||
1. Extend db/schema.sql with decisions, decision_refs, decision_tags tables
|
||||
2. Write db/connectors/decisions-sync.py
|
||||
3. Run sync script manually to verify parsing
|
||||
4. Add `make decisions-sync` target to Makefile
|
||||
|
||||
**Validation:** After sync, query decisions table and verify all decisions are present with correct metadata
|
||||
|
||||
### Phase 3: Update Agent Workflows (Week 3)
|
||||
1. Update agent briefings to reference specific domain files
|
||||
- Tyre's briefing: "Read decisions/architecture.md for D-010, D-020, D-030"
|
||||
- Paula's briefing: "Read decisions/content.md for D-024, D-028, D-029"
|
||||
2. Update /ticket skill to query decisions table for context
|
||||
3. Document query patterns for Si in docs/WORKFLOW-SI.md
|
||||
|
||||
**Validation:** Agent sessions load <200 lines of decision context (vs 474 previously)
|
||||
|
||||
### Phase 4: Add Query Tooling (Week 4)
|
||||
1. Create db/connectors/decisions-query wrapper (similar to sqlite-query)
|
||||
2. Add common queries as make targets:
|
||||
- `make decisions-coverage` — show decision → ticket coverage by domain
|
||||
- `make decisions-active` — list active decisions
|
||||
- `make decisions-pending` — show decisions with no implementing tickets
|
||||
3. Update Si's workflow documentation with query recipes
|
||||
|
||||
**Validation:** Si can run queries to plan sprints without reading all domain files
|
||||
|
||||
### Phase 5: Integration and Refinement (Week 5+)
|
||||
1. Add pre-commit hook to run decisions-sync automatically
|
||||
2. Re-index decisions/*.md files in Qdrant for semantic search
|
||||
3. Monitor for drift (markdown edited without sync)
|
||||
4. Iterate on taxonomy if domains prove wrong
|
||||
|
||||
**Validation:** Team uses domain files + database queries as primary workflow
|
||||
|
||||
---
|
||||
|
||||
## 6. Success Metrics
|
||||
|
||||
How we know the restructure is working:
|
||||
|
||||
### Context Efficiency
|
||||
- **Before:** Agents read 474 lines of DECISIONS.md per session
|
||||
- **After:** Agents read 50-100 lines of domain file(s) per session
|
||||
- **Target:** 70% reduction in decision-context token usage
|
||||
|
||||
### Discovery Time
|
||||
- **Before:** Agent must read all decisions to find relevant ones (or grep if they know the term)
|
||||
- **After:** Agent reads domain file index (10-20 decisions) or queries database
|
||||
- **Target:** Agent finds relevant decision in <60 seconds
|
||||
|
||||
### Sprint Planning Efficiency
|
||||
- **Before:** Si manually reads DECISIONS.md, searches tickets, cross-references by hand
|
||||
- **After:** Si queries database for decision coverage, dependency graph, ticket status
|
||||
- **Target:** Sprint planning queries run in <5 seconds, return actionable data
|
||||
|
||||
### Decision Coverage Visibility
|
||||
- **Before:** Unknown how many decisions have implementing tickets without manual audit
|
||||
- **After:** Query shows decision → ticket coverage by domain
|
||||
- **Target:** Team Leader can see coverage metrics on demand
|
||||
|
||||
### Git Workflow Quality
|
||||
- **Before:** DECISIONS.md diffs show all decisions even if only one changed
|
||||
- **After:** Git diffs show only changed domain file (smaller, more focused diffs)
|
||||
- **Target:** PR reviews can see decision changes without noise
|
||||
|
||||
### Onboarding (Future)
|
||||
- **Before:** New agent reads 474-line monolith to understand project decisions
|
||||
- **After:** New agent reads core.md (high-level) + their domain file (specialized)
|
||||
- **Target:** Onboarding context reduced by 60-70%
|
||||
|
||||
---
|
||||
|
||||
## 7. Risks and Mitigations
|
||||
|
||||
### Risk: Sync Drift
|
||||
**Description:** Markdown files edited without running sync script. Database becomes stale.
|
||||
**Likelihood:** Medium (human error)
|
||||
**Impact:** High (query results are wrong)
|
||||
**Mitigation:**
|
||||
- Pre-commit hook runs sync automatically
|
||||
- CI checks that sync is current (hash markdown files, compare to synced_at timestamp)
|
||||
- Document sync requirement in CLAUDE.md and decisions/README.md
|
||||
|
||||
### Risk: Taxonomy Errors
|
||||
**Description:** Decision placed in wrong domain file. Agent reads wrong file, misses context.
|
||||
**Likelihood:** Low (taxonomy is fairly clear)
|
||||
**Impact:** Medium (agent confusion, duplicate work)
|
||||
**Mitigation:**
|
||||
- decisions/README.md documents taxonomy with examples
|
||||
- Cross-domain decisions can be documented in multiple files with reference links
|
||||
- Periodic taxonomy review (every 50 decisions) to adjust if needed
|
||||
|
||||
### Risk: Sync Script Bugs
|
||||
**Description:** Parser fails to extract metadata, creates corrupt database entries.
|
||||
**Likelihood:** Low (script is testable)
|
||||
**Impact:** High (query results are wrong)
|
||||
**Mitigation:**
|
||||
- Write unit tests for parser (test fixtures: sample decision blocks)
|
||||
- Sync script runs validation checks (warn on missing references, supersession conflicts)
|
||||
- Manual review of sync output on first 3 runs
|
||||
|
||||
### Risk: Query Complexity
|
||||
**Description:** Agents struggle with SQL queries, revert to reading markdown only.
|
||||
**Likelihood:** Medium (SQL is not natural language)
|
||||
**Impact:** Low (hybrid still works with markdown-only workflow)
|
||||
**Mitigation:**
|
||||
- Provide common queries as make targets (make decisions-coverage, make decisions-pending)
|
||||
- Document query recipes in Si's workflow guide
|
||||
- Agents can fall back to reading markdown (sync adds query capability, doesn't remove readable source)
|
||||
|
||||
### Risk: Qdrant Indexing Lag
|
||||
**Description:** Qdrant semantic search returns outdated results if domain files aren't re-indexed.
|
||||
**Likelihood:** Low (indexing is manual trigger)
|
||||
**Impact:** Medium (semantic search less useful)
|
||||
**Mitigation:**
|
||||
- Document re-indexing workflow in decisions/README.md
|
||||
- Add `make decisions-index` target to trigger Qdrant re-index
|
||||
- Re-index after each decision-producing workshop (manual checklist for Qatux)
|
||||
|
||||
---
|
||||
|
||||
## 8. Alternative: Incremental Hybrid (Lower Risk)
|
||||
|
||||
If full hybrid (Option C) feels too aggressive, an incremental path:
|
||||
|
||||
### Phase 1: Split by domain (Option A)
|
||||
- Immediate benefit: agents read smaller files
|
||||
- No new tooling required
|
||||
- Validates taxonomy before building sync infrastructure
|
||||
|
||||
### Phase 2: Add database view layer (complete Option C)
|
||||
- Once domain files prove stable, build sync script
|
||||
- Adds queryability without changing source format
|
||||
- Agents already familiar with domain files
|
||||
|
||||
This path reduces risk by deferring sync tooling until domain split is validated. Cost: sprint planning remains manual during Phase 1.
|
||||
|
||||
**Recommendation:** Proceed with full hybrid. Sync script is low-risk (parser is testable, database is queryable, markdown remains source of truth). Incremental path delays queryability benefits that Si needs for sprint planning.
|
||||
|
||||
---
|
||||
|
||||
## 9. Recommendation Summary
|
||||
|
||||
**Adopt Option C: Hybrid (domain files + database view layer)**
|
||||
|
||||
**Rationale:**
|
||||
- Context-efficient: agents read 50-100 lines per domain, not 474+ monolith
|
||||
- Git-friendly: decisions are markdown, diffable, versionable
|
||||
- Queryable: database view enables sprint planning queries, dependency graphs, coverage metrics
|
||||
- Semantic search: Qdrant indexes markdown files (unchanged workflow)
|
||||
- Scalable: works at 200+ decisions without explosion
|
||||
- Intersects optimally with Si's ticket management and sprint planning workflows
|
||||
|
||||
**Migration path:** 5-week phased rollout (split → sync → briefings → queries → integration)
|
||||
|
||||
**Success metrics:** 70% context reduction, <60s decision discovery, <5s sprint queries, on-demand coverage metrics
|
||||
|
||||
**Risks:** Sync drift (mitigated by pre-commit hook), taxonomy errors (mitigated by clear documentation), query complexity (mitigated by make targets and recipes)
|
||||
|
||||
---
|
||||
|
||||
## 10. Next Actions
|
||||
|
||||
### Immediate (this week)
|
||||
1. **Team Leader approval** on Option C (hybrid approach)
|
||||
2. **Qatux:** Create decisions/ directory structure and README
|
||||
3. **Si:** Perform editorial pass to split DECISIONS.md by domain taxonomy
|
||||
|
||||
### Week 2
|
||||
4. **Tyre:** Extend db/schema.sql with decisions tables
|
||||
5. **Tyre:** Build decisions-sync.py parser with validation
|
||||
6. **Si:** Run initial sync and verify database population
|
||||
|
||||
### Week 3
|
||||
7. **Qatux:** Update agent briefings to reference domain files
|
||||
8. **Si:** Update /ticket skill to query decisions table
|
||||
9. **Si:** Document query patterns in docs/WORKFLOW-SI.md
|
||||
|
||||
### Week 4
|
||||
10. **Tyre:** Add make targets for common queries (coverage, pending, active)
|
||||
11. **Si:** Test sprint planning workflow with queries
|
||||
12. **Team Leader:** Review git diff workflow on domain files
|
||||
|
||||
### Week 5+
|
||||
13. **Tyre:** Add pre-commit hook for decisions-sync
|
||||
14. **Qatux:** Re-index decisions/*.md in Qdrant
|
||||
15. **Team:** Monitor for drift and taxonomy adjustments
|
||||
|
||||
---
|
||||
|
||||
**End of analysis.**
|
||||
|
||||
**File locations:**
|
||||
- Analysis: `/var/home/jeroenschweitzer/Projects/commonwealth/docs/discussions/decisions-restructure-si.md`
|
||||
- Current monolith: `/var/home/jeroenschweitzer/Projects/commonwealth/DECISIONS.md` (474 lines)
|
||||
- Current schema: `/var/home/jeroenschweitzer/Projects/commonwealth/db/schema.sql`
|
||||
- Current tickets: 273 total (24 initiatives, 33 epics, 200 stories, 15 tasks, 1 bug)
|
||||
@@ -0,0 +1,472 @@
|
||||
# Decision Architecture Restructure — Technical Feasibility Review
|
||||
|
||||
**Author:** Tyre (Technical Architect)
|
||||
**Date:** 2026-02-11
|
||||
**Status:** Technical review of Si and Qatux proposals
|
||||
**Inputs reviewed:** `docs/discussions/decisions-restructure-si.md`, `docs/discussions/decisions-restructure-qatux.md`, `db/schema.sql`, `DECISIONS.md`, `Makefile`, `db/connectors/sqlite_connector.py`
|
||||
|
||||
---
|
||||
|
||||
## 0. Summary Verdict
|
||||
|
||||
Both proposals independently converge on Option C / 2.4: hybrid domain-split markdown + SQLite index. I endorse the hybrid approach. It is feasible, the sync script is tractable, and the architecture is sound. I have specific modifications to the schema, the domain taxonomy, and the sync strategy that reduce maintenance burden and eliminate the primary failure mode (drift).
|
||||
|
||||
Let me be honest about what this means technically: this is a documentation infrastructure project, not a game systems project. The engineering is straightforward. The hard part is taxonomy discipline and workflow adoption. I will focus my review on what breaks, what is harder than it sounds, and where we can simplify.
|
||||
|
||||
---
|
||||
|
||||
## 1. Feasibility Assessment — Sync Script and Parsing
|
||||
|
||||
### 1.1 Is the markdown format machine-parseable enough?
|
||||
|
||||
**Yes, with caveats.** Looking at the actual structure of DECISIONS.md, every decision follows a consistent pattern:
|
||||
|
||||
```markdown
|
||||
### D-NNN: Title text
|
||||
- **Date:** YYYY-MM-DD
|
||||
- **Decision:** Free text (may span multiple lines, contain sub-lists, tables, code blocks)
|
||||
- **Rationale:** Free text
|
||||
- **Raised by:** Agent names
|
||||
- **Dissent:** Free text or "None"
|
||||
```
|
||||
|
||||
This is parseable. The heading line (`### D-NNN: Title`) is the anchor — reliable, unique, machine-extractable with a simple regex: `^### (D|Q|R)-(\d+):?\s*(.*)$`. The metadata fields (`Date:`, `Raised by:`, `Supersedes:`) use bold-prefixed list items, also regex-extractable.
|
||||
|
||||
**The caveats:**
|
||||
|
||||
1. **Decision body boundaries.** The body of a decision runs from the heading to the next heading of equal or higher level. This is standard markdown section parsing — not trivial to hand-roll, but well-solved by libraries. Python's `mistletoe`, `markdown-it-py`, or even a simple state machine splitting on `^###` lines will work. No need for a full AST parser.
|
||||
|
||||
2. **Freeform cross-references.** Decisions reference others in prose: "Supersedes: D-002", "Same principle as D-010", "See D-024", "Ties to D-010 principle 2". Extracting these is a regex scan for `D-\d{3}` and `Q-\d{3}` patterns in the body text. This catches 95% of references. The 5% it misses (oblique references like "the four architectural principles") are not worth chasing — that is what Qdrant semantic search is for.
|
||||
|
||||
3. **Superseded decisions are currently tombstones.** D-002 is just `### D-002: SUPERSEDED by D-005`. The original text is in git history. During migration, Qatux will need to restore the original text from git for archival completeness. This is a one-time editorial task, not a parsing challenge.
|
||||
|
||||
4. **Inconsistent metadata.** Not every decision has `Raised by:` or `Dissent:`. Some have `Supersedes:`, some have `Superseded by:`, some have `Resolves:`. The parser must treat all metadata fields as optional. This is easy.
|
||||
|
||||
### 1.2 Is the sync script realistic?
|
||||
|
||||
**Feasible. Straightforward, in fact.** *cracks knuckles*
|
||||
|
||||
Scope-wise, this means: a Python script of ~150-250 lines that reads markdown files, extracts decision blocks using heading-level splitting, parses metadata fields with regex, extracts cross-references with a `D-\d{3}|Q-\d{3}|R-\d{3}` scan, and upserts into SQLite. The existing `sqlite_connector.py` already handles connection management, WAL mode, and foreign keys. The sync script can import from it or use the same patterns.
|
||||
|
||||
**Difficulty tier: Low.** The parsing is deterministic, the input format is controlled by us, and we can add a `## Parsing Contract` section to the decisions README that specifies exactly what the parser expects. If someone writes a decision that breaks the parser, the parser fails loudly (missing ID, unparseable heading) rather than silently (wrong data).
|
||||
|
||||
**Time estimate:** 3-4 hours for the initial script with tests. 1-2 hours for edge case polish after the first real run.
|
||||
|
||||
### 1.3 What breaks when someone forgets to run the sync script?
|
||||
|
||||
This is the critical question. Both proposals identify sync drift as the primary risk.
|
||||
|
||||
**Si's mitigation:** Pre-commit hook runs sync automatically.
|
||||
**Qatux's mitigation:** Linter script checks markdown IDs match DB IDs.
|
||||
|
||||
Both are right, but I want to reframe the risk. The markdown files are the source of truth. The SQLite index is a derived view. If the index drifts, the consequences are:
|
||||
|
||||
- Agents querying the DB get stale metadata (wrong status, missing new decisions)
|
||||
- Cross-reference queries return incomplete results
|
||||
- Ticket-to-decision coverage reports are inaccurate
|
||||
|
||||
These are annoying but not catastrophic. No agent makes architectural decisions based solely on a DB query — they read the markdown file. The DB is a convenience layer for discovery and reporting. If it drifts for a day, nobody ships broken code.
|
||||
|
||||
**My recommendation: Make the sync script idempotent and cheap, then run it aggressively.** Specifically:
|
||||
|
||||
1. `decisions-sync` should take <2 seconds for our current 52 entries. At 200 entries, still under 5 seconds. SQLite is fast. There is no reason not to run it on every relevant commit.
|
||||
2. Add it to `make setup` so the DB is always current when an agent starts work.
|
||||
3. Add a `make decisions-sync` target that agents can call manually.
|
||||
4. Add a pre-commit hook that runs the sync and stages the DB file. This eliminates drift entirely for git-committed changes.
|
||||
5. **Do NOT require agents to manually run sync.** The pre-commit hook handles it. If Qatux edits a markdown file and commits, the hook runs, the DB updates, both get committed together.
|
||||
|
||||
The pre-commit hook is the key. It turns "two operations that must stay in sync" into "one operation that automatically does both." This is the difference between "manageable maintenance" and "inevitable drift."
|
||||
|
||||
---
|
||||
|
||||
## 2. Schema Review
|
||||
|
||||
### 2.1 Si's Proposed Schema
|
||||
|
||||
Si proposes three tables: `decisions`, `decision_refs`, `decision_tags`. The `decisions` table stores `id`, `type`, `domain`, `title`, `status`, `supersedes`, `superseded_by`, `date`, `raised_by`, `file_path`, `line_start`, `line_end`, `synced_at`.
|
||||
|
||||
### 2.2 Qatux's Proposed Schema
|
||||
|
||||
Qatux proposes two tables: `decision_index`, `decision_refs`. Similar to Si but uses `decision_index` as the table name, adds `round INTEGER`, `tags TEXT` (JSON array), and omits `raised_by` from the index (keeping it in markdown only).
|
||||
|
||||
### 2.3 My Assessment
|
||||
|
||||
Both schemas are reasonable. The differences are minor. Here is what I would change:
|
||||
|
||||
**Table naming: Use `decisions`, not `decision_index`.** The table IS the decisions metadata. "Index" implies it is secondary, which creates confusion about source of truth. Call it what it is. The markdown is the authoritative content; the DB table is the authoritative metadata and relationship store.
|
||||
|
||||
**Drop `line_start` / `line_end` from Si's schema.** These are fragile — they break every time someone edits the file above a decision. The `file_path` column is sufficient for directing agents to the right file. If you need to jump to a specific decision within a file, use the heading anchor (`architecture.md#d-010`). Markdown anchors are stable; line numbers are not.
|
||||
|
||||
**Drop `raised_by` from the DB.** It is in the markdown. It does not drive any query we care about ("show me all decisions raised by Tyre" is not a useful planning query). Keep the DB lean — only store what you actually query.
|
||||
|
||||
**Keep `round INTEGER`.** Qatux is right — this enables "what changed in Round 17?" queries, which are useful for sprint planning. Si's schema implicitly supports this through date, but round number is more natural for our workflow.
|
||||
|
||||
**Drop `decision_tags` as a separate table (for now).** Si proposes a tags table. Qatux proposes a JSON array column. Neither has a compelling query use case today. Tags are a future enhancement — add them when we have a query that needs them. YAGNI.
|
||||
|
||||
**Keep `decision_refs` with Si's `ref_type` enum.** Both proposals agree on this. The enum (`supersedes`, `references`, `resolves`, `depends_on`) is the right set. Qatux adds a `note TEXT` column — that is a nice touch for context, low cost, include it.
|
||||
|
||||
**Add ON DELETE CASCADE to `decision_refs`.** If a decision is removed from the markdown (rare but possible), the refs should clean up automatically.
|
||||
|
||||
### 2.4 Recommended Schema
|
||||
|
||||
```sql
|
||||
-- Decision metadata (synced from decisions/*.md files)
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
id TEXT PRIMARY KEY, -- 'D-031', 'Q-009', 'R-010'
|
||||
type TEXT NOT NULL CHECK(type IN ('confirmed', 'question', 'rejected')),
|
||||
domain TEXT NOT NULL, -- derived from source filename
|
||||
title TEXT NOT NULL,
|
||||
status TEXT DEFAULT 'active'
|
||||
CHECK(status IN ('active', 'superseded', 'resolved', 'open')),
|
||||
round INTEGER, -- discussion round number
|
||||
date TEXT, -- YYYY-MM-DD
|
||||
file_path TEXT NOT NULL, -- 'decisions/architecture.md'
|
||||
synced_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Cross-references between decisions (parsed from markdown content)
|
||||
CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
source_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE CASCADE,
|
||||
target_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE CASCADE,
|
||||
ref_type TEXT NOT NULL
|
||||
CHECK(ref_type IN ('supersedes', 'references', 'resolves', 'depends_on')),
|
||||
note TEXT, -- optional context
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_status ON decisions(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_drefs_target ON decision_refs(target_id);
|
||||
```
|
||||
|
||||
**Why this is better:**
|
||||
|
||||
- 2 tables, not 3 (dropped tags — add later if needed)
|
||||
- No fragile line numbers
|
||||
- No metadata that duplicates markdown content without query value
|
||||
- CASCADE cleanup on refs
|
||||
- Uses `IF NOT EXISTS` to match existing schema.sql conventions
|
||||
- Composes cleanly with existing ticket schema — the existing `tickets.decision_ref TEXT` column already points at `decisions.id` without requiring a foreign key constraint (which is correct, since some tickets predate the decisions table)
|
||||
|
||||
### 2.5 Composition with Existing Schema
|
||||
|
||||
The existing `tickets` table has `decision_ref TEXT` (line 17 of `db/schema.sql`) with an index on it (`idx_tickets_decision`). This is already the join key. No migration needed on the ticket side.
|
||||
|
||||
The key query both proposals want:
|
||||
```sql
|
||||
SELECT d.id, d.title, d.domain, COUNT(t.id) as ticket_count
|
||||
FROM decisions d
|
||||
LEFT JOIN tickets t ON d.id = t.decision_ref
|
||||
WHERE d.status = 'active' AND d.type = 'confirmed'
|
||||
GROUP BY d.id
|
||||
ORDER BY d.domain, d.id;
|
||||
```
|
||||
|
||||
This works today with the existing ticket schema, zero changes. That is elegant — the decisions table slots into the existing data model without any schema migration on the ticket side.
|
||||
|
||||
---
|
||||
|
||||
## 3. Tooling Implications
|
||||
|
||||
### 3.1 Makefile Changes
|
||||
|
||||
Add these targets:
|
||||
|
||||
```makefile
|
||||
# --- Decisions ---
|
||||
|
||||
decisions-sync:
|
||||
@python3 db/connectors/decisions_sync.py
|
||||
@echo "Decisions index synced."
|
||||
|
||||
decisions-coverage:
|
||||
@db/connectors/sqlite-query "SELECT d.domain, COUNT(DISTINCT d.id) as decisions, COUNT(DISTINCT t.decision_ref) as with_tickets FROM decisions d LEFT JOIN tickets t ON d.id = t.decision_ref WHERE d.status='active' AND d.type='confirmed' GROUP BY d.domain"
|
||||
|
||||
decisions-active:
|
||||
@db/connectors/sqlite-query "SELECT id, domain, title FROM decisions WHERE status='active' AND type='confirmed' ORDER BY domain, id"
|
||||
|
||||
decisions-orphan:
|
||||
@db/connectors/sqlite-query "SELECT id, title FROM decisions WHERE type='confirmed' AND id NOT IN (SELECT DISTINCT decision_ref FROM tickets WHERE decision_ref IS NOT NULL)"
|
||||
```
|
||||
|
||||
**Difficulty tier: Trivial.** Copy-paste into Makefile, done.
|
||||
|
||||
### 3.2 Connector Scripts
|
||||
|
||||
Add one new wrapper script: `db/connectors/decisions-sync` (bash one-liner, same pattern as `sqlite-init`).
|
||||
|
||||
The sync script itself (`db/connectors/decisions_sync.py`) is the main new artifact. It follows the same conventions as `sqlite_connector.py` — uses the same `config.json` for DB path, same connection patterns, JSON output.
|
||||
|
||||
**Do NOT create a separate `decisions-query` wrapper.** The existing `sqlite-query` and `sqlite-exec` wrappers already work for decisions queries. Adding another wrapper creates confusion about which to use. Just document the common queries in the decisions README and as make targets.
|
||||
|
||||
### 3.3 `make setup` Changes
|
||||
|
||||
Add `decisions-sync` to the `setup` target so the DB is current when an agent starts a session:
|
||||
|
||||
```makefile
|
||||
setup: setup-rust setup-godot setup-tooling decisions-sync
|
||||
```
|
||||
|
||||
This is important. It means every `make setup` run ensures the decisions index is current. Agents who follow the CLAUDE.md workflow ("run `make setup`") automatically get a fresh index.
|
||||
|
||||
### 3.4 Pre-commit Hook
|
||||
|
||||
Add a git pre-commit hook that:
|
||||
1. Checks if any `decisions/*.md` file is staged
|
||||
2. If so, runs `decisions-sync`
|
||||
3. Stages the updated `db/commonwealth.db`
|
||||
|
||||
This is ~10 lines of bash. It eliminates drift for any change that goes through git commit.
|
||||
|
||||
**Important:** The hook must be installed manually (git hooks are not tracked in the repo). Add a `make install-hooks` target and document it in CLAUDE.md. Or use the `core.hooksPath` git config to point at a tracked hooks directory.
|
||||
|
||||
### 3.5 CLAUDE.md Changes
|
||||
|
||||
Update the "Agent Instructions / Before starting work" section:
|
||||
|
||||
```markdown
|
||||
### Before starting work
|
||||
1. Read your briefing at `docs/briefings/{your-name}.md` for current project context
|
||||
2. Read the relevant `decisions/*.md` domain file(s) listed in your briefing
|
||||
3. Check `docs/discussions/` for recent discussion rounds if needed
|
||||
```
|
||||
|
||||
Update the "File conventions" section:
|
||||
|
||||
```markdown
|
||||
- Decision files: `decisions/*.md` (domain-split, source of truth)
|
||||
- Decision IDs: `D-NNN` (confirmed), `Q-NNN` (open questions), `R-NNN` (rejected)
|
||||
```
|
||||
|
||||
### 3.6 Agent Workflow Changes
|
||||
|
||||
**Difficulty tier: Low.** The change for agents is: instead of reading one 474-line file, read one or two 80-150 line files that your briefing points you to. This is strictly less work for agents. Adoption friction is near zero.
|
||||
|
||||
The only agent whose workflow changes materially is Qatux, who now writes to domain files instead of DECISIONS.md and must run `make decisions-sync` (or rely on the pre-commit hook). This is a modest workflow change with a net time savings (fewer briefing updates, faster Qdrant re-indexing).
|
||||
|
||||
---
|
||||
|
||||
## 4. Domain Taxonomy
|
||||
|
||||
### 4.1 The Proposals
|
||||
|
||||
Si proposes 6 content domains + questions + rejected = 8 files.
|
||||
Qatux proposes 9 content domains + questions + rejected = 11 files.
|
||||
|
||||
The difference: Qatux splits out `world.md`, `combat.md`, `multiplayer.md`, and `meta.md` as separate domains. Si groups these into broader buckets.
|
||||
|
||||
### 4.2 My Assessment: Too Many Domains is Worse Than Too Few
|
||||
|
||||
The point of domain splitting is that an agent knows which file to read. If there are 11 files, an agent starting a session asks "which of these 11 files do I need?" and we are back to a discovery problem — just at the file level instead of the decision level.
|
||||
|
||||
Qatux's `combat.md` currently has ONE decision (D-008). `meta.md` has TWO (D-004, D-021). `multiplayer.md` has ONE (D-009). `process.md` has ONE (D-022). Four files with 1-2 decisions each is over-engineering. These files will sit at 15-30 lines for months. That is not a domain, that is a stub.
|
||||
|
||||
**The right heuristic: a domain file should be 60-200 lines today and projected to grow to 150-400 lines over 12 months.** Below 60 lines, the file is overhead. Above 400 lines, the file needs splitting.
|
||||
|
||||
### 4.3 Recommended Taxonomy: 5 Content Domains + 2 Reference Files
|
||||
|
||||
| Domain | File | Current Decisions | Notes |
|
||||
|--------|------|-------------------|-------|
|
||||
| **architecture** | `decisions/architecture.md` | D-008, D-009, D-010, D-012, D-020, D-026, D-030, D-031 | Technical foundation. Includes combat design principles (D-008, which is about z-levels, LOD, procgen patterns), multiplayer architecture (D-009), simulation tiers (D-026), time system (D-031). ~8 decisions, ~160 lines. |
|
||||
| **perception** | `decisions/perception.md` | D-011, D-015, D-016, D-017, D-018, D-019 | Everything the player sees and hears. Camera, fog, sound, monologue, perception modes. ~6 decisions, ~120 lines. |
|
||||
| **content** | `decisions/content.md` | D-023, D-024, D-025, D-028, D-029 | NPC generation, templates, dialogue, population ratios. Will grow fastest. ~5 decisions now, projected 20-30. ~100 lines now. |
|
||||
| **scope** | `decisions/scope.md` | D-001, D-003, D-005, D-006, D-007, D-013, D-014, D-027 | What we are building, the core concept, design pillars, prototype definition, map specs, POI system. ~8 decisions, ~180 lines. |
|
||||
| **process** | `decisions/process.md` | D-004, D-021, D-022 | Team, title, workflow. Small now, grows slowly. ~3 decisions, ~40 lines. Acceptable as a thin file because process decisions are rarely consulted. |
|
||||
| -- | `decisions/questions.md` | Q-001 through Q-011 | Reference file. |
|
||||
| -- | `decisions/rejected.md` | R-001 through R-010 | Reference file. |
|
||||
|
||||
**Total: 7 files.** Down from Qatux's 11, up from a monolith.
|
||||
|
||||
**Why this works:**
|
||||
|
||||
- **Tyre reads:** `architecture.md` (primary) + `perception.md` (secondary). Two files, ~280 lines total. Down from 474.
|
||||
- **Paula reads:** `content.md` (primary). One file, ~100 lines. Down from 474.
|
||||
- **Gestalt reads:** `scope.md` + `content.md`. Two files, ~280 lines. Down from 474.
|
||||
- **Si reads:** `scope.md` (primary) + queries DB for cross-domain planning.
|
||||
- **Miri reads:** `scope.md` + `content.md` for lore-relevant decisions.
|
||||
|
||||
Every agent reads 1-2 files. No agent reads all 7. The briefing tells them which files to read. Discovery problem solved.
|
||||
|
||||
### 4.4 Where the Edge Cases Go
|
||||
|
||||
- **D-008 (action pillar):** Goes in `architecture.md`. It is about z-levels, LOD, procgen, combat system architecture. Tyre needs it. When the combat system grows, we can split `architecture.md` into `architecture-core.md` and `architecture-combat.md`. Not yet.
|
||||
- **D-009 (multiplayer):** Goes in `architecture.md`. It is an architectural constraint, not a game design domain. The "design for multiplayer" principle affects every system Tyre builds.
|
||||
- **D-013 (diegetic insert/POI):** Goes in `scope.md`. It is part of the game concept — how navigation works. Cross-reference in `perception.md` if needed.
|
||||
- **D-026 (simulation tiers):** Goes in `architecture.md`. It is a performance budget and ECS architecture decision. Content implications are noted via cross-reference.
|
||||
- **D-031 (time system):** Goes in `architecture.md`. It is a simulation infrastructure decision with gameplay implications.
|
||||
|
||||
The principle: **if Tyre has to validate the technical feasibility, it goes in architecture. If Paula has to write content for it, it goes in content. If Gestalt has to design systems around it, it goes in scope or content depending on whether it is a "what" decision (scope) or a "how" decision (content).**
|
||||
|
||||
### 4.5 When to Split
|
||||
|
||||
Set a threshold: when any domain file exceeds 350 lines (~25 decisions), review whether it should split. `content.md` will hit this first. Split into `content-npcs.md` and `content-dialogue.md` when it happens. The DB makes this painless — update `file_path` column, move the text. Git diff shows the reorganization clearly.
|
||||
|
||||
---
|
||||
|
||||
## 5. Alternative Considerations
|
||||
|
||||
### 5.1 Can We Get 80% of the Benefit With 20% of the Work?
|
||||
|
||||
**Yes.** The domain split alone (Option A / Si's Phase 1) delivers the vast majority of the value:
|
||||
|
||||
- 70-80% context reduction: agents read domain files, not the monolith
|
||||
- Cleaner git diffs: domain-scoped changes
|
||||
- Faster Qdrant re-indexing: per-domain-file
|
||||
- Better discovery: agents know which file to read
|
||||
|
||||
The DB index adds:
|
||||
- Cross-reference queries (nice but agents can grep markdown)
|
||||
- Ticket-decision linkage reports (useful for Si's sprint planning)
|
||||
- Structured supersession tracking (nice but rare — 2 superseded decisions in 31)
|
||||
- "Decisions without tickets" audit (genuinely useful)
|
||||
|
||||
**If time is tight: do the domain split now, defer the DB index.** The domain split is a 2-3 hour editorial pass with zero new tooling. The DB index is another 4-6 hours of scripting and testing. Both are cheap, but the split delivers immediate value to every agent while the DB index primarily benefits Si's planning workflow.
|
||||
|
||||
**However:** Since both Si and Qatux have already designed the DB schema and sync approach in detail, and the engineering is straightforward, I see no reason to defer. Do both. The total effort is under a day. The sync script is a weekend build, same as D-028's line previewer CLI.
|
||||
|
||||
### 5.2 What About Just Using Qdrant?
|
||||
|
||||
One approach neither proposal explored: instead of SQLite, use Qdrant's metadata filtering capabilities. Index the domain files with metadata (domain, decision_id, status) and let agents search with filters.
|
||||
|
||||
**I would reject this.** Qdrant is a semantic search engine, not a relational database. It does not support JOINs (decisions-to-tickets), aggregations (coverage reports), or precise equality queries (get D-010 exactly). It complements the DB, it does not replace it.
|
||||
|
||||
### 5.3 What About a Single README Index File Instead of SQLite?
|
||||
|
||||
A `decisions/README.md` with a manually maintained table of all decisions (ID, domain, title, status) would give agents a lightweight discovery mechanism without any new tooling.
|
||||
|
||||
**This is actually worth doing regardless of whether we build the DB index.** It is a 20-minute manual creation during the split. It serves as the human-readable entry point to the domain files. The DB index can then be validated against it.
|
||||
|
||||
I recommend: create `decisions/README.md` as part of the migration. Whether we build the DB sync script immediately or defer it, the README has standalone value.
|
||||
|
||||
---
|
||||
|
||||
## 6. My Recommendation
|
||||
|
||||
### 6.1 Endorse Hybrid Approach With Modifications
|
||||
|
||||
I endorse the hybrid domain-split + SQLite index approach. Both proposals are well-reasoned. My modifications:
|
||||
|
||||
1. **Reduce domain count from 11 to 7 files** (5 content domains + questions + rejected). See Section 4.3 for the taxonomy.
|
||||
|
||||
2. **Simplify the schema to 2 tables** (decisions + decision_refs). Drop decision_tags, drop line numbers, drop raised_by from DB. See Section 2.4 for the schema.
|
||||
|
||||
3. **Make the sync script a pre-commit hook, not a manual step.** This eliminates drift as a failure mode entirely. See Section 1.3.
|
||||
|
||||
4. **Add `decisions-sync` to `make setup`.** Every agent session starts with a current index.
|
||||
|
||||
5. **Create `decisions/README.md` as a human-readable index** regardless of DB timeline.
|
||||
|
||||
6. **Do NOT create new wrapper scripts for decisions queries.** Use existing `sqlite-query` and `sqlite-exec`. Add common queries as make targets.
|
||||
|
||||
7. **Phase the migration in 2 phases, not 5.** Si's 5-week phased rollout is too slow for the scope of work. This is a day of focused effort, not a multi-week project.
|
||||
|
||||
### 6.2 Revised Migration Plan
|
||||
|
||||
**Phase 1: Split + Index (Day 1, ~4-5 hours)**
|
||||
|
||||
1. Create `decisions/` directory
|
||||
2. Split DECISIONS.md into 7 domain files using the taxonomy in Section 4.3
|
||||
3. Restore superseded decision text from git history (D-002, D-006)
|
||||
4. Add cross-reference hyperlinks between domain files
|
||||
5. Create `decisions/README.md` with index table
|
||||
6. Replace root `DECISIONS.md` with a redirect
|
||||
7. Extend `db/schema.sql` with decisions tables (Section 2.4)
|
||||
8. Run `sqlite-init` to apply new schema
|
||||
9. Build and run `db/connectors/decisions_sync.py`
|
||||
10. Update all agent briefings to point to domain files
|
||||
11. Re-index domain files in Qdrant
|
||||
12. Update `CLAUDE.md`
|
||||
13. Commit everything in one atomic commit
|
||||
|
||||
**Phase 2: Automation (Day 2, ~2-3 hours)**
|
||||
|
||||
1. Add `decisions-sync` target to Makefile
|
||||
2. Add `decisions-coverage`, `decisions-active`, `decisions-orphan` make targets
|
||||
3. Add `decisions-sync` to `make setup`
|
||||
4. Create pre-commit hook for auto-sync
|
||||
5. Add `make install-hooks` target
|
||||
6. Write 5-10 parser tests (test fixtures: sample decision blocks)
|
||||
7. Commit
|
||||
|
||||
**Total effort: ~7-8 hours across 2 focused sessions.** Not 5 weeks.
|
||||
|
||||
### 6.3 What I Would Build
|
||||
|
||||
If assigned the technical implementation (which Si's proposal assigns to me), here is what I would deliver:
|
||||
|
||||
1. **`db/connectors/decisions_sync.py`** (~200 lines Python)
|
||||
- Reads all `decisions/*.md` files
|
||||
- Parses decision blocks using `^### (D|Q|R)-\d+` heading splits
|
||||
- Extracts: id, type, title, status, date, round (from metadata fields)
|
||||
- Derives: domain (from filename), file_path
|
||||
- Scans body for `(D|Q|R)-\d{3}` references, infers ref_type from context
|
||||
- Upserts into `decisions` and `decision_refs` tables
|
||||
- Validates: warns on broken references, orphaned supersessions
|
||||
- Output: JSON summary (decisions synced, refs created, warnings)
|
||||
- Idempotent: safe to run repeatedly
|
||||
|
||||
2. **`db/connectors/decisions-sync`** (bash one-liner wrapper, same pattern as `sqlite-init`)
|
||||
|
||||
3. **Schema additions to `db/schema.sql`** (the 2-table schema from Section 2.4)
|
||||
|
||||
4. **Makefile targets** (Section 3.1)
|
||||
|
||||
5. **Pre-commit hook** (~15 lines bash)
|
||||
|
||||
6. **Parser test fixtures** (3-4 sample decision blocks + expected parse output)
|
||||
|
||||
**Difficulty tier: Low-to-medium. Challenging only in the "doing it carefully" sense, not the "might not work" sense.** The parsing is deterministic, the schema is simple, the tooling patterns are established. This is a confidence build, not a research spike.
|
||||
|
||||
---
|
||||
|
||||
## 7. Risks I Want to Flag
|
||||
|
||||
### 7.1 Binary DB File in Git
|
||||
|
||||
Both proposals commit `commonwealth.db` to git. Binary files in git are not diffable. Qatux acknowledges this; Si does not address it.
|
||||
|
||||
**My position: this is acceptable.** The DB is a derived artifact (synced from markdown). Its git history is the markdown file history. If you need to see "what changed in the decisions index," look at the markdown diff. The DB file is committed for convenience (agents have a current index without running sync), not for auditability.
|
||||
|
||||
**Mitigation:** If the DB grows large (unlikely — decisions metadata is tiny), we can `.gitignore` it and require `make setup` to regenerate it. But at <100KB for hundreds of decisions, this is a non-issue.
|
||||
|
||||
### 7.2 Taxonomy Disputes
|
||||
|
||||
When D-032 arrives, someone must decide which domain file it goes in. If the taxonomy is unclear, this becomes a recurring friction point.
|
||||
|
||||
**Mitigation:** The `decisions/README.md` must include a clear one-sentence definition of each domain's scope and a "when in doubt" rule. My proposed rule: **if a decision constrains how we build, it is architecture. If it defines what we build, it is scope. If it defines what the player experiences, it is content or perception. If it defines how the team works, it is process.**
|
||||
|
||||
### 7.3 Cross-Domain Decisions
|
||||
|
||||
D-026 (simulation tiers) touches architecture AND content. D-013 (POI system) touches scope AND perception.
|
||||
|
||||
**My position: single-homing with cross-reference notes.** Every decision lives in exactly one file. The other file gets a one-line cross-reference: `> See also: [D-026: Simulation tiers](architecture.md#d-026) — performance budgets affect content density`. This is cleaner than duplication and keeps the DB simple (one file_path per decision).
|
||||
|
||||
### 7.4 The "Nobody Reads the README" Problem
|
||||
|
||||
We are creating a `decisions/README.md` that explains the taxonomy. If agents skip it and go straight to the wrong domain file, they miss decisions.
|
||||
|
||||
**Mitigation: Briefings are the primary routing mechanism, not the README.** Agents do not discover which file to read — their briefing tells them. The README is for humans and for agents who need cross-domain context. This is already how it works (briefings tell agents which decisions to read); we are just making the pointer more granular (domain file vs monolith).
|
||||
|
||||
---
|
||||
|
||||
## 8. Conclusion
|
||||
|
||||
This is a good idea proposed by two agents who thought about it carefully. The technical execution is straightforward. The main value — context window reduction for agents — is immediate and significant. The secondary value — structured queries for sprint planning — is real but can be phased.
|
||||
|
||||
I endorse the hybrid approach with the modifications described above: fewer domains (7 not 11), leaner schema (2 tables not 3), automated sync (pre-commit hook, not manual), and compressed timeline (2 days not 5 weeks).
|
||||
|
||||
Assign me the sync script, schema additions, and Makefile targets. I can have Phase 1 and 2 deliverables ready in two focused sessions.
|
||||
|
||||
---
|
||||
|
||||
**File locations referenced:**
|
||||
- This review: `/var/home/jeroenschweitzer/Projects/commonwealth/docs/discussions/decisions-restructure-tyre.md`
|
||||
- Si's analysis: `/var/home/jeroenschweitzer/Projects/commonwealth/docs/discussions/decisions-restructure-si.md`
|
||||
- Qatux's analysis: `/var/home/jeroenschweitzer/Projects/commonwealth/docs/discussions/decisions-restructure-qatux.md`
|
||||
- Current schema: `/var/home/jeroenschweitzer/Projects/commonwealth/db/schema.sql`
|
||||
- Current decisions: `/var/home/jeroenschweitzer/Projects/commonwealth/DECISIONS.md`
|
||||
- Makefile: `/var/home/jeroenschweitzer/Projects/commonwealth/Makefile`
|
||||
- SQLite connector: `/var/home/jeroenschweitzer/Projects/commonwealth/db/connectors/sqlite_connector.py`
|
||||
|
||||
**End of technical review.**
|
||||
Reference in New Issue
Block a user