Ports settled-reach's decisions_sync.py + ticket + decision scripts, Scrum-stripped. Writes to .pql/pql.db (gitignored). Verb shape mirrors the eventual `pql` subcommands so migration is a call-site find-replace once pql ships feature parity (D-040, R-011). Ticket IDs are T-NNN (TEXT PKs) — reshape from settled-reach's integer auto-increment so the stopgap's writes are compatible with pql's future reads without a data migration. No `sprints` table; the pyramid is kanban / waterfall (D-035). Verified end-to-end: sync parses 71 records (39 confirmed, 23 open questions, 9 rejected) with 62 cross-refs and zero broken links; `ticket new`, `ticket status`, `ticket board`, and `decisions show --with-refs` all round-trip correctly. Co-Authored-By: Claude <noreply@anthropic.com>
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
"""SQL schema for .pql/pql.db — adapted from settled-reach, Scrum stripped.
|
|
|
|
Differences from settled-reach:
|
|
- Tickets use TEXT PK (`T-NNN`), not INTEGER AUTOINCREMENT.
|
|
- No `sprints` table; no `sprint_id` column.
|
|
- `ticket_deps.{blocker,blocked}_id` are TEXT (referencing `tickets.id`).
|
|
"""
|
|
|
|
SCHEMA_SQL = """
|
|
PRAGMA journal_mode=WAL;
|
|
PRAGMA foreign_keys=ON;
|
|
|
|
CREATE TABLE IF NOT EXISTS decisions (
|
|
id TEXT PRIMARY KEY,
|
|
type TEXT NOT NULL CHECK(type IN ('confirmed','question','rejected')),
|
|
domain TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active'
|
|
CHECK(status IN ('active','superseded','resolved','open')),
|
|
date TEXT,
|
|
file_path TEXT NOT NULL,
|
|
synced_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
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','amends')),
|
|
note TEXT,
|
|
PRIMARY KEY (source_id, target_id, ref_type)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tickets (
|
|
id TEXT PRIMARY KEY,
|
|
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
|
parent_id TEXT REFERENCES tickets(id),
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
status TEXT NOT NULL DEFAULT 'backlog'
|
|
CHECK(status IN ('backlog','ready','in_progress','review','done','cancelled')),
|
|
priority TEXT DEFAULT 'medium'
|
|
CHECK(priority IN ('critical','high','medium','low')),
|
|
assigned_to TEXT,
|
|
team TEXT,
|
|
decision_ref TEXT REFERENCES decisions(id),
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS ticket_deps (
|
|
blocker_id TEXT NOT NULL REFERENCES tickets(id),
|
|
blocked_id TEXT NOT NULL REFERENCES tickets(id),
|
|
PRIMARY KEY (blocker_id, blocked_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS ticket_history (
|
|
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
|
field TEXT NOT NULL,
|
|
old_value TEXT,
|
|
new_value TEXT,
|
|
changed_by TEXT,
|
|
changed_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS ticket_labels (
|
|
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
|
label TEXT NOT NULL,
|
|
PRIMARY KEY (ticket_id, label)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
|
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
|
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
|
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
|
"""
|
|
|
|
|
|
TICKET_STATUSES = (
|
|
"backlog",
|
|
"ready",
|
|
"in_progress",
|
|
"review",
|
|
"done",
|
|
"cancelled",
|
|
)
|
|
|
|
TICKET_TYPES = ("initiative", "epic", "story", "task", "bug")
|
|
|
|
PRIORITIES = ("critical", "high", "medium", "low")
|