core/docs/adr/ADR-0229-contract-proof-replay-adapter-boundary.md

35 KiB

ADR-0229: Contract/Proof Replay Adapter Boundary

Status: Proposed

Date: 2026-06-22

Scope: Kernel diagnostics, candidate replay classification, residual-gated practice loop

Depends on:

  • ADR-0225 ContractResidual read model
  • ADR-0226 Residual-Gated Practice Loop v1
  • ADR-0227 ComputeBudgetPolicy envelope
  • ADR-0228 GeometricSearchRun envelope
  • PR #870 inert GeometricSearchRun shell

1. Summary

The Contract/Proof Replay Adapter is a diagnostic replay classifier. It is the first boundary after GeometricSearchRun that may classify one exact candidate attempt under the existing organ-specific contract and proof authorities.

The adapter consumes an immutable run identity, one attempt from that run, the exact immutable candidate reconstruction, the complete upstream identity chain, the organ-specific contract replay target, the applicable proof obligations, and explicit policy/schema versions. It emits either an immutable replay result or an immutable replay refusal.

Successful replay has exactly three semantic dispositions:

contract_refused
contract_closed_but_proof_refused
contract_and_proof_closed

These dispositions are replay evidence. Even contract_and_proof_closed is not an answer, serving decision, promotion, mutation, global uniqueness decision, or sealed practice trace.

This ADR defines the boundary before any replay implementation exists. It adds no code, replay execution, candidate generation, operator implementation, repair, answer production, serving behavior, or mutation path.

2. Why this exists

PR #870 implemented the inert GeometricSearchRun shell authorized by ADR-0228. That shell can represent a bounded run, ordered candidate attempts, operator provenance, structural budget consumption, and replay-pending state. It deliberately cannot decide whether a candidate is contract-refused, contract-closed, proven, answerable, promotable, or serveable.

The missing boundary is:

candidate attempt + exact candidate reconstruction
→ same organ-specific ContractAssessment replay
→ applicable proof/verifier replay
→ replay disposition evidence

Without a separate adapter, future code could collapse exploration into truth: successful candidate construction could be mistaken for contract closure, absence of blockers could be mistaken for proof, or a search run could be mutated after the fact to claim that it had solved the problem. A dedicated adapter preserves the duality required by the loop: candidate reconstruction is the forward proposal; contract and proof replay are its corrective conjugates.

The intrinsic state space is therefore not a list of candidate scores. It is a typed relation between one identified attempt, one exact reconstruction, the original obstruction chain, and the unchanged authorities that can close or refuse the obligations.

3. Architectural directions considered

3.1 Monolithic replay, selection, and answer controller

A single controller could replay contracts and proofs, compare candidates, select an answer, and serve it. This would combine diagnostic classification, uniqueness, answer selection, and serving authority. A local replay success could silently become a user-facing assertion. Rejected.

3.2 Closure inferred from blocker absence or generation success

The run could classify a candidate as closed when candidate construction succeeds or no known blocker remains. This avoids replay cost but changes the meaning of ContractAssessment: failure to observe a blocker is not execution of the existing contract, and a generated candidate is not proof. Rejected.

3.3 Replay state written back into GeometricSearchRun

The adapter could mutate CandidateAttempt.replay_status or replace the run with an updated run. This would destroy the immutability of the exploration evidence and make the replay classifier a retroactive author of its own input. It would also create a reverse dependency from replay into search. Rejected.

3.4 Immutable one-attempt replay classifier

The adapter validates one exact run/attempt/reconstruction chain, invokes only the already-authoritative contract and proof seams, and emits a separate content-addressed result or refusal. Candidate comparison, answer production, sealing, display, serving, and promotion remain later boundaries. Selected.

This direction makes adapter failure distinct from candidate refusal and makes illegal authority transitions visible in the output type.

4. Decision

The v1 policy identity is conceptually:

CONTRACT_PROOF_REPLAY_POLICY_VERSION = "contract_proof_replay.v1"

The public conceptual outcome is a discriminated union:

ReplayAdapterOutcome = ReplayAdapterResult | ReplayAdapterRefusal

ReplayAdapterResult exists only when the input identity chain was validated and the required replay authorities ran far enough to produce a semantic candidate classification.

