tx

tx decision

Decisions as first-class artifacts — capture, review, and sync to specs

Purpose

Decisions are first-class artifacts in tx. When agents or developers make implementation choices, those decisions are captured, reviewed, and optionally synced back to design docs as invariants. This closes the spec-driven development loop: code changes produce decisions, which feed back to update specs and tests.

Decision Lifecycle

pending → approved → synced to doc (creates invariant)
        → rejected
        → edited → approved
        → superseded (by newer decision)

Decision Sources

SourceDescription
manualManually recorded via tx decision add
diffExtracted from a code diff
transcriptExtracted from an agent transcript
agentRecorded by an agent during execution

Content-Hash Dedup

Decisions are deduplicated by content hash (SHA256). Adding a decision with identical content to an existing one returns the existing decision instead of creating a duplicate.

Usage

# Add a decision manually
tx decision add "Use WAL mode for SQLite" --question "Which journal mode?"
tx decision add "Batch inserts for bulk operations" --task tx-abc123 --source agent

# List decisions
tx decision list
tx decision list --status pending --json
tx decision list --source manual --limit 10

# Show decision details
tx decision show dec-abc123def456

# Review decisions
tx decision approve dec-abc123def456 --reviewer james --note "Good call"
tx decision reject dec-abc123def456 --reviewer james --reason "Too complex"
tx decision edit dec-abc123def456 "Use WAL mode with 64MB cache" --reviewer james

# Shorthand for pending decisions
tx decision pending

# Spec health (aggregates decisions + docs + invariants)
tx spec health
tx spec health --json
import { TxClient } from '@jamesaphoenix/tx-agent-sdk'

// HTTP mode
const tx = new TxClient({ apiUrl: 'http://localhost:3456' })

// Or direct SQLite mode
const tx = new TxClient({ dbPath: '.tx/tasks.db' })

// Record a decision
const decision = await tx.decisions.add({
  content: 'Use WAL mode for SQLite',
  question: 'Which journal mode?',
  source: 'agent',
})

// List and filter decisions
const all = await tx.decisions.list()
const pending = await tx.decisions.pending()
const filtered = await tx.decisions.list({ status: 'pending', source: 'agent' })

// Show a specific decision
const detail = await tx.decisions.show('dec-abc123def456')

// Review decisions
await tx.decisions.approve(pending[0].id, 'agent', 'Verified correct')
await tx.decisions.reject('dec-abc123def456', 'reviewer', 'Too complex')
await tx.decisions.edit('dec-abc123def456', 'Updated content', 'reviewer')

Tools:

ToolDescription
tx_decision_addRecord a decision (auto-deduplicates by content hash)
tx_decision_listList decisions with optional status/source filters
tx_decision_showShow a decision by ID
tx_decision_approveApprove a pending decision
tx_decision_rejectReject a pending decision (requires reason)
tx_decision_editEdit a pending decision's content
tx_decision_pendingList all pending decisions awaiting review

Example request:

{
  "name": "tx_decision_add",
  "arguments": {
    "content": "Use WAL mode for SQLite",
    "question": "Which journal mode?",
    "source": "agent"
  }
}
POST /api/decisions
GET  /api/decisions?status=pending&source=agent&limit=10
GET  /api/decisions/:id
POST /api/decisions/:id/approve
POST /api/decisions/:id/reject
POST /api/decisions/:id/edit
GET  /api/decisions/pending

Examples:

# Create a decision
curl -X POST http://localhost:3456/api/decisions \
  -H 'Content-Type: application/json' \
  -d '{"content": "Use WAL mode for SQLite", "source": "manual"}'

# List pending decisions
curl http://localhost:3456/api/decisions?status=pending

# Approve a decision
curl -X POST http://localhost:3456/api/decisions/dec-abc123def456/approve \
  -H 'Content-Type: application/json' \
  -d '{"reviewer": "james", "note": "Good call"}'

Decision Fields

FieldDescription
idUnique ID: dec-<12 hex chars> (derived from content hash)
contentThe decision text
questionOptional: the question the decision answers
statuspending, approved, rejected, edited, superseded
sourcemanual, diff, transcript, agent
commitShaGit commit associated with the decision
taskIdLinked task ID
docIdLinked doc ID
contentHashSHA256 hash for dedup
reviewedByWho reviewed the decision
reviewNoteReview notes or rejection reason
editedContentUpdated content (when status is "edited")
reviewedAtTimestamp of review
supersededByID of the decision that supersedes this one
syncedToDocWhether the decision has been synced to a doc
runIdLinked run ID (if extracted during a run)
invariantIdLinked invariant ID (after doc sync)

Spec Health

Use tx spec health to see overall spec-driven development health:

$ tx spec health

Spec Health: DRIFTING

  Decisions:    3 pending, 1 approved-unsynced
  Doc Drift:    0 docs with hash mismatch
  Invariants:   21 active

  Action items:
    - Review 3 pending decisions
    - Sync 1 approved decision to docs

The spec health command aggregates decision status, doc drift, and invariant coverage into a single health view.

On this page