classification and self-declared
supports[] / contradicts[] edges. That is enough to track provenance, but
it cannot answer one question: does the result actually support or contradict
the hypothesis? An agent can write a refutation next to a confirming number
and the graph records it faithfully.
The trust layer (mareforma.trust) closes that gap. The same assertion is
expressed as a falsifiable proposition, bound to a pre-registered
prediction, measured by an effect estimate. The direction of evidence
(the bearing) is computed, never declared. A count over independent data
then derives a status.
It is additive. Every finding still rides a signed claim as its attestation
(who asserted it, when), so it appears in query() with a support_level
exactly like any other claim. The trust layer adds the structured meaning on
top.
The proposition: the unit of sameness
AProposition is a truth-apt, observer-independent claim about the world,
built from typed parts rather than a sentence:
content_idis the answer:sha256over the normalized(subject, relation, object, scope, direction, magnitude). Same truth conditions produce the same id, on any host, in any language (tokens are NFC-normalized, casefolded, whitespace-collapsed; the byte serialization is RFC 8785).frame_idis the question: the same hash withdirectionandmagnitudedropped. Two propositions share a frame when they are about the same question. They are the same proposition when their direction and magnitude also match; they contradict when their directions are contraries (INCREASESvsDECREASES,PRESENTvsABSENT).
UNSPECIFIED) and state a non-empty scope. An unscoped,
directionless assertion forbids no observation and is refused.
The prediction: a pre-registered rule
APrediction is the decision rule, bound to the proposition before the
numbers are seen. It is what makes the bearing earned rather than chosen.
- Superiority declares the predicted side of the null
(
direction_of_interest). - Equivalence (TOST) declares an equivalence region
(
equivalence_lower/equivalence_upper) around the null, for testing aNO_EFFECTproposition.
The estimate: a value plus its uncertainty
AnEffectEstimate carries the result and exactly what the gate needs:
p_value, a full (ci_lower, ci_upper, ci_level) triple, or both. The
estimate runs its own consistency checks on construction and refuses
inconsistent input (a CI that does not bracket the estimate, a non-finite
bound, a p_value outside [0, 1]) rather than storing it.
The bearing: computed, not declared
compute_bearing is the gate. It reads the pre-registered rule and the realised
estimate and returns supports / refutes / neutral. The agent never picks
the label.
alpha (the direction is pre-registered). The estimate
is significant when p_value < 2*alpha (the supplied p is two-sided by the
metafor/escalc convention, so the one-sided alpha level is 2*alpha), or,
with no p, when the (1 - 2*alpha) CI excludes the null. The sign of
estimate - null is then compared against the pre-registered direction. For
equivalence, a CI lying entirely inside the region supports the no-effect
proposition, entirely outside refutes it, and straddling a margin is neutral.
Because the label is a function of the registered rule and the number, an agent
cannot relabel a refutation as support.
The status: derived from independent data
Status is a count over independent lines of evidence on a single
content_id, not an assertion of truth and not a human gate. Independence is a
distinct-artifact heuristic: two findings count as independent when their
data_id differs.
| Status | Meaning |
|---|---|
UNTESTED | No supporting or refuting lines yet. |
PRELIMINARY | Exactly one independent supporting line. |
CORROBORATED | Two or more independent supporting lines, none refuting. |
REFUTED | At least one independent refuting line, none supporting. |
CONTESTED | Independent support and independent refute on the same proposition. |
REFUTED and CONTESTED are derived labels, not auto-refutation: REFUTED
means “no surviving independent support,” not “this proposition is false.”
Status is a versioned policy (status_policy@v1) over durable stored
counts, never baked into the schema. Improving the rule later is a new policy
over the same data, not a migration.
Frame-level contest
Separately from a proposition’s own status, its frame is contested when a contrary proposition in the same frame has at least one independent supporting line. The contest is surfaced at read time; it does not silently corroborate or refute either side.Recording a finding
assert_finding ties it together in one call: it validates the inputs, computes
the bearing, writes a signed claim as the attestation, persists the evidence
tree, and derives the status.
CORROBORATED:
assert_finding is idempotent on (content_id, data_id): re-asserting the same
finding on the same dataset returns the prior finding rather than double-counting
it.
How this relates to support levels
The two layers answer different questions and run side by side:support_level(PRELIMINARY/REPLICATED/ESTABLISHED, see Trust) is about a claim’s provenance: did independent agents converge on a shared upstream, and did a human validate it.Status(UNTESTED,PRELIMINARY,CORROBORATED,REFUTED,CONTESTED) is about a proposition’s evidence: how many independent datasets support or refute it, with the direction of each computed from a pre-registered rule.