Decisions
DecisionLog
The decisions ledger at ~/.cc-transcript/decisions.db.
Usage
DecisionLog(actor)Opened in WAL mode with a busy timeout because cc-review’s Go daemon writes the same file concurrently. Durable by convention: rows are never auto-dropped. Requires a local disk — WAL does not work over NFS.
Example
>>> log = await DecisionLog.open()
>>> async with log:
... await log.append(decision)
... await log.attribute_tool(session_id, tool_digest=digest, near_ts_ms=ts)Methods
| Name | Description |
|---|---|
| attribute_nearest() |
The decision nearest near_ts_ms for a digestless event.
|
| attribute_tool() |
The nearest decision preceding near_ts_ms with this digest.
|
| open() |
Opens the exact v1 decision ledger at path.
|
attribute_nearest()
The decision nearest near_ts_ms for a digestless event.
Usage
attribute_nearest(
session_id, *, event, near_ts_ms, kind=None, window_ms=300000
)The documented-probabilistic path for Stop, UserPromptSubmit, and Notification fires, which carry no tool digest: filters by event (and kind when given), then picks the smallest absolute timestamp distance within the window, on either side of near_ts_ms.
Returns
Decision | None-
The nearest matching decision, or None when none lies in
[near_ts_ms - window_ms, near_ts_ms + window_ms].
attribute_tool()
The nearest decision preceding near_ts_ms with this digest.
Usage
attribute_tool(session_id, *, tool_digest, near_ts_ms, window_ms=300000)Joins by digest equality plus time window only — never by message text. Repeated identical calls share a digest, so in tight loops the nearest-preceding pick is probabilistic; prefer event_uuid to disambiguate when the row carries one.
Returns
Decision | None-
The matching decision, or None when no digest-equal row lies in
[near_ts_ms - window_ms, near_ts_ms].
open()
Opens the exact v1 decision ledger at path.
Usage
open(path=None)Decision
One row of the decision ledger.
Usage
Decision(
ts_ms,
session_id,
source,
kind,
source_file,
event,
action,
tool_name=None,
tool_digest=None,
event_uuid=None,
message=None,
detail=dict()
)Attributes
ts_ms: int-
Integer-millisecond timestamp of the decision; part of the UNIQUE key, so re-running a writer is exactly idempotent.
session_id: SessionId-
The Claude session UUID the decision fired in.
source: str-
The writing system, e.g.
captain-hookorcc-review. kind: str-
The writer’s decision taxonomy, e.g. the hook name.
source_file: str-
The file the deciding hook was registered from;
""when the writer has no file provenance. event: str-
The Claude Code event, e.g.
PreToolUseorStop. action: Action-
What the decision did: allow, block, warn, nudge, or note.
tool_name: str | None-
The tool under decision, for tool-shaped events.
tool_digest: ToolDigest | None-
The cross-language content digest of the tool call; the preferred attribution key.
event_uuid: EventUuid | None-
The transcript entry uuid, when the writer knows it; the preferred disambiguator for repeated identical calls.
message: str | None-
The user-visible decision text, if any.
detail: Mapping[str, Any]-
Structured extras, serialized to
detail_json.
Action
Action=Literal["allow", "block", "warn", "nudge", "note"]
DECISIONS_DDL
DECISIONS_DDL="CREATE TABLE cc_review_decisions_schema_v1 ( id INTEGER PRIMARY KEY CHECK (id = 1), component TEXT NOT NULL CHECK (component = 'cc-review-decisions-v1'), schema_version INTEGER NOT NULL CHECK (schema_version = 1), ddl_fingerprint TEXT NOT NULL, object_fingerprint TEXT NOT NULL);TABLE decisions ( id INTEGER PRIMARY KEY, ts_ms INTEGER NOT NULL, session_id TEXT NOT NULL, source TEXT NOT NULL, kind TEXT NOT NULL, source_file TEXT NOT NULL, event TEXT NOT NULL, action TEXT NOT NULL CHECK (action IN ('allow', 'block', 'warn', 'nudge', 'note')), tool_name TEXT, tool_digest TEXT, event_uuid TEXT, message TEXT, detail_json TEXT NOT NULL, UNIQUE (session_id, ts_ms, source, kind, tool_digest));INDEX idx_decisions_session_ts ON decisions (session_id, ts_ms);INDEX idx_decisions_tool_digest ON decisions (tool_digest);INDEX idx_decisions_source_file ON decisions (source_file);TABLE dispatch_heartbeats ( session_id TEXT NOT NULL, event TEXT NOT NULL, first_ts_ms INTEGER NOT NULL, last_ts_ms INTEGER NOT NULL, count INTEGER NOT NULL, PRIMARY KEY (session_id, event));INDEX idx_heartbeats_session ON dispatch_heartbeats (session_id);"