Skip to main content

Observations

An observation is the atomic unit of memory in Kindling: a single, immutable record of something that happened during development.

Structure

Every observation has the same shape:

interface Observation {
id: string; // unique identifier (UUID)
kind: ObservationKind; // what happened
content: string; // the captured text
provenance: object; // source-specific metadata (free-form JSON)
ts: number; // epoch milliseconds
scopeIds: ScopeIds; // session / repo / agent / user / task
redacted: boolean; // whether the content has been forgotten
}

Observations are immutable. The only field that ever changes is redacted — set via forgetting — which masks the content without deleting the record.

Kinds

The set of kinds is fixed. There are no free-form tags or custom kinds; this keeps retrieval and adapters predictable across every tool.

KindCaptures
tool_callAn AI tool invocation (Read, Edit, Bash, …).
commandA shell command, typically with its exit code/output.
file_diffA file change, with the affected path(s).
errorAn error, often with a stack trace.
messageA user or assistant message. The CLI default.
node_startA workflow node began executing.
node_outputA workflow node produced output.
node_errorA workflow node failed.
node_endA workflow node finished (success or failure).

The first five are produced both manually (via kindling log) and by the Claude Code and OpenCode adapters. The node_* kinds are produced by the PocketFlow adapter.

Content

content is the human-readable text of the observation — the thing you will search for later. When logging manually, be specific and self-contained:

# Vague
kindling log "API issue"

# Specific
kindling log --kind error "API returns 500 when payload exceeds 1MB"

Provenance

provenance is a free-form JSON object carrying source-specific metadata — whatever the producer wants to attach. Adapters populate it richly; for example, a command observation may carry its exit code, a tool_call may carry the tool name and arguments, and an error may carry a stack trace.

Provenance is also where retrieval explainability comes from: it travels with the observation so you can always answer "where did this come from?".

Scope

Every observation carries a ScopeIds record. All fields are optional, so an observation can be globally visible or narrowly scoped:

FieldMeaning
sessionIdThe session that produced it.
repoIdThe repository it belongs to.
agentIdThe agent that produced it.
userIdThe user it belongs to.
taskIdThe task it belongs to (carried for provenance; not a retrieval filter today).

Scope is how search is narrowed:

kindling log "rate limit is 100 req/min" --session s-1 --repo ./my-project
kindling search "rate limit" --session s-1 --repo ./my-project

Capturing observations

# Manually
kindling log --kind error "JWT validation failed: token expired"

# Attached to an open capsule
kindling log --capsule cap_8a3f… "found the root cause"

Adapters capture them automatically — see Automatic Capture.

The kindling-service layer masks common secret patterns in observation content before persistence. Adapter-level filters (for example in the OpenCode package) add a second line of defence.

Forgetting

To redact an observation, use its exact ID:

kindling forget <observation-id>

This sets redacted: true and masks the content. The record remains (so history stays consistent), but it is excluded from results unless redacted items are explicitly requested.

Next