Skip to content
internal/trace
Edit
On this page

API reference for the trace package — reads the strictcli process trace store to resolve which tool invoked a deletion.

#internal/trace

#internal/trace

Package trace reads the strictcli process trace store, so a deletion can record who ran it.

The store is a shared, append-only JSONL record of process ancestry: at the seam where one command-line tool spawns another, the spawning invocation writes one line describing itself and hands that line's identifier to the child in STRICTCLI_TRACE_PARENT. saferm is a consumer, never a writer. It parses the variable and reads the store itself, per the normative specification (strictcli's docs/process-trace-store.md), because the framework deliberately exposes no accessor for the ancestry stack -- nothing in the framework may branch on data no framework code reads.

Everything here is observational. A capture cannot fail a deletion: an absent variable, a polluted one, a pruned store, a torn line and a parent that resolves to nothing are all legal states, each recorded as an anomaly and carried on from. Consumers noticing dangling parents is the store's primary failure-detection channel, which is why the anomalies are written into the record rather than discarded.

A capture resolves the FULL ancestry chain at capture time and keeps the flattened entries, not only their identifiers, so the record stays self-contained: age-based pruning of the store can never orphan it. The identifiers are kept alongside for correlation with whatever store data still exists.

#ParentEnv

Go go
const ParentEnv = "STRICTCLI_TRACE_PARENT"

ParentEnv is the one variable ancestry travels through. It carries exactly one thing: the identifier of the entry describing the process that spawned this one.

#AnomalyMalformedParentValue

Go go
const AnomalyMalformedParentValue = "malformed-trace-parent"

AnomalyMalformedParentValue: the environment variable was set to something that is not a canonical identifier. Recorded verbatim.

#AnomalyDanglingParent

Go go
const AnomalyDanglingParent = "dangling-parent"

AnomalyDanglingParent: an identifier resolved to no entry -- the store was pruned or missing, the writer was another tool, or someone set the variable by hand. Legal by design, and the store's primary failure-detection channel.

#AnomalyMalformedEntry

Go go
const AnomalyMalformedEntry = "malformed-entry"

AnomalyMalformedEntry: a line in a partition could not be read as an entry -- torn by a non-atomic write, missing one of the thirteen keys, or carrying an unparseable identifier.

#AnomalyStoreUnreadable

Go go
const AnomalyStoreUnreadable = "store-unreadable"

AnomalyStoreUnreadable: a partition or the store directory could not be read at all. A store that does not exist is NOT this: that is an ordinary dangling parent.

#AnomalyChainCycle

Go go
const AnomalyChainCycle = "chain-cycle"

AnomalyChainCycle: walking parent_id revisited an identifier. No store a conforming writer produces can contain one, since an entry's parent is always older than itself.

#AnomalyOversizedField

Go go
const AnomalyOversizedField = "oversized-entry-field"

AnomalyOversizedField: a string an entry contributes to the embedded chain was longer than a chain entry may carry, and was truncated. The line was conforming -- nothing in the entry rules bounds a value's length -- so this is the consumer stating what it kept, not a complaint about the writer.

#AnomalyAnomaliesDropped

Go go
const AnomalyAnomaliesDropped = "anomalies-dropped"

AnomalyAnomaliesDropped: the capture saw more anomalies than one record may carry. Synthetic, always last, and present only when something was dropped: it names how many, so a truncated anomaly list can never read as a complete one.

#Entry

Go go
type Entry struct

Entry is one line of the store: an invocation that spawned a child. Every key is always present in a conforming line, so an absent one makes the line malformed rather than defaulted.

#Anomaly

Go go
type Anomaly struct

Anomaly is something the capture saw and could not treat as well-formed.

#Capture

Go go
type Capture struct

Capture is what one deletion records about its ancestry.

Chain holds the flattened ancestry, nearest caller first, so the record stays readable after the store is pruned. ChainIDs is the same walk as bare identifiers, kept for correlation with whatever store data still exists.

#Collect

Go go
func Collect() *Capture

Collect resolves the ancestry of the running process from the store.

It returns nil when STRICTCLI_TRACE_PARENT is unset -- nothing claimed this invocation, which is not an anomaly and is the state every deletion is in until callers upgrade to a framework that writes the store.

#StoreDir

Go go
func StoreDir(home string) string

StoreDir is the store's literal path under home.

It is deliberately NOT derived from XDG_DATA_HOME or any other variable, despite matching the XDG default: a writer that honoured XDG_DATA_HOME and one that did not would write to two stores on the same machine, and a chain crossing them would dangle at both ends while both writers behaved correctly.

#Capture.Origin

Go go
func (c *Capture) Origin() (name, version *string)

Origin is the immediate caller's declared name and version -- the two values a deletion records as its origin. Both are nil when nothing resolved, which is what "no tool claimed this" means.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search