Makes the CL(4,1) field geometry legible, honestly. The engine owns the field;
this surfaces ONLY the exact scalar invariants it computes — never the raw
multivector — so the workbench shows "this is the geometry, it's exact, it
can't fake coherence" without a decorative blob or any motion.
Persist-first (the C3 gating work was Python, not React): the honest scalars
were computed live per turn but discarded. Now captured per turn into the
journal so a read-only surface has real evidence.
Backend:
- workbench/field_evidence.py: FieldEvidence computed from the engine result —
exact versor_condition, field_valid (vs the 1e-6 ceiling), a content-
addressed field_digest (sha256 of the engine-canonical array bytes), and
cga_inner(before, after) as the exact transition value. Raw field bytes
never cross the boundary: only floats + digests. validate() is fail-closed —
field_valid can never disagree with versor_condition vs the ceiling (the
wrong=0 analogue for the geometry). No engine math re-implemented
(versor_condition / cga_inner imported from algebra; bytes via array_codec).
- workbench/schemas.py: FieldEvidence dataclass; field_evidence on
ChatTurnResult + TurnJournalEntrySchema. schema-snapshot.json regenerated.
- workbench/journal.py + api.py: persisted at from_chat_turn; first-class read
endpoint GET /trace/{turn_id}/field (trace facet, consistent with /pipeline).
- workbench/replay.py: field_evidence classified CRITICAL — replay now also
proves field determinism (digest + scalars must match on re-execution).
Frontend:
- types/api.ts FieldEvidence + field_evidence passthrough; client/query hook;
FieldInvariantCard (measured value vs ceiling, cga_inner transition, digests;
honest missing_evidence; no blob, no motion); Trace route Field tab.
Honest-empty for pre-widening journal rows (missing_evidence). Deferred:
cross-turn field-coherence trends, session-level field persistence.
Validation: 138 workbench/practice Python tests (incl. non-vacuous field guards
+ replay field-determinism); 465/465 frontend incl. schemaDrift; pnpm build
clean; git diff --check clean. No generate.derivation / reliability_gate /
stream / field.propagate / vault.store imports.
169 lines
6.2 KiB
Python
169 lines
6.2 KiB
Python
"""C3 field-substrate evidence — exact scalar invariants for a turn's field.
|
|
|
|
The engine owns the CL(4,1) field; this module surfaces, read-only, only the
|
|
EXACT scalars the engine computes over it — ``versor_condition`` (the
|
|
``< 1e-6`` validity invariant) and the ``cga_inner`` transition value — plus a
|
|
content-addressed ``field_digest``. The raw multivector NEVER leaves this
|
|
module: only floats and a hash cross the boundary, so the workbench can show
|
|
"this is the geometry, it's exact, it can't fake coherence" without rendering a
|
|
decorative blob.
|
|
|
|
No engine math is re-implemented here: ``versor_condition`` and ``cga_inner``
|
|
are imported from :mod:`algebra` and the field bytes are encoded by the
|
|
engine-owned :func:`core.array_codec.encode_array`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
|
|
from algebra import cga_inner, versor_condition
|
|
from core.array_codec import encode_array
|
|
from workbench.schemas import FieldEvidence
|
|
|
|
# The non-negotiable CORE invariant (CLAUDE.md; enforced at field/state.py:223
|
|
# as ``versor_condition(F) >= 1e-6`` -> raise). Surfaced so the UI can render
|
|
# the measured value against the bound rather than asserting validity blindly.
|
|
VERSOR_CONDITION_CEILING: float = 1e-6
|
|
|
|
|
|
def _field_digest(field_array: Any) -> str:
|
|
"""Content-addressed digest of a field multivector — provenance, not the blob.
|
|
|
|
Hashes the engine-canonical ``encode_array`` payload (dtype + shape + raw
|
|
bytes), so the digest fully determines the array yet exposes none of it.
|
|
"""
|
|
|
|
payload = encode_array(np.asarray(field_array))
|
|
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
|
return "sha256:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def missing_field_evidence(
|
|
*,
|
|
trace_hash: str | None,
|
|
reason: str,
|
|
) -> FieldEvidence:
|
|
"""Honest absence — old journal rows have no persisted field evidence."""
|
|
|
|
return FieldEvidence(
|
|
schema_version="field_evidence_v1",
|
|
status="missing_evidence",
|
|
missing_reason=reason,
|
|
trace_hash=trace_hash,
|
|
versor_condition=None,
|
|
versor_condition_ceiling=VERSOR_CONDITION_CEILING,
|
|
field_valid=None,
|
|
field_digest=None,
|
|
parent_field_digest=None,
|
|
transition_inner_product=None,
|
|
)
|
|
|
|
|
|
def validate(record: FieldEvidence) -> None:
|
|
"""Fail-closed honesty gate.
|
|
|
|
A recorded record must carry the scalars, and ``field_valid`` must agree
|
|
with ``versor_condition < ceiling`` EXACTLY — the field card can never claim
|
|
a valid field while the measured condition breaches the bound (the wrong=0
|
|
analogue for the geometry surface).
|
|
"""
|
|
|
|
if record.status != "recorded":
|
|
return
|
|
if record.versor_condition is None or record.field_valid is None:
|
|
raise ValueError(
|
|
"recorded field evidence missing versor_condition / field_valid"
|
|
)
|
|
if record.field_digest is None:
|
|
raise ValueError("recorded field evidence missing field_digest")
|
|
if record.field_valid != (record.versor_condition < record.versor_condition_ceiling):
|
|
raise ValueError(
|
|
"field_valid disagrees with versor_condition vs ceiling: "
|
|
f"valid={record.field_valid} condition={record.versor_condition:.3e} "
|
|
f"ceiling={record.versor_condition_ceiling:.3e}"
|
|
)
|
|
|
|
|
|
def field_evidence_from_result(result: Any) -> FieldEvidence:
|
|
"""Build validated field evidence from a live engine turn result.
|
|
|
|
Reads the engine's ``field_state_after`` / ``field_state_before`` and
|
|
``versor_condition``; emits only scalars + digests. Returns an honest
|
|
``missing_evidence`` record when the result carries no field state.
|
|
"""
|
|
|
|
trace_hash = str(getattr(result, "trace_hash", "") or "") or None
|
|
after = getattr(result, "field_state_after", None)
|
|
if after is None or getattr(after, "F", None) is None:
|
|
return missing_field_evidence(
|
|
trace_hash=trace_hash, reason="field_state_not_available"
|
|
)
|
|
|
|
measured = getattr(result, "versor_condition", None)
|
|
condition = (
|
|
float(measured) if measured is not None else float(versor_condition(after.F))
|
|
)
|
|
field_digest = _field_digest(after.F)
|
|
|
|
before = getattr(result, "field_state_before", None)
|
|
if before is not None and getattr(before, "F", None) is not None:
|
|
parent_field_digest: str | None = _field_digest(before.F)
|
|
transition_inner_product: float | None = float(cga_inner(before.F, after.F))
|
|
else:
|
|
parent_field_digest = None
|
|
transition_inner_product = None
|
|
|
|
record = FieldEvidence(
|
|
schema_version="field_evidence_v1",
|
|
status="recorded",
|
|
missing_reason=None,
|
|
trace_hash=trace_hash,
|
|
versor_condition=condition,
|
|
versor_condition_ceiling=VERSOR_CONDITION_CEILING,
|
|
field_valid=condition < VERSOR_CONDITION_CEILING,
|
|
field_digest=field_digest,
|
|
parent_field_digest=parent_field_digest,
|
|
transition_inner_product=transition_inner_product,
|
|
)
|
|
validate(record)
|
|
return record
|
|
|
|
|
|
def _coerce(raw: Any) -> FieldEvidence:
|
|
if isinstance(raw, FieldEvidence):
|
|
return raw
|
|
if not isinstance(raw, dict):
|
|
raise ValueError("field_evidence must be an object")
|
|
return FieldEvidence(
|
|
schema_version=raw["schema_version"],
|
|
status=raw["status"],
|
|
missing_reason=raw.get("missing_reason"),
|
|
trace_hash=raw.get("trace_hash"),
|
|
versor_condition=raw.get("versor_condition"),
|
|
versor_condition_ceiling=raw.get(
|
|
"versor_condition_ceiling", VERSOR_CONDITION_CEILING
|
|
),
|
|
field_valid=raw.get("field_valid"),
|
|
field_digest=raw.get("field_digest"),
|
|
parent_field_digest=raw.get("parent_field_digest"),
|
|
transition_inner_product=raw.get("transition_inner_product"),
|
|
)
|
|
|
|
|
|
def field_evidence_from_journal_entry(entry: Any) -> FieldEvidence:
|
|
"""Project a persisted journal row into the field-evidence read model."""
|
|
|
|
raw = getattr(entry, "field_evidence", None)
|
|
if raw is None:
|
|
return missing_field_evidence(
|
|
trace_hash=getattr(entry, "trace_hash", None),
|
|
reason="field_evidence_not_persisted",
|
|
)
|
|
record = _coerce(raw)
|
|
validate(record)
|
|
return record
|