ReplayAdapterRefusal exists when the adapter cannot lawfully classify the candidate because input, identity, policy, schema, authority, or obligation validation failed. A refusal is not a partial replay result and must not masquerade as contract_refused.

The adapter may decide only:

candidate replay disposition under existing contract/proof obligations

It does not decide that the problem is globally solved. It does not select an answer. It does not alter the original ContractAssessment authority. It is evidence, not authority beyond replay classification.

5. Position in the loop

Dependency and authority remain one-way:

ContractAssessment
→ ContractResidual
→ SearchGateDecision
→ ComputeBudgetDecision
→ GeometricSearchRun
→ Contract/Proof Replay Adapter
→ future SealedPracticeTrace
→ future Workbench read-only projection

There is no reverse dependency. In particular:

  • the adapter does not create or alter a GeometricSearchRun;
  • the run, budget, gate, residual, and assessment modules do not import the adapter;
  • a future sealed trace may reference replay results but may not rewrite them;
  • Workbench may later display persisted replay evidence but may not invoke, repair, or override replay; and
  • no stage in this chain gains serving authority from its position in the chain.

The adapter is one-attempt-in and one-outcome-out. Batch traversal and ordering belong to a later orchestrator. If such an orchestrator is authorized, it must visit attempts in the canonical GeometricSearchRun.candidate_attempts order.

6. Inputs

6.1 Candidate reconstruction boundary

Candidate reconstruction is a separate immutable value. It is never an in-place repair of the original ProblemFrame.

The conceptual reconstruction contains enough canonical state to replay the candidate without consulting hidden mutable state:

@dataclass(frozen=True, slots=True)
class CandidateReconstruction:
    candidate_reconstruction_digest: str
    run_id: str
    attempt_id: str
    original_problem_frame_digest: str
    candidate_problem_frame_digest: str | None
    frame_delta_digest: str | None
    candidate_bindings: tuple[object, ...]
    candidate_relations: tuple[object, ...]
    candidate_targets: tuple[object, ...]
    evidence_spans: tuple[SourceSpan, ...]
    operator_set_id: str
    operator_set_version: str
    operator_id: str
    operator_version: str

This is a conceptual schema, not an implementation instruction to introduce a universal intermediate representation. A future implementation must reuse the existing typed ProblemFrame values that the relevant organ consumes. The schema names the identity obligations; it does not authorize a new parser, candidate format, operator, or frame-delta engine.

Exactly one of candidate_problem_frame_digest or frame_delta_digest must be present unless a later organ-specific schema explicitly requires both. Any frame delta must reconstruct to one exact candidate ProblemFrame before contract replay. Reconstruction cannot read source text again to infer missing roles.

The candidate may carry:

candidate ProblemFrame or frame-delta identity
candidate bindings
candidate relations
candidate targets
candidate evidence spans
operator provenance
attempt identity

The candidate may not:

overwrite the original frame
alter source text
synthesize evidence spans
deduplicate evidence spans
repair upstream records in place
claim hidden provenance

6.2 ReplayAdapterInput

The conceptual adapter input is:

@dataclass(frozen=True, slots=True)
class ReplayAdapterInput:
    input_digest: str
    replay_policy_version: str
    run_id: str
    run_policy_version: str
    attempt_id: str
    attempt_index: int
    candidate_digest: str
    candidate_reconstruction_digest: str
    problem_frame_digest: str
    original_contract_assessment_id: str
    candidate_organ: str
    residual_ids: tuple[str, ...]
    gate_decision_id: str
    budget_id: str
    operator_set_id: str
    operator_set_version: str
    contract_replay_target: str
    proof_obligation_refs: tuple[str, ...]
    schema_versions: tuple[tuple[str, str], ...]

The input is accompanied by immutable values for the identified original ProblemFrame, refused original ContractAssessment, residual context, gate, budget, GeometricSearchRun, exact CandidateAttempt, and exact CandidateReconstruction. An implementation may consume a validated identity-bearing projection instead of a complete upstream value when that projection contains every field required to reproduce the identity and cross-chain checks. Identities are not substitutes for values that the contract/proof authorities must evaluate; values are not substitutes for canonical identities.

The input binds to one candidate attempt from one exact run. It cannot contain a batch, a candidate range, a preferred candidate, or a fallback candidate.

