feat(tooling): add decision show <D-NNN> with implementing tickets + refs
Closes the decision-to-ticket coverage gap (#723). The `decisions-coverage` Makefile target already reported per-decision counts; this adds the single-decision drill-down via `tooling/db/decision show D-159`, returning linked tickets, outbound refs, and inbound refs in one shot. Schema unchanged — reverse link is a SELECT on tickets.decision_ref. Refs: #723 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -414,6 +414,49 @@ def claim_id(cfg, prefix, domain, title):
|
||||
conn.close()
|
||||
|
||||
|
||||
def show_decision(cfg, decision_id):
|
||||
"""Show a single decision with full details including linked tickets and cross-refs."""
|
||||
conn = get_connection(cfg)
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM decisions WHERE id = ?",
|
||||
(decision_id,),
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
return {"ok": False, "error": f"Decision not found: {decision_id}"}
|
||||
|
||||
decision = dict(row)
|
||||
|
||||
# Implementing tickets: tickets where decision_ref = this ID
|
||||
ticket_rows = conn.execute(
|
||||
"SELECT id, title, status, type FROM tickets"
|
||||
" WHERE decision_ref = ? ORDER BY id",
|
||||
(decision_id,),
|
||||
).fetchall()
|
||||
decision["implementing_tickets"] = [dict(t) for t in ticket_rows]
|
||||
|
||||
# Cross-refs outbound: references from this decision to others
|
||||
refs_out = conn.execute(
|
||||
"SELECT target_id, ref_type, note FROM decision_refs"
|
||||
" WHERE source_id = ? ORDER BY target_id",
|
||||
(decision_id,),
|
||||
).fetchall()
|
||||
decision["refs_out"] = [dict(r) for r in refs_out]
|
||||
|
||||
# Cross-refs inbound: other decisions referencing this one
|
||||
refs_in = conn.execute(
|
||||
"SELECT source_id, ref_type, note FROM decision_refs"
|
||||
" WHERE target_id = ? ORDER BY source_id",
|
||||
(decision_id,),
|
||||
).fetchall()
|
||||
decision["refs_in"] = [dict(r) for r in refs_in]
|
||||
|
||||
return {"ok": True, "decision": decision}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def check_dupes(cfg):
|
||||
"""Check for duplicate decision IDs across all markdown files."""
|
||||
# Pre-existing collisions too deeply embedded to renumber (139+ references).
|
||||
@@ -461,6 +504,7 @@ Settled Reach Decisions Sync & ID Management
|
||||
|
||||
Usage:
|
||||
decisions_sync.py sync Parse decisions/*.md and upsert into SQLite
|
||||
decisions_sync.py show <D-NNN> Show a decision with linked tickets + refs
|
||||
decisions_sync.py next [D|Q|R] Show next available ID (all prefixes or one)
|
||||
decisions_sync.py claim <D|Q|R> <domain> [title] Claim next ID and insert placeholder
|
||||
decisions_sync.py check-dupes Check for duplicate IDs across markdown files
|
||||
@@ -493,6 +537,11 @@ def main():
|
||||
|
||||
if cmd == "sync":
|
||||
result = sync(cfg)
|
||||
elif cmd == "show":
|
||||
if len(sys.argv) < 3:
|
||||
result = {"ok": False, "error": "Usage: show <decision_id> e.g. show D-159"}
|
||||
else:
|
||||
result = show_decision(cfg, sys.argv[2])
|
||||
elif cmd == "next":
|
||||
result = next_id(cfg, sys.argv[2] if len(sys.argv) > 2 else None)
|
||||
elif cmd == "claim":
|
||||
|
||||
Reference in New Issue
Block a user