TreeTrace
The ledger · data model

WHAT A
TRACE
RECORDS

Every node is one turn: the prompt, the correction, the tool call, the outcome.

TreeTrace reads coding and CLI agent sessions and extracts the human steering layer: what was asked, what changed direction, what was corrected, what was abandoned, and what was refused. It writes the result as one structured, vendor-neutral record your tools can read. No upload. No telemetry. No LLM judge.

PROMPTNODE · node_003 kind: correction
parentIdnode_002
kindcorrection
titleNever log raw tokens
statusaccepted
actionsEdit /src/auth/jwt.js · 1 tool call
signalsecurity_or_privacy_risk resolvedBy node_004
carrieslesson_004 · 1 eval candidate
One session, row by row

The lineage is a ledger of turns.

Each row is one prompt node: who steered, what kind of move it was, what actually happened, and the signal TreeTrace attached. Tool noise, retries, and "continue" nudges are filtered out before anything is recorded.

SESSION LINEAGE · session_01H9F2K LIVE
node_001 root "add JWT refresh to auth middleware" accepted
node_002 direction "use rotating refresh tokens" refines
node_003 correction console.log("token:", t) caught → [REDACTED:stripe-key] security_or_privacy_risk
node_004 correction rule: never log raw tokens → lesson_004 resolved
node_005 rejection declined tool: git push --force user_declined_tool
head outcome PR #214 merged on the corrected path accepted
6 prompts · 2 dead-ends · 1 secret caught · 1 decline · 1 lesson · 0 bytes left your machine
What each node captures

A node is one turn, fully described.

Every prompt node carries its place in the lineage, the human text after redaction, the tool calls it triggered, and any failure or refusal signals, so the record is readable without ever replaying the raw transcript.

idstring

Stable identifier within the file (node_001), with parentId linking it to its lineage parent. The root node has a null parent.

kindenum

The kind of move: root, direction, correction, scope-change, checkpoint, question, or rejection, derived from prompt topology and your text.

titlestring

A first-sentence distillation of the prompt, alongside text, the full prompt after the redaction gate has run.

statusenum

accepted or abandoned. Abandoned branches are kept, not deleted. The dead-ends are the point.

actions[]Action[]

The tool calls made in response: tool, file path, shell command, and model. File and command values pass the same redaction gate as the prompt text.

failureSignals[]Signal[]

Typed, explainable labels: a type, confidence score, evidence text, and the node that resolved it. Every signal is a transparent heuristic, not a model verdict.

rejections[]v0.3

Typed stop signals: declined tools, interrupts, explicit declines, tool errors, permission-denied, and model refusals, each with source and confidence.

lessonIds[]string[]

Compact rules derived from this turn, plus evalCandidate, what should carry forward to the next agent as memory or a regression eval.

model · timestampmeta

Which model handled the turn, when it happened, and the session it came from. Raw transcript record IDs are referenced, never exported.

A sample record

One node, as written to tree.json.

This is the correction node from the session above, exactly as it lands in .treetrace/tree.json, with the secret already replaced with a redaction marker before the file was ever written.

.treetrace/tree.json · nodes[2]
{
  "id": "node_003",
  "parentId": "node_002",
  "role": "user",
  "kind": "correction",
  "title": "Never log the raw token.",
  "text": "Don't log the raw token. Mask it before printing.",
  "status": "accepted",
  "nudges": 0,
  "reruns": 0,
  "session": "session_01H9F2K",
  "timestamp": "2026-06-18T12:34:56.789Z",
  "model": "claude-opus-4-8",
  "actions": [
    { "tool": "Edit", "file": "/src/auth/jwt.js", "command": null, "model": "claude-opus-4-8" }
  ],
  "failureSignals": [
    {
      "type": "security_or_privacy_risk",
      "confidence": 0.82,
      "evidence": [REDACTED:stripe-key],
      "resolvedBy": "node_004"
    }
  ],
  "rejections": [],
  "evalCandidate": true,
  "lessonIds": ["lesson_004"],
  "sourceEventIds": ["…"]
}
One local record

Written to your repo. Never anywhere else.

The lineage lands as a single canonical file in your project. Every other artifact is derived from that same redacted tree, so there is one source of truth and one redaction pass.

One canonical file

.treetrace/tree.json is the machine-readable lineage: nodes, edges, correction chains, lessons, and eval candidates. The human-readable PROMPT_TREE.md narrates the same path.

Open & vendor-neutral

Lineage schema v0.3 is additive: consumers that only understand older versions keep reading nodes and edges and ignore the rest. It maps cleanly onto W3C PROV for provenance tooling.

Local-first by construction

TreeTrace reads local transcripts and writes to disk. Zero runtime dependencies, no accounts, no uploads, no telemetry, no LLM judge. Your transcripts never leave the machine.

The redaction gate

Every text, file, command, and evidence value runs the gate before write. Detected secrets become [REDACTED:rule-id]; a shadow scan checks the rendered output; redactions.json stores only content hashes, never raw secrets.

Derived from the same redacted tree
PROMPT_TREE.mdHuman-readable narrative of the build path
.treetrace/tree.jsonCanonical machine-readable lineage schema
.treetrace/failures.jsonFailure signals and correction chains
.treetrace/rejections.jsonTyped refusal, decline, and permission-denied events
.treetrace/evals.jsonlModel-agnostic eval cases from real corrections
.treetrace/agent-memory.mdHandoff memory pack for the next session

Make your own record.

One command, in any repo. It reads local transcripts and writes the ledger to disk. Node 18+.

$npx treetrace