The following validations are mandatory before replay:

  1. run_id reproduces from the supplied immutable run.
  2. attempt_index is in range and the attempt at that index is structurally identical to the supplied attempt.
  3. attempt_id reproduces canonically and belongs to run_id.
  4. CandidateAttempt.input_digest == GeometricSearchRun.input_digest.
  5. candidate_digest equals both the attempt's candidate digest and the independently recomputed digest of the canonical candidate content: the candidate frame/delta, bindings, relations, targets, and exact spans.
  6. candidate_reconstruction_digest reproduces from the complete supplied reconstruction envelope, including its run/attempt/operator provenance and the validated candidate_digest.
  7. Candidate evidence spans equal the supplied reconstruction spans exactly, in order, including duplicates.
  8. Candidate operator id/version equal the attempt operator id/version, and the reconstruction's operator-set id/version equal the run's closed operator-set identity.
  9. Problem-frame, assessment, residual, gate, and budget identities equal the original chain bound into the run input.
  10. The original assessment identity reproduces, its runnable state is refused, and its candidate_organ equals the input candidate_organ.
  11. contract_replay_target is the statically allowlisted authority for that candidate organ. No dynamic target lookup is permitted.
  12. proof_obligation_refs exactly equal the obligations declared by the existing organ/proof policy. The adapter cannot add, remove, reorder, or deduplicate them.
  13. Every schema and policy version is supported explicitly.
  14. schema_versions contains unique schema names in ascending lexical order; missing, duplicate, or reordered entries are invalid.
  15. input_digest reproduces from the complete structural payload defined in Section 13.

Any failed validation produces ReplayAdapterRefusal; no contract or proof authority is called.

7. Outputs

7.1 ReplayAdapterResult

@dataclass(frozen=True, slots=True)
class ReplayAdapterResult:
    replay_result_id: str
    replay_policy_version: str
    input_digest: str
    run_id: str
    attempt_id: str
    candidate_digest: str
    contract_replay_assessment_id: str
    proof_obligation_refs: tuple[str, ...]
    proof_replay_refs: tuple[str, ...]
    replay_disposition: ReplayDisposition
    reason_codes: tuple[str, ...]
    evidence_spans: tuple[SourceSpan, ...]
    explanation: str

contract_replay_assessment_id is an adapter-owned content identity over the new diagnostic ContractAssessment replay output, or an equivalent immutable diagnostic reference if that authority already exposes one. It does not add an ID field to, mutate, or replace the existing ContractAssessment type.

proof_replay_refs identify the exact existing verifier/proof outcomes in the same order as proof_obligation_refs. They are references to replay evidence, not newly invented proofs.

evidence_spans copies the candidate reconstruction's exact ordered spans. Contract and proof records retain their own evidence references. The adapter does not merge, widen, synthesize, sort, or deduplicate spans.

7.2 ReplayAdapterRefusal

@dataclass(frozen=True, slots=True)
class ReplayAdapterRefusal:
    replay_refusal_id: str
    replay_policy_version: str
    input_digest: str | None
    run_id: str | None
    attempt_id: str | None
    candidate_digest: str | None
    replay_disposition: ReplayDisposition
    reason_codes: tuple[str, ...]
    explanation: str

Optional identities are populated only when they can be validated without guessing. A malformed input may therefore produce a refusal with None identities. The refusal never carries a replay assessment or proof replay reference because no complete semantic replay result exists.

7.3 Forbidden output fields

Neither output type may emit or contain:

answer
final_answer
served_output
promotion
mutation
teaching_update
pack_update
policy_update
identity_update
Workbench_state
runtime_effect
confidence
score
rank
priority
selected_candidate
serving_allowed

Generated prose is explanatory only and excluded from identity.

8. ReplayDisposition vocabulary

ReplayDisposition is closed.

Semantic result dispositions, inherited from ADR-0226:

contract_refused
contract_closed_but_proof_refused
contract_and_proof_closed

Adapter-refusal dispositions:

invalid_replay_input
candidate_identity_mismatch
contract_replay_unavailable
proof_replay_unavailable
unsupported_replay_policy
unsupported_schema_version
unsupported_proof_obligation

The output type constrains the vocabulary:

