foley.obs.recorder#

The run-recorder: owns the manifest, drives the OTel mirror, off by default (#11).

This is where the manifest-first design lives. A RunRecorder builds the RunManifest + its span tree from its own clock + id-factory — independent of the tracer — so the artifact is complete with a total no-op tracer. The façades reach it through facade_run() (a get-or-create, off-by-default context manager) and current_run(); a #7 find() opens an explicit run() scope so nested façade calls aggregate into one manifest.

Discipline (mirroring the rest of foley): off by default — a plain import foley + a plain foley.generate(...) is a byte-for-byte no-op that touches no store and stays dol-only. foley.obs.enable() / $FOLEY_OBS=1 / with foley.obs.run(...) turn it on. Emitting a manifest needs NO extra (all stdlib); only the OTel span mirror needs foley[obs]. Recording never raises into a façade — a store-write failure degrades gracefully.

Functions

configure(**overrides)

Apply ObsConfig overrides WITHOUT flipping enabled (the test-injection seam).

current_run()

The active RunRecorder, or _NULL_RUN (for the shared child span).

disable()

Turn observability off process-wide (façades revert to a byte-for-byte no-op).

enable(**overrides)

Turn observability on process-wide (and apply any ObsConfig overrides).

facade_run(op, *[, inputs, params])

The get-or-create seam every façade wraps its body in.

is_enabled()

Whether observability is on (via enable() or $FOLEY_OBS in {1,true,yes}).

reset()

Restore the default config (test teardown; clears injected store/tracer/clock).

run([op, inputs, params])

Open an explicit run scope that aggregates nested façade calls into ONE manifest.

Classes

ObsConfig([enabled, force_disabled, ...])

Process-wide observability configuration (flipped by enable()/disable()).

RunRecorder(manifest, *, tracer, redactor, ...)

Builds one RunManifest (span tree + composed shapes), tracer-independent.

class foley.obs.recorder.ObsConfig(enabled=False, force_disabled=False, redaction_mode=RedactionMode.hash, salt='foley-obs-v1', prefer_otel=True, run_store=None, tracer=None, clock=<built-in function time>, id_factory=<function ObsConfig.<lambda>>, now=<function _iso_now>)[source]#

Bases: object

Process-wide observability configuration (flipped by enable()/disable()).

clock()#

time() -> floating-point number

Return the current time in seconds since the Epoch. Fractions of a second may be present if the system clock provides them.

now()#

ISO-8601 UTC timestamp (real wall-clock; overridden to None in tests).

Return type:

Optional[str]

class foley.obs.recorder.RunRecorder(manifest, *, tracer, redactor, run_store, clock, id_factory)[source]#

Bases: object

Builds one RunManifest (span tree + composed shapes), tracer-independent.

add_step(step)[source]#

Append a SELECT-stage Step (#7), deep-redacting detail at record time.

The record-time net (belt over the emit-time sweep): event text carried in detail (under the query key) is hashed here so a manifest read straight off self.manifest.steps — before any emit — never holds raw narration. seq is assigned here (append position) when the caller left it None.

Return type:

None

emit()[source]#

Persist the manifest to the run store — swallowing any write failure.

Return type:

None

set_plan_ref(plan_ref)[source]#

Fill the reserved #8 plan_ref slot (a light join dict — no text).

Return type:

None

span(name, *, kind=None, **attributes)[source]#

Open a span: append a SpanRecord (from our own clock/ids) + mirror it.

foley.obs.recorder.configure(**overrides)[source]#

Apply ObsConfig overrides WITHOUT flipping enabled (the test-injection seam).

Return type:

None

foley.obs.recorder.current_run()[source]#

The active RunRecorder, or _NULL_RUN (for the shared child span).

foley.obs.recorder.disable()[source]#

Turn observability off process-wide (façades revert to a byte-for-byte no-op).

Return type:

None

foley.obs.recorder.enable(**overrides)[source]#

Turn observability on process-wide (and apply any ObsConfig overrides).

Return type:

None

foley.obs.recorder.facade_run(op, *, inputs=None, params=None)[source]#

The get-or-create seam every façade wraps its body in.

Nested (a run is already active) → a child span into the SAME manifest, no emit (the outer owner emits). Enabled + top-level → a new recorder that emits one manifest on exit. Disabled → the zero-cost _NULL_RUN.

foley.obs.recorder.is_enabled()[source]#

Whether observability is on (via enable() or $FOLEY_OBS in {1,true,yes}).

force_disabled (set by foley.runtime.offline_scope() for a telemetry-off posture) and a telemetry-off runtime ($FOLEY_OFFLINE) hard-override both — so offline mode’s “nothing leaves the device” contract holds even when $FOLEY_OBS is exported.

Return type:

bool

foley.obs.recorder.reset()[source]#

Restore the default config (test teardown; clears injected store/tracer/clock).

Return type:

None

foley.obs.recorder.run(op='run', *, inputs=None, params=None, **overrides)[source]#

Open an explicit run scope that aggregates nested façade calls into ONE manifest.

Forces observability on for the scope even when it is merely disabled (the #7 find() aggregation entrypoint). The one thing it does not override is the hard-off force_disabled flag: foley.runtime.offline_scope() sets it for a telemetry-off posture, so an explicit run() inside offline mode records and exports nothing — the “nothing leaves the device” contract holds on the find()/weave() paths too, not just the facade_run ones. Get-or-create: a nested run() reuses the active recorder. overrides are per-scope ObsConfig fields (e.g. run_store, prefer_otel, clock).