jouleclaw: an answer carries its verifier's backing, so a cached answer is not refused as ungrounded #276

Merged
dcharlot merged 1 commit from fix/answer-carries-entailment-backing into main 2026-10-16 09:41:29 -04:00

2026-10-16

Transaction Science
jouleclaw: an answer carries its verifier's backing, so a cached answer is not refused as ungrounded
All checks were successful
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m6s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 52s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 4s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m7s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m2s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m35s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m9s
An answer's `Provenance` stored the entailment results the verifier consulted,
but not the backing the verifier declared for them
(`VerificationReport::grounding_backing`). So jouleclaw-edge-cli's cache
rebuild could only declare the floor, `Backing::Unstated`, and
`require_substantive_grounding` refused every cached answer, including those
a single real NLI model had grounded. The rebuild's own comment named the
fix: persist the backing beside the entailments.

- `Provenance.entailment_backing: Option<Backing>`. It is omitted from the
  wire when absent, so answers and caches written before it keep their bytes
  and deserialize as before; absent reads as unstated.
- `compose_verified_answer` carries the report's backing into the answer.
- The cache rebuild restores that backing and never derives one from the
  cached `model_id`s. `require_substantive_grounding` still cross-checks it
  against the cached results: a fixture stays a fixture, and a backing that
  names a model other than the one whose results are attached is refused.

Tests:
- `the_answer_carries_the_verifiers_backing`: compose carries any backing,
  the floor included, unchanged.
- `a_cached_answer_keeps_the_backing_its_verifier_declared`: a single model's
  labels are grounded after the round trip.
- `a_cached_backing_is_never_promoted_or_trusted_past_its_results`: fixture
  stays refused, a mismatched model is refused, and an old entry is unstated.
- The answer round trip: the field is absent from the wire when None and
  round-trips when Some.

`AnswerOrRefusal` gains `allow(clippy::large_enum_variant)` with its reason.
There is one per query, and boxing would change a public enum for no
measurable gain.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 04:46:33 -04:00