Output type Permitted dispositions
ReplayAdapterResult contract_refused, contract_closed_but_proof_refused, contract_and_proof_closed
ReplayAdapterRefusal invalid_replay_input, candidate_identity_mismatch, contract_replay_unavailable, proof_replay_unavailable, unsupported_replay_policy, unsupported_schema_version, unsupported_proof_obligation

The separation is load-bearing:

  • contract_refused means the existing contract authority ran successfully over the exact candidate and refused it.
  • contract_replay_unavailable means the adapter could not run the required authority. It says nothing about whether the candidate would close.
  • contract_closed_but_proof_refused means contract replay closed, but at least one applicable proof authority returned a typed refused or unavailable obligation outcome.
  • proof_replay_unavailable means no lawful proof replay could be performed at all because the authority or its identity was absent, malformed, or failed before producing an identity-bearing outcome.

An exception, missing callback, unknown target, malformed return value, or infrastructure failure is adapter refusal, never semantic candidate refusal.

9. Contract replay obligations

Every candidate considered potentially closing must replay through the same organ-specific ContractAssessment authority that judged the original frame.

The adapter must bind the replay target from the original assessment's candidate_organ through a static, versioned allowlist. It may call the existing organ function over the exact reconstructed candidate ProblemFrame. It may not use dynamic imports, filesystem discovery, plugin registries, naming conventions, or a generic fallback authority.

The replayed contract assessment is a new diagnostic record over the candidate reconstruction. Its identity includes, at minimum, the candidate organ, ordered missing bindings, ordered unresolved hazards, runnable/refused state, and exact ordered evidence spans. Its explanation is excluded from identity.

The adapter may not:

reuse the original refused assessment as if it assessed the candidate
patch the original assessment
mutate the original ProblemFrame
mutate the original ContractAssessment
mutate ContractResidual
mutate SearchGateDecision
mutate ComputeBudgetDecision
mutate GeometricSearchRun
declare contract closure without replay
declare closure from absent blockers
declare closure from candidate generation success
declare closure from semantic proximity
declare closure from budget exhaustion

Contract replay outcomes map as follows:

  • runnable == False produces ReplayAdapterResult(contract_refused). Exact missing-binding and hazard codes are preserved in authority-defined order as reason evidence. Proof replay is not called and proof_replay_refs is empty.
  • runnable == True advances to the applicable proof obligations. It does not itself produce contract_and_proof_closed.
  • a malformed result, target mismatch, exception, or missing authority produces ReplayAdapterRefusal(contract_replay_unavailable).

The adapter cannot redefine or weaken the organ contract. Any change to an organ's obligations belongs to that organ's separately reviewed contract PR.

10. Proof replay obligations

Proof replay begins only after the existing contract replay closes.

The adapter receives the exact ordered proof_obligation_refs declared by the existing organ/proof policy. It cannot add a proof obligation by intuition, remove one for convenience, substitute a weaker verifier, or interpret an unknown obligation as optional.

The rules are:

If contract replay refuses, proof replay does not run and cannot convert the
candidate into closure.

If contract replay closes but an applicable proof obligation returns a typed
refusal or identity-bearing unavailable outcome, the result is
contract_closed_but_proof_refused.

Only when contract replay closes and every declared applicable proof obligation
closes may the result be contract_and_proof_closed.

An empty proof-obligation set may close only when the existing versioned organ/proof policy explicitly declares that no additional proof obligation is applicable. Absence of discovered obligations is not such a declaration.

If an obligation reference is unsupported, the adapter returns ReplayAdapterRefusal(unsupported_proof_obligation) before proof execution. If the proof authority cannot be invoked or fails before returning a lawful typed outcome, the adapter returns ReplayAdapterRefusal(proof_replay_unavailable), not a partial result.

contract_and_proof_closed is still not answer production. It is per-candidate replay evidence for a later result/trace stage that remains separately authorized.

This ADR introduces no proof engine, verifier, theorem prover, solver, arithmetic organ, or proof-obligation discovery mechanism.

11. Candidate identity and reconstruction boundary

The adapter must prove that the supplied candidate is exactly the candidate claimed by the run attempt before any replay authority is called.

Required checks are:

attempt_id belongs to run_id
attempt_index addresses that exact attempt
candidate_digest matches the supplied candidate reconstruction
candidate reconstruction binds back to the original ProblemFrame identity
candidate evidence spans are exact and ordered
duplicate evidence spans are preserved
operator id/version match the attempt
operator provenance matches the run's closed operator-set identity
input identities match the original residual/gate/budget/run chain

