Repository navigation
P6a: Promotion decision (decide(), C-45 enforcement) - #24
Merged
Merged
Conversation
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…C-41, boundary rule R18 (Doc 09 §17-§29; C-37, C-41, C-157 to C-161) R18 was written test-first: its self-tests failed until the rule existed in check_module (core.promotion imports only the standard library and core.domain.*). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… 05 §15, Doc 11 §26; C-157, C-158, C-160, C-161) PromotionDecision and PromotionEvidence (guards, hash recomputed by the constructor, round trip), the closed DecisionReason and ProofSource vocabularies, OracleVerdict, ProofPlanEntry, DecisionOutcome and PromotionPolicy (non-success table, retry budget 1 to 10, the oracle cannot be accepted). dail_policy and stateful_full_policy differ in carry-forward only (C-22). The 48 tests were written first and failed with ImportError until the types existed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… 06 §14, Doc 09 §28; C-45, C-141, C-159) carry_forward(parent_ref, candidate, impact) now refuses unless the impact report of exactly this candidate and parent says the invariant is unaffected with eligible evidence (ImpactReport.carry_forward_ok): an affected, uncertain or unmentioned invariant, a report for another candidate, parent or version, and a report whose hash no longer recomputes all raise. The callers got the new argument only: tests/unit/test_proof.py (8 calls), tests/adversarial/test_state_machine_rules.py (1) and tests/domain_builders.carried_proof (which feeds tests/unit/test_promotion_coverage.py). The 33 changed or new tests were shown failing first (31 TypeErrors), and 5 scratch-copy mutants of the new check are killed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ess table, proof sources, oracle conflicts, retry budget, emergency switch, completeness (Doc 09 §17-§29, Appendix A; C-41, C-45, C-157 to C-161) decide(candidate, trusted, impact, verification, policy, ...) is a pure function that examines every required item (gates, invariants, baseline items), proves each by a fresh sufficient PASS or by carrying its reference forward (never for an affected, non-PROTECTED, version-changed or policy-disabled invariant), folds the blocking conditions by severity (REJECT above ESCALATE above RETRY), converts an exhausted RETRY to ESCALATE, treats an oracle FAIL against a proven check as a conflict, and downgrades an incomplete or switched-off PROMOTE to ESCALATE. A stale parent wins. 95 tests, written from the spec first: they failed with ImportError until decide existed; at the first run 88 passed and the 7 failures were mistakes in the tests (a duplicate result id, the wrong definition picked by index, a replace() the domain forbids, an expected list order, an expected proof plan, a text scan, a stale-parent that was not stale), not in the code. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ycle without touching Trusted State (Doc 06 §5, §20, §22 SM-001 to SM-004, SM-010; C-29, C-157) PROMOTE -> PROMOTABLE (never PROMOTED), REJECT -> REJECTED (a PROMOTABLE candidate only through the stale-parent exit), RETRY -> RETRY_REQUIRED, ESCALATE -> ESCALATED, with the existing transition functions only. The decision must be about this candidate and its parent; a PROMOTE against a state that is no longer the parent raises StaleParentError (SM-010). The trusted state is read and never changed. 17 tests, written first; at the first run 14 of 17 passed and the 3 failures were one test comparing resources of two separate worlds. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t through the real pipeline (Doc 12 §5-§7, Doc 06 §25-§26; C-22, C-45, C-159) tests/promotion_pipeline.py runs normalisation, identity, graphs, impact (with the prior proofs of the trusted state), the scope policy, the verification engine and decide. patch_safe promotes (INV-SEC-001 VERIFIED because its reference is VIOLATED; the others VERIFIED or CARRIED_FORWARD as the engine's own impact report says), patch_regression is rejected with the functional check as its blocking condition, Stateful-Full carries nothing, and 15 of the other 15 decisions are among the reviewer's decision.allowed. The real pipeline found a defect in decide: every gate item was EVIDENCE_NOT_SUFFICIENT, because the engine gives STRUCTURAL and CONSISTENCY level L0 and decide applied the L1 minimum to them; the test builder had used L1 for them too. The level rule now applies to invariants and baseline items only (C-159 updated), with tests for both. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…§17, §19; C-45, C-158 to C-161) Five seeds, 60 generated worlds each: a required FAIL always rejects, a required non-success never promotes, a PROMOTE has a full proof plan and only supporting results that exist, a stale parent is rejected, nothing affected, uncertain, not PROTECTED or policy-disabled is carried forward (C-45), the emergency switch, the retry budget and an oracle FAIL each stop a PROMOTE, and nothing is mutated. The generator reaches all four decisions and 13 reasons. Every checker is also handed a decision that breaks its rule and must fail. The fault cases (verifier crash, hash mismatch, stale candidate, truncated report, missing STRUCTURAL) each give a known non-PROMOTE decision and leave the trusted state unchanged. Also fixes a line over 100 columns in the fixtures test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
P6a REPORT
Process (C-28): do not merge until the external review is pasted into this PR.
1. Branch and commits
feature/p6afrom main c3524ef, 8 commits. Each was run in a clean clone on Python 3.12 withdev.py test, and every one is OK:carry_forwardneeds the impact report (C-45)decide()candidate_afterEvery run shows
OK (skipped=1). Final tree on Python 3.14: 3724 tests, OK (skipped=2). Python 3.13: NOT RUN — not installed.2. Files
22 files changed, +5274 / −23.
core/domain/promotion.py,core/promotion/{decide,policy,lifecycle}.py; builderstests/promotion_builders.pyandtests/promotion_pipeline.py; 7 test filestest_promotion_{domain,policy,decide,lifecycle,fixtures,properties,faults}.py.core/domain/invariant.py(signature ofcarry_forward); docstrings ofcore/domain/__init__.pyandcore/promotion/__init__.py; the contract test (R18 only, nothing loosened); the register;PHASE_GATES.md(step 0 only, plus the heading "P6a–P10 — NOT STARTED").carry_forwardcallers changed by the new argument only:tests/unit/test_proof.py(8 calls plus a smallimpact()helper);tests/adversarial/test_state_machine_rules.py(1 call);tests/domain_builders.carried_proof, which feedstest_promotion_coverage.py.expected.json, the registry spec andrequirements/.3. Acceptance criteria A1–A11
All IMPLEMENTED.
core/domain/promotion.py; the policy refuses oracle acceptance; two constructors. Tests:test_promotion_domain.py(31),test_promotion_policy.py(17).TestSeverityFold,TestOrderIndependence.test_promotion_decide.pyand the properties.TestOracleEvidence).candidate_afteruses only legal transitions and never touches the trusted state (17 tests).test_promotion_fixtures.py; see section 6.4. Commands
dev.py test3.12:Ran 3724 tests,OK (skipped=1). Before: 3508 at c3524ef, so +216.3723 passed, 1 skipped, 79777 subtests passed.dev.py bootstrap: 3724 tests OK, health checks PASS,bootstrap complete, exit 0.pip-audit(no dependency changed).5. Decision table (Doc 09 §20 and §41)
My hand derivation, the spec's rule and the code's answer agree in every row; tests are in
TestAppendixAAndTheDecisionMatrix.6. Canonical transitions and every fixture with a parent
All 17 fixtures with a parent give a decision among the reviewer's
decision.allowed. The trusted state of each is built from the parent directory's ownexpected.json. Proof plan: V = verified, C = carried forward, − = no proof.patch_safepromotes with nothing carried and the regression is rejected. With the DAIL scope and the Stateful-Full policy, the result is REJECT INSUFFICIENT_EVIDENCE.7. C-45 evidence
TestCarryForwardNeedsTheImpactReport, 11 tests): an affected invariant, an uncertain one, an unmentioned one, an unaffected one without eligible evidence, a report for another candidate, another parent or another version, and a report whose hash no longer recomputes all raiseDomainValidationErrornaming C-45. The old two-argument call is aTypeError.TypeError). Five scratch-copy mutants of the new check are killed. The first mutation run left the "version not passed" mutant alive; I added a test with a reference at version 2 and it now dies.decide: the propertycheck_c45andTestCarryForwardRules(each precondition removed in turn).8. Mutation, properties, faults
affected_for, the status check and the version check each duplicatecarry_forward_okorcan_satisfy_proof. Removing them together is killed.9. P6a evidence map (the part of Doc 15 §13 this package reaches)
Evidence, not a verdict. The criteria are those of the work package; the text of Doc 15 §13 was not available.
TestAppendixAAndTheDecisionMatrix,TestEvidenceSufficiencyAndFreshnessand the properties.TestStaleParent, the properties,process_stale_parent.TestOracleEvidence.10. Register rows, spec gaps, new Open rows
reason,attempt_numberandcontent_hashwere added.11. What P6b needs from this package
decide(...) -> DecisionOutcome(decision,proof_plan, evidence). A PROMOTABLE candidate may be decided again at commit time.InvariantProofs: VERIFIED through an evaluation, CARRIED throughcarry_forward(ref, candidate, impact).content_hashexcludes the id and the clock.committed_state_idandnew_state_idare forced to null today. P6b needs a sanctioned way to set them once, such as awith_commit(state_id)method.dail_policyandstateful_full_policytake the reviewedautomatic_enabledflag as a required argument. The gate verifier ids are copied from the engine and pinned by a test.12. Open questions and disclosures
ambiguous_identity_uncertainthe engine's impact report leaves FUNC unaffected, so it is carried forward although some identity is uncertain. That is P4's call; P6a only consumes it. Is that intended?decide(every gate item was rejected as below L1) after the synthetic tests were green, because my test builder had used L1 for gates too. It is fixed and tested.decide's outputs for all 17 fixtures before the fixture test was written. The canonical expectations in its docstring come from CLAUDE.md andexpected.json, and the cross-check uses the reviewer'salloweddata, but the exploration came first.allowed.🤖 Generated with Claude Code