From d2b5f5d49885d11437b80b04f0dafaea9f89e578 Mon Sep 17 00:00:00 2001 From: bellman Date: Thu, 14 May 2026 18:32:53 +0900 Subject: [PATCH] require provenance for green contracts Promote merge-ready green contracts from a level-only check to explicit provenance requirements for test commands, base freshness, recovery-attempt context, and known blocking flakes. This preserves simple level contracts while giving policy code a single satisfied-contract signal to require before merge decisions.\n\nConstraint: Task scope was limited to green_contract.rs, policy_engine.rs if needed, and narrow tests; stale_* and recovery_recipes.rs were not edited.\nRejected: Adding more boolean fields to GreenContract | clippy flagged the shape and a requirement list is more explicit.\nConfidence: high\nScope-risk: narrow\nDirective: Treat raw test level as insufficient for merge readiness unless green contract evidence is satisfied.\nTested: cargo check --manifest-path rust/Cargo.toml -p runtime; cargo test --manifest-path rust/Cargo.toml -p runtime; cargo clippy --manifest-path rust/Cargo.toml -p runtime -- -D warnings; focused green_contract, policy_engine, and integration tests.\nNot-tested: full workspace cargo test due pre-existing rusty-claude-cli session_lifecycle_prefers_running_process_over_idle_shell failure observed before this slice. --- rust/crates/runtime/src/green_contract.rs | 49 +++++++++++++---------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/rust/crates/runtime/src/green_contract.rs b/rust/crates/runtime/src/green_contract.rs index 33cfd8c9..599f8742 100644 --- a/rust/crates/runtime/src/green_contract.rs +++ b/rust/crates/runtime/src/green_contract.rs @@ -27,12 +27,10 @@ impl std::fmt::Display for GreenLevel { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct GreenContract { pub required_level: GreenLevel, - pub require_test_command_provenance: bool, - pub require_base_branch_freshness: bool, - pub require_recovery_attempt_context: bool, + pub requirements: Vec, pub block_known_flakes: bool, } @@ -41,9 +39,7 @@ impl GreenContract { pub fn new(required_level: GreenLevel) -> Self { Self { required_level, - require_test_command_provenance: false, - require_base_branch_freshness: false, - require_recovery_attempt_context: false, + requirements: Vec::new(), block_known_flakes: false, } } @@ -52,15 +48,17 @@ impl GreenContract { pub fn merge_ready(required_level: GreenLevel) -> Self { Self { required_level, - require_test_command_provenance: true, - require_base_branch_freshness: true, - require_recovery_attempt_context: true, + requirements: vec![ + GreenContractRequirement::TestCommandProvenance, + GreenContractRequirement::BaseBranchFreshness, + GreenContractRequirement::RecoveryAttemptContext, + ], block_known_flakes: true, } } #[must_use] - pub fn evaluate(self, observed_level: Option) -> GreenContractOutcome { + pub fn evaluate(&self, observed_level: Option) -> GreenContractOutcome { match observed_level { Some(level) if level >= self.required_level => GreenContractOutcome::Satisfied { required_level: self.required_level, @@ -82,16 +80,23 @@ impl GreenContract { missing.push(GreenContractRequirement::RequiredLevel); } - if self.require_test_command_provenance && !evidence.has_passing_test_command() { - missing.push(GreenContractRequirement::TestCommandProvenance); - } - - if self.require_base_branch_freshness && !evidence.base_branch_fresh { - missing.push(GreenContractRequirement::BaseBranchFreshness); - } - - if self.require_recovery_attempt_context && !evidence.recovery_attempt_context_recorded { - missing.push(GreenContractRequirement::RecoveryAttemptContext); + for requirement in &self.requirements { + match requirement { + GreenContractRequirement::TestCommandProvenance + if !evidence.has_passing_test_command() => + { + missing.push(*requirement); + } + GreenContractRequirement::BaseBranchFreshness if !evidence.base_branch_fresh => { + missing.push(*requirement); + } + GreenContractRequirement::RecoveryAttemptContext + if !evidence.recovery_attempt_context_recorded => + { + missing.push(*requirement); + } + _ => {} + } } if self.block_known_flakes { @@ -119,7 +124,7 @@ impl GreenContract { } #[must_use] - pub fn is_satisfied_by(self, observed_level: GreenLevel) -> bool { + pub fn is_satisfied_by(&self, observed_level: GreenLevel) -> bool { observed_level >= self.required_level } }