The adapter must recompute identities independently; equality of user-supplied strings alone is insufficient. A mismatch yields ReplayAdapterRefusal(candidate_identity_mismatch) and invokes neither contract nor proof replay.

Reconstruction must be pure and deterministic. It cannot read files, query a database, call a model, inspect environment configuration, reparse raw prose, or infer omitted fields. The original source text and source spans remain immutable. A candidate that lacks enough exact state to reconstruct is invalid; the adapter does not repair it.

The adapter cannot mutate CandidateAttempt.replay_status. Its result is a separate immutable record. A future trace may relate the two records by identity, but the original run remains byte-stable.

12. Uniqueness and disagreement semantics

Replay closure is per candidate. It is not global proof that the problem is solved.

The one-attempt adapter has no candidate-selection authority and cannot observe or resolve a set of competing replay results. Therefore:

  • if multiple candidates close but disagree, no answer-producing stage may proceed unless an existing organ-specific uniqueness or disagreement rule closes over the complete candidate set;
  • if no such rule exists or it refuses, the overall problem remains refused;
  • a candidate that closes one contract target or proof obligation does not close unrelated targets or obligations;
  • multiple contract_and_proof_closed records do not authorize voting, majority selection, best-of-N selection, score comparison, or arbitrary tie-breaking; and
  • the adapter reports each candidate independently and never marks one as selected, preferred, final, or answerable.

Any later multi-candidate orchestrator must preserve run order, group results under the exact run identity, invoke only an existing explicitly applicable uniqueness/disagreement authority, and emit a separate result. That stage is not authorized here.

13. Determinism and identity

All load-bearing identities use canonical JSON:

json.dumps(
    payload,
    ensure_ascii=False,
    sort_keys=True,
    separators=(",", ":"),
)

The serialized string is encoded as UTF-8 and hashed with SHA-256. The digest is the full lowercase hexadecimal encoding. Floats, NaN, Infinity, implicit object serialization, locale-dependent values, and unordered iteration are forbidden in identity payloads.

13.1 Candidate reconstruction digest

candidate_digest hashes the canonical candidate content: original/candidate frame identities, frame delta when present, exact typed bindings, relations, targets, and exact ordered evidence spans. It excludes the attempt and operator envelope so it retains the candidate-content meaning established by ADR-0228.

candidate_reconstruction_digest self-seals the complete reconstruction envelope. It includes the validated candidate_digest, run/attempt identity, operator-set identity, and operator id/version in addition to the canonical candidate content. Its own digest field is blanked during self-sealing.

For v1, CandidateAttempt.candidate_digest must equal the independently recomputed candidate-content digest, and the reconstruction digest must bind that value to the supplied run/attempt/operator provenance. A future schema may layer these identities differently only through a new ADR/version.

13.2 Input digest

input_digest hashes exactly the structural ReplayAdapterInput fields other than input_digest itself:

replay_policy_version
run_id
run_policy_version
attempt_id
attempt_index
candidate_digest
candidate_reconstruction_digest
problem_frame_digest
original_contract_assessment_id
candidate_organ
ordered residual_ids
gate_decision_id
budget_id
operator_set_id
operator_set_version
contract_replay_target
ordered proof_obligation_refs
canonical schema_versions

In-memory record objects and callable objects are represented only through their validated content identities and static authority/version identifiers. Callable identity, object address, or function repr never participates.

13.3 Result and refusal identities

replay_result_id self-seals the structural result payload with its own field blanked. It includes:

replay_policy_version
input_digest
run_id
attempt_id
candidate_digest
contract_replay_assessment_id
ordered proof_obligation_refs
ordered proof_replay_refs
replay_disposition
ordered reason_codes
exact ordered evidence_spans

replay_refusal_id self-seals the structural refusal payload with its own field blanked. It includes all available optional identities, the replay policy version, refusal disposition, and ordered reason codes.

explanation is excluded from every identity. IDs also exclude:

wall-clock time
timestamps
random values
UUIDs
environment variables
hostname
OS details
CI metadata
file paths
generated prose
memory addresses
thread or process identifiers
filesystem order
hash-map iteration order unless canonicalized

