@contextgraph/dtg
Decision Trace Graph - tracks decisions with full lifecycle and audit trails.
Installation
pnpm add @contextgraph/dtg
Overview
The DTG tracks every significant decision through its lifecycle:
PROPOSED → APPROVED → EXECUTED → COMPLETED
↓ ↓
REJECTED FAILED
Creating the DTG
import { DecisionTraceGraph } from '@contextgraph/dtg';
const dtg = new DecisionTraceGraph(storage);
await dtg.initialize();
Recording Decisions
const decision = await dtg.record({
type: 'deployment',
title: 'Deploy v2.0.0 to production',
description: 'Release new authentication system',
proposedBy: agentId,
riskLevel: 'high',
context: {
environment: 'production',
services: ['api', 'web'],
},
});
Decision Lifecycle
Approve
await dtg.approve(decisionId, approverId, 'Approved after security review');
Reject
await dtg.reject(decisionId, reviewerId, 'Needs more testing');
Execute
await dtg.execute(decisionId, {
executedBy: agentId,
executedAt: createTimestamp(),
});
Complete
await dtg.complete(decisionId, {
success: true,
outcome: {
deploymentId: 'dep_123',
duration: 45000,
},
});
Fail
await dtg.fail(decisionId, {
error: 'Database migration failed',
failedAt: createTimestamp(),
});
Querying Decisions
Get by ID
const decision = await dtg.get(decisionId);
Query by Status
const pending = await dtg.queryDecisions({
status: 'proposed',
limit: 50,
});
const approved = await dtg.queryDecisions({
status: 'approved',
from: startTime,
to: endTime,
});
Query by Agent
const agentDecisions = await dtg.queryDecisions({
proposedBy: agentId,
});
Query by Risk Level
const highRisk = await dtg.queryDecisions({
riskLevel: ['high', 'critical'],
status: 'proposed',
});
Decision Interface
interface Decision {
id: DecisionId;
type: string;
title: string;
description?: string;
status: DecisionStatus;
riskLevel: RiskLevel;
proposedBy: AgentId;
proposedAt: Timestamp;
approvedBy?: AgentId;
approvedAt?: Timestamp;
rejectedBy?: AgentId;
rejectedAt?: Timestamp;
executedAt?: Timestamp;
completedAt?: Timestamp;
context?: Record<string, unknown>;
outcome?: Record<string, unknown>;
}
type DecisionStatus =
| 'proposed'
| 'approved'
| 'rejected'
| 'executed'
| 'completed'
| 'failed';
type RiskLevel = 'low' | 'medium' | 'high' | 'critical';
Decision History
Get the full history of a decision:
const history = await dtg.getHistory(decisionId);
for (const event of history) {
console.log(`${event.timestamp}: ${event.previousStatus} → ${event.newStatus}`);
console.log(` By: ${event.actor}`);
console.log(` Reason: ${event.reason}`);
}
Statistics
const stats = await dtg.getStats();
console.log(`Total: ${stats.total}`);
console.log(`Proposed: ${stats.byStatus.proposed}`);
console.log(`Approved: ${stats.byStatus.approved}`);
console.log(`Completed: ${stats.byStatus.completed}`);