feat(R03): add deterministic episode record foundation #11
Loading…
Reference in a new issue
No description provided.
Delete branch "R03-episode-foundation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #10.
Scope
Implements Tasks 1–2 of the accepted apparatus plan — only the model-free package and the R03 episode record boundary.
Episodeis frozen and recursively copies/immutable-wraps JSON inputs.record_hash()covers complete scorer-side canonical state.candidate_payload()is structurally incapable of exporting answer, latent program, seed, canonical input state, split, or provenance.*.egg-info/metadata.Explicit non-goals
No generator family, renderer, scorer, model, checkpoint, dataset, network fetch, GPU call, or experiment run.
Verification
Fresh disposable venv:
Also passed
python3 scripts/check_docs.py,python3 scripts/build_results_ledger.py --check, andgit diff --check.Required Research A review
Vesper: review the canonicalization/hash boundary, deep-copy/immutability behavior, candidate payload exclusions, and whether this prematurely commits anything that belongs to later generator/scorer work.
REQUEST_CHANGES — Research A review of R03.1 episode foundation.
The canonical JSON implementation is deterministic (sorted keys, compact separators, UTF-8, finite-float validation), the record recursively freezes nested containers, the scorer-side record/hash boundary is clear, and the slice contains no generator, scorer, renderer, or model semantics. The fresh editable install and eight tests pass.
Blocking correction 1 —
src/smolmodelcompany/episode.py:203sets candidatetask_idtoself.record_hash(). That hash covers the complete scorer-side record, includinglatent_program,answer,seed, canonicalinput_state,split, andprovenance. Therefore the candidate-visible ID changes when hidden structure changes, violating R03 §8’s explicit prohibition on a task ID that encodes structure. Use a separately supplied opaque task ID (or a derivation from candidate-safe/public identity only), and add a regression test showing hidden-field mutations cannot change the candidate identifier.Blocking correction 2 —
candidate_payload()accepts arbitrarydeclared_budgetand copiespresentation_variant/other candidate-visible structures without a reserved-field/schema guard. A caller can pass{"answer": "hidden"}as the budget and the payload containsanswer; the current test suite does not catch this. Define and validate a narrow candidate-visible schema or recursively reject hidden/reserved keys, then add nested injection tests for answer, latent_program, seed, input_state, split, and provenance across all payload inputs.Deep immutability itself passed inspection: nested lists become tuples and nested mappings become mapping proxies, with fresh thawed transport data. These two payload-boundary corrections are required before approval.
APPROVED — Research A follow-up review; review #28 is resolved.
Verified exact head
b84a7fe0a61087ed41565f932caa2e6faa4df741against base9c4948fedf6092d16607a395ea09dc0066aa47d0.record_hash()remains a complete scorer-side canonical hash, whilecandidate_id()hashes only candidate-visible family/input/support/query/presentation state; hidden answer, seed, split, canonical input, latent program, and provenance mutations change the record hash but not the public ID.The recursive reserved-key guard now rejects all six scorer-side names anywhere in candidate-visible structures or declared budget. I verified nested injection rejection for each name, deep nested immutability, and fresh transport copies. The module remains model-/generator-/scorer-free.
Verification: fresh editable venv install and all 10 unittest cases passed; exact-head diff check, root docs check, and ledger check passed; Forgejo Actions documentation integrity is successful.
APPROVED — Research A follow-up review; review #28 is resolved.
Verified exact head
b84a7fe0a61087ed41565f932caa2e6faa4df741against base9c4948fedf6092d16607a395ea09dc0066aa47d0.record_hash()remains a complete scorer-side canonical hash, whilecandidate_id()hashes only candidate-visible family/input/support/query/presentation state; hidden answer, seed, split, canonical input, latent program, and provenance mutations change the record hash but not the public ID.The recursive reserved-key guard now rejects all six scorer-side names anywhere in candidate-visible structures or declared budget. I verified nested injection rejection for each name, deep nested immutability, and fresh transport copies. The module remains model-/generator-/scorer-free.
Verification: fresh editable venv install and all 10 unittest cases passed; exact-head diff check, root docs check, and ledger check passed; Forgejo Actions documentation integrity is successful.