Replay of byte-equivalent inputs, authorities, policy/schema versions, and obligation results must reproduce the same outcome type, disposition, reason codes, evidence ordering, references, and IDs byte-for-byte.

14. Authority boundary

The adapter may decide only the replay disposition of one candidate under existing obligations.

Concern Authority owner Replay adapter authority
Original organ runnable/refused state Existing ContractAssessment organ None; preserves original record
Candidate contract replay Same existing organ authority Classifies its new diagnostic result
Candidate proof replay Existing applicable verifier/proof authority Classifies its typed outcomes
Search eligibility SearchGateDecision None
Compute allocation ComputeBudgetDecision None
Candidate generation/order GeometricSearchRun and future authorized operators None
Cross-candidate uniqueness Existing organ-specific disagreement authority None in this one-attempt adapter
Answer production/selection Future separately authorized result stage None
Sealed trace integrity Future SealedPracticeTrace None
Durable promotion Existing review/certificate paths None
Workbench Read-only future projection None

The adapter has no authority to:

generate candidates
rank candidates
select final answers
serve output
mutate original assessments
mutate frames
mutate residuals
mutate gate/budget/run records
allocate budget
change search eligibility
write sealed traces
promote findings
edit packs
edit teaching data
edit policy
edit identity
edit eval reports
change Workbench state

No replay disposition changes durable epistemic standing. Source kind (search, replay, or practice) grants no promotion authority.

15. Forbidden imports/calls/effects

15.1 Allowed dependency surface

A future boundary implementation may import only:

  • standard-library immutable value, enum, canonical JSON, and SHA-256 facilities;
  • SourceSpan and existing identity-bearing ProblemFrame values;
  • ContractAssessment as a value type;
  • ContractResidual as a value type;
  • SearchGateDecision as a value type;
  • ComputeBudgetDecision as a value type;
  • GeometricSearchRun, CandidateAttempt, and their value enums/types; and
  • local replay-adapter immutable value types and static authority manifests.

An actual organ-specific contract replay function or applicable existing proof/verifier function may be called only when a separately reviewed implementation PR names that function explicitly and proves the dependency surface. There is no generic dispatch, dynamic plugin discovery, entry-point scan, or fallback target.

The next shell authorized in Section 18 may use injected deterministic mock contract/proof callbacks in tests to prove the classification state machine. It does not authorize production replay wiring by implication.

15.2 Forbidden calls and effects

The adapter may not import or call:

candidate generation
search execution
operator execution
repair
answer realization
serving/runtime
teaching or proposal mutation
pack, policy, or identity mutation
eval or report mutation
Workbench mutation
sealed-trace writing
Vault or recall
field or algebra mutation
filesystem or network I/O
subprocess or shell execution
clock or timestamp APIs
randomness or UUID generation
environment or hostname inspection
dynamic import or plugin discovery
external model or tool invocation

No reverse import from assessment, residual, gate, budget, or search-run modules into the replay-adapter module is permitted.

No hidden fallback is permitted. An unavailable authority, unsupported obligation, malformed return value, or execution failure produces a typed replay refusal.

16. Failure modes and fail-closed behavior

Failure or outcome Required adapter outcome
Malformed replay input ReplayAdapterRefusal(invalid_replay_input)
Attempt not found at the claimed position in the run ReplayAdapterRefusal(candidate_identity_mismatch)
Attempt identity does not reproduce or belong to the run ReplayAdapterRefusal(candidate_identity_mismatch)
Candidate digest or reconstruction digest mismatch ReplayAdapterRefusal(candidate_identity_mismatch)
Candidate evidence span mismatch, reorder, synthesis, or deduplication ReplayAdapterRefusal(candidate_identity_mismatch)
Operator provenance does not match attempt/run operator-set identity ReplayAdapterRefusal(candidate_identity_mismatch)
Residual, gate, budget, assessment, or frame chain mismatch ReplayAdapterRefusal(candidate_identity_mismatch)
Unsupported replay policy version ReplayAdapterRefusal(unsupported_replay_policy)
Unsupported schema version ReplayAdapterRefusal(unsupported_schema_version)
Unsupported proof obligation reference ReplayAdapterRefusal(unsupported_proof_obligation)
Missing, mismatched, failed, or malformed contract replay authority ReplayAdapterRefusal(contract_replay_unavailable)
Contract replay refuses ReplayAdapterResult(contract_refused); proof replay is not called
Contract closes but no lawful proof replay can start ReplayAdapterRefusal(proof_replay_unavailable)
Contract closes but an invoked proof obligation returns typed unavailable/refused ReplayAdapterResult(contract_closed_but_proof_refused)
Contract and every declared applicable proof obligation close ReplayAdapterResult(contract_and_proof_closed)

