/data/trial.csv, fall back to what it remembers from training, and write a
number that never touched the file. The claim records the citation faithfully and
the reader has no way to tell the difference. This is the silent fallback, and it
is the failure the grounding observer exists to catch.
mareforma.observe computes a second, separate signal: did the cited data
actually flow into the code that authored this finding? The answer is derived
from execution, never from what the producer declares. It is one of three states.
| State | Meaning |
|---|---|
GROUNDED | A read matching the cited source returned data inside the observed scope. |
UNGROUNDED | The scope was fully observed and the cited data did not arrive. This is the silent-fallback tell. |
OPAQUE | The observer could not see. A read may have happened across a boundary it cannot cross, so absence cannot be trusted. |
OPAQUE is first-class on purpose. A confident GROUNDED or UNGROUNDED across a
boundary the observer cannot see would be confidently wrong, which is worse than
admitting the blind spot.
Observing a scope
Wrap the span that authors a finding inobserve(...). Inside it, wrapped loaders
(builtins.open, sqlite3, and pandas / httpx / requests when you already
import them) record what data flowed, and a PEP-578 audit hook records the reads
and boundaries the loaders cannot see. On exit the observer computes the verdict
from what it captured.
with block closes, since it is a
function of the whole span. Read obs.verdict after the block, not inside it.
A claim must be authored inside the scope and signed after it closes. Asserting
a claim while its grounding scope is still open would bind a verdict computed from
a partial observation, so it is refused.
A read only grounds when it matches the citation
GROUNDED is not “some loader returned data.” It is “a read that matches the
cited source returned data.” Reading a config file, a tokenizer, or a .env
through the same wrapped open() is an incidental read: it does not ground the
finding. The match is what makes UNGROUNDED mean the cited data did not
arrive, not merely that nothing was read.
Matching is by identifier (same absolute file path, same database target, same
scheme://host/path for a URL) or, when you opt in with content_address=True,
by the sha256: hash of the returned bytes against a cited data id.
When the observer cannot see: seams
The observed scope propagates intoasyncio tasks but not into library-spawned
threads or a child process. A read on the far side of one of those boundaries is
invisible. Rather than call that invisible read a genuine absence, the observer
records a seam and returns OPAQUE:
- a thread start (
threading.Thread.start,_thread.start_new_thread), - a subprocess or new process (
subprocess,os.exec,os.fork, …), - a raw socket connection,
- a cited path opened through an uninstrumented reader (
os.open, C-extension I/O) that the loader wrappers do not cover.
Honest bounds
The observer names what it cannot do:- For a plain
open()file,GROUNDEDmeans the cited file was opened for reading and is non-empty. Thesqliteandhttpwrappers observe the actual returned rows and bytes; the file path proxies flow by file size, so it does not prove the bytes were consumed. - A resource opened before the scope (a module-level or pooled connection
reused inside it) is not wrapped, so its reads are invisible and the finding can
read
UNGROUNDED. Open the cited source inside the scope for the tell to hold. - The verdict is tamper-evidence over what a cooperating producer’s run did. It is not a proof against an adversarial operator, who signs under their own key.
The causal oracle: an independent check
The observer measures flow: did the cited bytes arrive. The oracle measures influence: does the finding actually depend on the data. It perturbs the input, re-runs the pipeline, and sees whether the finding moves. It never reads the observer’s log, so a detector that agreed with itself cannot look correct here.INFLUENCED only when the perturbation moves the finding
past the noise floor. When the effect sits inside the noise band the answer is
UNDECIDABLE, never a silent INFLUENCED.
Flow and influence are different constructs, so reconcile reads a mismatch as a
construct difference, not a detector error. A finding can read the cited data
(flow) and then ignore it (no influence). The one combination worth investigating
is UNGROUNDED yet INFLUENCED: the data demonstrably shapes the finding but no
cited read was seen, which points at a coverage gap the observer missed.
The split over a pipeline
A single verdict answers one finding.summarize aggregates many into the numbers
a report states: the GROUNDED / UNGROUNDED / OPAQUE fractions, how often an
incidental read occurred that citation binding correctly refused to count, and
what fraction of the cited reads the observer actually saw.
OPAQUE dominates, the observer cannot see enough of the pipeline for the
other numbers to mean anything, so the honest response is to instrument deeper
before publishing a measurement.
Bound into the claim
Pass the verdict toassert_finding or submit_finding and it is bound into the
signed in-toto statement, the append-only chain hash, and the queryable column,
so it is tamper-evident and re-checked on restore. The field is optional and
versioned: a claim asserted without the observer produces byte-identical signed
bytes to a pre-observer claim, and its absence is read as “no verdict recorded,”
never as tampering.
Grounding is a necessary floor for promotion, never sufficient. A finding that
execution shows is not grounded (UNGROUNDED or OPAQUE) never counts toward a
support-level promotion. A GROUNDED verdict still has to clear the
independent-signer counts described in Trust. A claim without a
verdict is unaffected, so the axis is purely additive.
Not the same as the declared classification
The observed axis is separate from the claim’s declaredclassification
(INFERRED / ANALYTICAL / DERIVED), which is what the producing agent asserts.
It is also separate from the soft grounding_score, a text-level hint that scores
a claim against its cited supports. Observed grounding is what execution shows.
The two never share a value space, so a reader can never confuse a
self-declaration with a computed result.
See the API reference for full signatures and
Findings for how a grounded finding earns its status.