Repository navigation
#332 Phase 7 wave 1: reused-instance state, L2 infra crediting, verify wording - #346
Conversation
…oads A local review loads .code-forge/state.json only when its source hash matches the review. Otherwise the run is meant to start empty. A StateMachine instance whose run() is called a second time did not: when the state file had been removed, or belonged to another diff, the second run kept the State the first run left in memory and resumed its rounds, dispositions and clean credit. With preserved schema/coverage credit that carried-over State could also reach PASS sooner than a new instance reviewing the same workspace. A later local run() on the same instance now drops that in-memory State before it looks for state.json, so it starts where a new instance starts. The first run() still starts from the State the instance was built or seeded with, which callers and fixtures use. CI never reloads state.json and keeps its in-memory State across runs on one instance, as before. The comments that described which attributes survive a run() now say which ones are kept for the instance's lifetime. New differential tests run the second run on the reused instance and on a new instance from the same files and compare every file both write; they fail without this change and pass with it. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…essors The state machine bound its rejected-evidence snapshot and the schema-fail companion check to the L1 producer's private invocation token and rejection tuple, reaching into another module's underscore attributes. That made the producer's storage part of the machine's contract. The producer now exposes both as read-only properties that return the stored objects themselves, so the machine's identity checks against the current invocation keep holding, and the machine reads only those. The storage, its reset on every call and the tamper tests that write the backing attributes are unchanged. A new test pins identity, the per-call token and that the properties cannot be assigned. Returning a copy from the rejection accessor makes 27 existing evidence-rejection tests fail. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
Preserved-row validation lives in state_preserved, which state imports at module level. The validator needs the product row projection, but that function was defined in state, so state_preserved reached it through a function-level import on every call, and carried a second unused shim for is_host_round. Those shims existed only to break the import cycle. The authority field set, is_host_round and the product row projection now live in state_rows, which imports nothing from the package. state re-exports them unchanged, so existing imports keep working, and state_preserved imports the projection directly. Both modules bind the same function objects; the call-time shims are gone. Behavior is unchanged. The two tests that pinned the shim layout now pin one shared definition: the same object in all three modules, and both callers reach it. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…tests The next change edits one assertion in this module. Lint is diff-scoped and a touched file has to be left clean, and this module predates the pinned formatter. Formatting it on its own keeps the behavioral change reviewable. The syntax tree is identical before and after; only layout changes. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
… product When receipt verification found an open finding whose basis it could not accept, the reason always read "unresolved unverified product finding". Most of these are not product findings: the host gives authority infra-unavailable to INFRA diagnostics (provider failures, schema-fail, incomplete coverage) and to UNTRUSTED reviewer output, and the rest have a malformed or unrecognized basis. Reporting them all as product defects contradicts the report separation of infrastructure failures from product defects. The reason now names what the receipt basis shows: a finding without verified evidence (an infrastructure failure, or a product candidate in untrusted reviewer output), or a finding with a malformed or unrecognized basis. A receipt finding does not carry the source that separates INFRA from UNTRUSTED, and its file and description can come from the reviewer, so the wording claims neither a product defect nor a trusted infrastructure diagnostic, keeps the possible product candidate visible and echoes nothing from the receipt. The verdict, failure kind and the recorded unresolved findings are unchanged; the machine compares those, not the text. The infra-unavailable authority string is now a named constant shared by the writer and the verifier. The tests assert the label for every unverified basis shape. Restoring the old wording makes 13 of them fail. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…infra rounds Two combinations of the schema/coverage credit preserve had no test: a schema-fail finding beside literal receipt damage on the receipt gate path, and a round with both a schema-fail and an incomplete-coverage finding together with an L2 infrastructure error. The first must take the full reset, as the coverage variant already does; the second must preserve, because what remains after removing both findings is clean or incomplete only through the L2 error, and a new product finding beside them must still reset. The gate-path test now runs schema-fail with literal damage in both pass orders, and two predicate tests cover the mixed round with and without the L2 error. Dropping the L2-infra arm of the preserve rule makes the mixed L2 case fail. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…erve The credit-preserve change listed its deferred follow-ups only in its merge request. This note collects them in the repository next to the architecture and plan for the host-failure classification work: which bounded items have landed (reused-instance state, the excerpt accessors, the row helper module, the comments, the verify wording and the missing test combinations), and, for each item that changes credit policy or the persisted round shape, what happens today, the options and the decision the owner has to make. Listing an item does not schedule it. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…eason The previous change described every open finding with authority infra-unavailable as an infrastructure failure or a product candidate in untrusted reviewer output. That missed the most common producer: a product candidate from a validated reviewer reply whose falsification failed is published to the receipt as INFRA/UNCERTAIN, so it also carries infra-unavailable. The wording steered a reader towards discounting a real product candidate. The reason now reads "unresolved finding without verified evidence (a product candidate the host could not verify, or an infrastructure failure)". It names a product candidate first, which covers both a failed falsification and untrusted output, and still claims no trusted infrastructure diagnostic. The docstring lists all three producers. The receipt-gate diagnostic fingerprints its message text, so a receipt failure recorded by an older version and the same failure recorded now get different fingerprints; the verdict is unchanged. The falsifier protocol and transport run test now asserts the exact reason the real producer gets. The basis tests use an explicit table of expected labels instead of a helper that mirrored the implementation. Restoring the previous wording fails 14 tests, including both run cases. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…structure error An L2 mutation infrastructure error (a raised L2 runner or a typed MUTATION_ERROR) is meant to earn no clean credit, keep a window already earned on the same source, and be non-PASS. That held only when the review wrote no host receipts, which is how the existing tests run it. With a diff and active L1, the normal local review, every round is a host attempt: the fixpoint turned CLEAN into INCOMPLETE, the round was recorded as interrupted, and the round history validator then refused that row as corrupt, because it accepted INCOMPLETE only beside a falsifier protocol failure. The review stopped with CorruptedStateError on the first round whose mutation layer failed. The machine now records "l2_infra_error": true on a host round whose L2 phase returned an infrastructure error, and the validator accepts an interrupted INCOMPLETE round with that fact. It refuses the fact when it is not exactly true or when L2 did not return, and on an earned or evidence-rejected round, which an L2 error makes INCOMPLETE. Rows without the field validate as before. A second leak fed the same refusal: the L2 error was cleared only when the next L2 phase started, so a round that failed after L1 and before its own L2 recorded INCOMPLETE from the previous round's error. It is now also cleared at the start of every round. New host-mode tests run a raised and a typed L2 failure: the window is kept, the state reloads, recovery earns the next cycle, three failing rounds stop at the mutation infrastructure breaker, and a round that fails before L2 records the CLEAN its L1 observed. Loader tests pin the new field. Without this change 15 of the 17 tests fail; without only the per-round clear, the round-B test fails. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…cases Every per-run attribute is reset when run() starts. For most of them a reused instance cannot be told apart from a new one anyway, because the round or attempt rewrites the attribute before anything reads it. Three can be told apart, and no test did: - run 1 cannot write its receipts and fails after the round; run 2 fails after L1. Run 2's failed path would log run 1's receipt-write error a second time. - run 1 finds another diff's state.json and no receipts, saves its own empty state, and stops in the mutation preflight. Run 2 would continue the other diff's cycle numbers instead of starting at round 0. - run 1's L2 runner fails; run 2 fails after L1, before its own L2. Run 2 would record INCOMPLETE from run 1's L2 error. Each test runs run 2 on run 1's instance and on a new instance from the same files and compares every file both write. Dropping the run-entry reset of the receipt-write errors or of the other diff's state makes the first or second test fail. The L2 error is now also cleared at the start of every round, so the third test guards that scenario without depending on the run-entry reset. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
A later local run() starts empty unless a matching state file loads, while the first run uses the State the instance was built or seeded with. The first run was identified by counting run() calls, so a run that raised before it ever reached the state load (for example while resolving the trusted gate) still counted, and the next run discarded a seeded State nobody had used. The state load now marks the built State as used, and only a later run after that starts empty. A new test fails the first run before the load and checks that the second run keeps the seeded State; it fails with the counter. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
Adds the classification of every per-run reset whose removal no test noticed: which ones a reused instance can expose (now covered), which one became redundant once the L2 error is cleared every round, and why the rest cannot be observed. Records the host-receipt L2 crash found by that analysis and its fix, the corrected verify wording, the run-entry marker, and the open question of CI runs reusing an instance's State. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…credit When the receipt gate refuses a round's evidence (for example a reviewer excerpt that does not match the diff), the round is recorded as unavailable: the inherited clean window cannot be proved, and the next run fails without calling a reviewer. The gate maps an INCOMPLETE fixpoint to an interruption instead, which keeps the window. An L2 infrastructure error alone makes the fixpoint INCOMPLETE, so since the previous change the same damaged round beside an unrelated mutation failure kept its window, and a later healthy round could reach PASS on it. On the gate path only a falsifier protocol stop or an acquisition failure now interrupts; a damaged round whose INCOMPLETE comes from the L2 error alone is unavailable, as it is with a healthy L2. The validator accepts an unavailable INCOMPLETE round only with the recorded L2 error. A new test runs literal receipt damage with a healthy, a raising and a typed failing L2: all three are unavailable, cannot be restored, and the next run fails with no reviewer call. Restoring the old gate mapping fails the two L2 cases. The round-B test now also asserts that its unavailable round is not restored. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…ation Describes how a round whose receipts the gate refused stays unavailable beside an L2 error, records as an open question that the mutation infrastructure breaker demotes a preserved round now that host-receipt reviews reach it, and records the full-suite comparison against the base and the review findings that changed the series. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…e label The unresolved-finding reason was picked by a helper that parsed and normalized a finding's basis a second time, separately from the check that decided the finding was unverified. The two could disagree, and the label was looked up for a finding tracked beside the unresolved list rather than from it. One classifier now returns what a basis is: absent, verified, infra-unavailable, malformed or unrecognized. The unverified check and the reason label both read it, and the label of the first unresolved finding is taken in the same loop that records it. The infra-unavailable label is keyed by the shared constant. Behaviour is unchanged: the classifier matches the old check for every basis shape the tests cover, and the reason text is the same. The label tests are an explicit table of basis and expected label, and a new test puts findings of two basis kinds in two passes and checks that the reason names the first one and its pass. Taking the label or the cycle and pass from the last unresolved finding instead makes it fail. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…urately A few comments and test docstrings from the row-helper move and the excerpt accessors no longer matched the code. The excerpt token docstring said the token was owned by its rejections, when each rejection records the token. The leaf-module and preserved-validation docstrings still described the removed call-time shims. The projection test claimed to prove a single shared function, which a separate identity test does, and one assertion guarded a shim name that no longer exists. Two test docstrings carried planning vocabulary. The text now says what the code does, the dead assertion is gone, and the accessor test sits after the reviewer-response group it interrupted. No behaviour changes. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
…isory buffer A later local run() on one StateMachine starts empty unless a matching state file loads. It decided that with a flag set when the state load was reached, so it also threw away a State a caller assigned between runs, and a run whose state load raised still marked the seeded State as used. The pre-existing L0 advisory buffer was an instance attribute: a run that raised after L0 and before the advisory flush left its entries for the next run, which published them if it returned without an L0 phase of its own. The machine now records the State object the last run used, once its state load returned, and a later local run drops exactly that object. A State the instance was built or seeded with, one a caller assigned after the previous run, and one no run reached are kept; CI is unchanged. The buffer is reset with the other per-run state at every run() entry. Comments that called instance-lifetime attributes per-run, or the reverse, now say which they are, and the CI docstring says CI never loads the state file. New tests cover a State assigned between runs, a run whose state load raised, and the buffer case; each fails on the previous code. The reused-instance differentials share one helper, which gained a hook between the runs and an nth-call failure seam. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
An L2 infrastructure error turns what would be a CLEAN round into INCOMPLETE. The receipt gate had been fixed to record a damaged round beside such an error as unavailable, but three other paths still let the L2 error keep the inherited window: - An attempt that failed after its L2 returned (a crash before publishing receipts, a receipt-gate read error, a state save failure) mapped any INCOMPLETE to interrupted, even with damaged receipts, so a later healthy round could reach PASS on the kept window. - Receipt acceptance (hunk witness, coverage floor, completeness) ran only for CLEAN rounds, so evidence it would refuse went unchecked when an L2 error sat beside it. - The survivor breaker had its own mapping. A failed attempt, a refused receipt gate and the survivor breaker now share one rule: only a falsifier protocol stop or an acquisition failure interrupts; a round that is INCOMPLETE only through its L2 error is unavailable, as a CLEAN round is on those paths. The gate and the breaker use one helper for it, and the protocol cause of INCOMPLETE has one definition. A round that would be CLEAN but for its L2 error gets the same receipt acceptance as a CLEAN round. The L2 error is now cleared when a host attempt begins (also for non-host local rounds), replacing the clear at the start of each round, so an attempt that fails before its L2 never reads an earlier round's error. The validator refuses the L2 fact beside a CLEAN fixpoint, which the machine never writes, and its message for an unexplained INCOMPLETE now names both causes. One consequence is stricter on purpose: a pure excerpt rejection, which alone keeps the window, is unavailable beside an L2 error. docs/round-history.md documents the field, the outcomes, compatibility with older versions, and recovery from a state an older version left unloadable. New tests run each failure after L2, unverifiable evidence, a pure excerpt rejection, an acquisition failure, the survivor breaker beside an L2 error and beside a protocol stop, and an attempt failing before its round, against a healthy, raising and typed L2; each behaviour change fails at least one of them on the previous code or under a targeted mutant. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
The plan described an earlier state of the series: the per-round L2 clear, the survivor breaker test it no longer runs, verification counts from before the last fixes, and commit hashes that changed. It also claimed the gate-path protocol-plus-damage outcome was unpinned when a test pins it. It now describes the reused-instance and L2 rules as implemented, with the compatibility and recovery notes, the full test coverage, the mutant-sweep classification, the open owner decisions (including the breaker demoting a preserved round and CI State reuse), the deferred refactors, and the final verification and review record. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
PR Summary by QodoFix reused LOCAL state and L2 infrastructure credit in host reviews
AI Description
Diagram
High-Level Assessment
Files changed (15)
|
Brings in the fixpoint state-invariance tests (#337), the fixval JUnit evidence rule (#331) and the isolate startup diagnostic (#329). Both sides appended tests at the end of tests/test_review_interruption_source_controls.py; the resolution keeps both blocks unchanged. The touched test files and the neighbouring machine, state and verify suites pass on the merged tree. Signed-off-by: Minxi Hou <houminxi@gmail.com> Claude-Session: https://claude.ai/code/session_01Rf2jBZrtS24e7qpWUBy3uo
Base:
main20b4ace (Phase 6 landed as #319). 19 commits; merges cleanly onto currentmain(only shared file isfactories.py, non-overlapping hunks). The full record, including what is deliberately deferred and what needs an owner decision, is indocs/gsd/future-direction/332-host-failure-classification/phase7-plan.md.What changes
Reused
StateMachineinstance (PR #319's "planned for the next phase"). A later LOCALrun()on one instance drops the State object the previousrun()used before it looks forstate.json, so a missing, other-diff or corrupt file leaves it where a new instance starts. A State the instance was built or seeded with, one a caller assigned between runs, and one no run reached are kept. CI is unchanged. The pre-existing L0 advisory buffer is reset per run. Production builds one instance per review, so this is reachable only through the Python API.L2 infrastructure errors in host-receipt reviews (crash fix). With a diff and active L1 (the normal LOCAL review), any mutation-infrastructure error (raised L2 runner or typed
MUTATION_ERROR) made the machine write aninterrupted+ INCOMPLETE row that its own validator refused, so the review stopped withCorruptedStateError. #114's tests run without host receipts and never saw it. Now:"l2_infra_error": truewhen this round's L2 returned an infrastructure error; the validator accepts INCOMPLETE with it and refuses the field when not exactly true, when L2 did not return, or beside CLEAN.interrupted(window kept, nothing earned, non-PASS, policy: L2 mutation infrastructure errors (MUTATION_ERROR, source=INFRA) reset consecutive clean credit like code findings #114). A round that cannot prove credit (failed attempt, refused receipt gate, survivor breaker) isunavailable, as a CLEAN round is there; only a falsifier protocol stop or an acquisition failure interrupts. Receipt acceptance also runs for a round that would be CLEAN but for its L2 error.unavailable(fail closed).docs/round-history.mddocuments the field, compatibility with older versions, and recovery from a state an older version left unloadable.Verify wording (#70, 2026-10-30 comment). The unresolved-finding reason no longer calls INFRA/UNTRUSTED findings "product findings". It reads "unresolved finding without verified evidence (a product candidate the host could not verify, or an infrastructure failure)", or names a malformed / unrecognized basis. Verdict,
failure_kindandunresolved_findingsare unchanged; the receipt-gate diagnostic fingerprint changes with the text.Smaller items from #319's follow-up list. Public read-only accessors for excerpt rejections on
_L1Call; the host round-row helpers in a new leaf modulestate_rows.py(no call-time shims); corrected per-run vs instance-lifetime comments; the plan's missing test combinations; a keep-across-runs mutant sweep of_reset_run_statewith every surviving mutant either killed by a new test or shown equivalent (plan section 2).Compatibility
interrupted/unavailablerows whose INCOMPLETE comes from the L2 error alone (they crashed on those rounds anyway) and ignore the field on protocol, reset and preserved rows.test_provider_capacity_reporting.pywas formatted in its own commit (AST unchanged) because a touched file must be left lint-clean.Verification
test_deleted_scope_consumers.pyx2,test_exec_falsifytimeout,test_ci_null_baseline_deadline...), plus timing-dependent cases that also fail on the base.ruff check/ruff format --check(pinned 0.17.3) clean on every changed file.Needs an owner decision (not in this PR)
Credit-policy items TD-P6-IV/V/VI, persisting the preserve basis on
preservedrows, companion fingerprints inever_seen, CI State reuse on one instance (W2-9), and the mutation-infra breaker demoting apreservedthird round (W2-10). See plan section 3.Generated by Claude Code