A malformed input must return a refusal record, not raise an ordinary public exception and not emit a partial result pretending to be replay evidence.

No failure may become:

best guess
partial answer
soft proof
confidence score
rank
priority
serving fallback
proposal promotion
Unknown == False

Search or budget exhaustion has no special replay meaning. It cannot create a replay result and cannot change the original refusal.

17. Test obligations for future implementation PR

The future implementation must add executing tests that meaningfully fail for each prohibited state:

  1. Public API exports are exact.
  2. Malformed input returns replay refusal, not exception or partial result.
  3. Attempt/run identity mismatch fails closed.
  4. Candidate digest mismatch fails closed.
  5. Evidence span mismatch fails closed, including reorder or deduplication.
  6. Unsupported replay policy fails closed.
  7. Missing contract replay authority fails closed.
  8. Contract-refused replay yields contract_refused and never calls proof replay.
  9. Contract-closed/proof-refused yields contract_closed_but_proof_refused.
  10. Contract-and-proof-closed yields contract_and_proof_closed.
  11. contract_and_proof_closed does not create an answer field or answer value.
  12. Multiple candidate disagreement is not resolved by the adapter.
  13. Replay IDs are canonical and exclude prose, time, environment, path, host, process, and random values.
  14. Duplicate evidence spans are preserved exactly and participate in identity.
  15. No candidate generation, search execution, operator execution, or repair is reachable.
  16. No runtime, serving, Workbench, teaching, proposal, eval, report, pack, policy, or identity mutation is reachable.
  17. No reverse dependency exists from assessment, residual, gate, budget, or run modules into the replay adapter.
  18. No filesystem, network, time, random, subprocess, environment, UUID, hostname, dynamic-plugin, or path identity is reachable.
  19. The adapter never mutates the original frame, assessment, residual, gate, budget, run, attempt, or candidate reconstruction.
  20. Focused tests and the repository smoke lane pass.

Additional required controls:

  • tests independently recompute candidate, input, result, and refusal hashes rather than calling the implementation's private hashing helper;
  • static coupling tests parse the adapter and enforce the allowed import/call surface;
  • test callbacks are deterministic, side-effect-free, and identity-bearing;
  • a callback exception proves the adapter-failure versus candidate-refusal distinction; and
  • disagreement tests construct at least two independently closed, conflicting results and prove the one-attempt adapter provides no selection API.

18. Authorized next PR

This ADR authorizes exactly one next implementation PR:

feat(kernel): implement diagnostic replay adapter shell

That PR may only:

  • add replay-adapter frozen dataclasses, closed enums, canonical hashing helpers, and a discriminated outcome type;
  • consume one existing GeometricSearchRun and one exact CandidateAttempt identity;
  • consume one immutable candidate reconstruction value;
  • validate run/attempt/candidate/upstream identity consistency;
  • represent ReplayAdapterRefusal and ReplayAdapterResult records;
  • support injected deterministic mock contract/proof replay callbacks for boundary tests only if needed;
  • produce deterministic input, result, and refusal IDs; and
  • add tests only for the boundary and isolation obligations in Section 17.

That PR explicitly excludes:

candidate generation
operator implementation
search execution
operator execution
repair
production replay wiring
answer production
candidate selection or disagreement resolution
sealed practice trace
Workbench
runtime/serving
teaching/proposal/report/eval mutation
pack/policy/identity mutation
promotion
new proof engine

The shell may prove the state machine with injected test doubles, but it cannot claim live replay capability until existing organ-specific contract and proof functions are explicitly named and authorized in a later reviewed PR.

No other implementation PR is authorized by this ADR. Candidate-producing operators, production replay wiring, multi-candidate orchestration, sealed practice, Workbench display, answer production, serving, evaluation, and promotion each remain separately gated.