fix(cognition): expose hash_surface; point the register-matrix pin at it
*** VERIFICATION PENDING — see the bottom of this message. *** 99 of the 107 full-suite failures are one test, red on clean main since 2026-07-23, and it is guarding the wrong field. WHAT IS NOT WRONG. test_register_matrix_trace_hash_invariant — the load-bearing ADR-0072 truth-path-isolation claim — passes on all 99 registers on pristine main. trace_hash does not vary across the register axis. The invariant holds; the seals and wrong=0 are untouched. WHAT IS. test_register_matrix_canonical_surface_byte_identical asserts on CognitiveTurnResult.surface, on its docstring's claim that this is "the truth-path field compute_trace_hash consumes". That stopped being true when Phase 0 flipped resolve_surface to response-first precedence and introduced SurfaceResolution.hash_surface as the register-invariant capture. Since then pipeline.py folds hash_surface into compute_trace_hash, while result.py documents surface as "final voiced surface (what the user sees)". So the test demanded that registers NOT differ on the served surface — the opposite of what the register axis exists for, and a direct contradiction of test_register_substantive_consumption.py, which pins that they DO differ and is green in smoke. Two tests on main asserting opposite things about the same bytes; the red one is in neither gate, so it stayed red. ROOT CAUSE, and why this is a code fix rather than a test edit: hash_surface was pipeline-local. The invariant was unobservable from a turn result, which is why the test reached for the nearest visible field and drifted. Exposing it makes the contract testable at the seam that owns it: - CognitiveTurnResult.hash_surface — new field, populated from the value the pipeline already computed - evals/cognition CaseResult carries it through - the test asserts byte-identity on hash_surface, with the history recorded so it cannot drift back No stored baseline to regenerate — the fixture computes it live. [Verification]: NOT YET RUN. The register matrix is ~16 min and was still executing when the working tree had to be committed; its output buffers through tail, so no partial signal was available. Committed to preserve the work with an honest label rather than to claim it passes. DO NOT MERGE until `pytest tests/test_cognition_eval_register_matrix.py` is green — expected 0 failed where main has 99, with test_register_matrix_trace_hash_invariant still passing. Result to follow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
This commit is contained in:
parent
3b2c7608a9
commit
bf17f42834
4 changed files with 49 additions and 12 deletions
|
|
@ -863,6 +863,7 @@ class CognitiveTurnPipeline:
|
|||
proposition=response.proposition,
|
||||
articulation=response.articulation,
|
||||
surface=surface,
|
||||
hash_surface=hash_surface,
|
||||
walk_surface=response.walk_surface,
|
||||
articulation_surface=articulation_surface,
|
||||
dialogue_role=response.dialogue_role,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,19 @@ class CognitiveTurnResult:
|
|||
vault_hits: int
|
||||
recall_energy_class: str | None = None
|
||||
|
||||
#: The register-INVARIANT truth-path bytes — what ``compute_trace_hash``
|
||||
#: actually folds (ADR-0069 inv C, ADR-0077 R6). Distinct from ``surface``,
|
||||
#: which is the served, register-decorated string the user reads.
|
||||
#:
|
||||
#: Exposed 2026-07-25. It was pipeline-local before that, which made the
|
||||
#: truth-path-isolation invariant unobservable from a turn result — so
|
||||
#: ``test_register_matrix_canonical_surface_byte_identical`` asserted it on
|
||||
#: ``surface`` instead, and went red across all 99 registers the moment
|
||||
#: Phase 0 flipped ``resolve_surface`` to response-first precedence and
|
||||
#: moved the invariant here. The contract was never broken; it just could
|
||||
#: not be seen. Empty string ⇒ identical to ``surface`` (no divergence).
|
||||
hash_surface: str = ""
|
||||
|
||||
# --- intent / graph telemetry ---
|
||||
intent: DialogueIntent | None = None
|
||||
proposition_graph: PropositionGraph | None = None
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ class CaseResult:
|
|||
versor_condition: float
|
||||
trace_hash: str
|
||||
surface: str
|
||||
#: The register-invariant truth-path bytes (what compute_trace_hash folds).
|
||||
#: ``surface`` is the served, register-decorated string; these two are
|
||||
#: different fields with different contracts — see CognitiveTurnResult.
|
||||
hash_surface: str = ""
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
|
|
@ -71,6 +75,7 @@ def _run_case(case: dict[str, Any], pipeline: CognitiveTurnPipeline) -> CaseResu
|
|||
versor_condition=result.versor_condition,
|
||||
trace_hash=result.trace_hash,
|
||||
surface=result.surface,
|
||||
hash_surface=result.hash_surface or result.surface,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -302,24 +302,42 @@ def test_register_matrix_versor_condition_byte_identical(
|
|||
def test_register_matrix_canonical_surface_byte_identical(
|
||||
register_report, baseline_by_id,
|
||||
):
|
||||
"""``CognitiveTurnResult.surface`` is the pre-decoration /
|
||||
canonical composer output (the truth-path field
|
||||
``compute_trace_hash`` consumes). Substantive register transforms
|
||||
apply downstream on ``turn_log[-1].surface``; the canonical
|
||||
must remain byte-identical across the register axis.
|
||||
"""``hash_surface`` — the truth-path bytes ``compute_trace_hash`` folds —
|
||||
must be byte-identical across the register axis.
|
||||
|
||||
If this test ever fails for a non-null register, the substantive
|
||||
transform has leaked into the canonical surface and the
|
||||
truth-path-isolation contract is broken.
|
||||
If this fails for a non-null register, a substantive transform has leaked
|
||||
into the truth path and the isolation contract is broken.
|
||||
|
||||
ASSERTED ON ``hash_surface``, NOT ``surface`` (corrected 2026-07-25).
|
||||
This test used to read ``surface``, on the docstring's claim that it was
|
||||
"the pre-decoration / canonical composer output". That stopped being true
|
||||
on 2026-07-23: Phase 0 flipped ``resolve_surface`` to response-first
|
||||
precedence and introduced ``SurfaceResolution.hash_surface`` as the
|
||||
register-invariant capture, so ``pipeline.py`` now folds *that* into
|
||||
``compute_trace_hash`` while ``surface`` carries the served, decorated
|
||||
bytes (``CognitiveTurnResult.surface``: "final voiced surface (what the
|
||||
user sees)").
|
||||
|
||||
Asserting byte-identity on ``surface`` therefore demanded that registers
|
||||
NOT differ — the exact opposite of what the register axis is for, and in
|
||||
direct contradiction with ``test_register_substantive_consumption.py``,
|
||||
which pins that they DO differ and is green in smoke. This file is in
|
||||
neither `smoke` nor `deductive`, so it went red on main across all 99
|
||||
registers and stayed there.
|
||||
|
||||
The invariant itself was never broken: ``test_register_matrix_trace_hash_invariant``
|
||||
passed throughout, which is the load-bearing proof, since a leak into
|
||||
``hash_surface`` would move ``trace_hash`` immediately. What was missing was
|
||||
the ability to *observe* the field — now exposed on the turn result.
|
||||
"""
|
||||
register_id, report = register_report
|
||||
rows = _diff_rows(
|
||||
register_id, baseline_by_id, _by_id(report), "surface",
|
||||
register_id, baseline_by_id, _by_id(report), "hash_surface",
|
||||
)
|
||||
assert not rows, (
|
||||
f"CANONICAL SURFACE LEAK under register {register_id!r} — "
|
||||
"substantive transforms have escaped into the pre-decoration "
|
||||
"surface that feeds compute_trace_hash.\n" + "\n".join(rows)
|
||||
f"TRUTH-PATH SURFACE LEAK under register {register_id!r} — "
|
||||
"substantive transforms have escaped into hash_surface, the bytes "
|
||||
"compute_trace_hash folds.\n" + "\n".join(rows)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue