Skip to content

Commit d2b5f5d

Browse files
bellmanbellman
authored andcommitted
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.
1 parent 607f071 commit d2b5f5d

1 file changed

Lines changed: 27 additions & 22 deletions

File tree

rust/crates/runtime/src/green_contract.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ impl std::fmt::Display for GreenLevel {
2727
}
2828
}
2929

30-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3131
pub struct GreenContract {
3232
pub required_level: GreenLevel,
33-
pub require_test_command_provenance: bool,
34-
pub require_base_branch_freshness: bool,
35-
pub require_recovery_attempt_context: bool,
33+
pub requirements: Vec<GreenContractRequirement>,
3634
pub block_known_flakes: bool,
3735
}
3836

@@ -41,9 +39,7 @@ impl GreenContract {
4139
pub fn new(required_level: GreenLevel) -> Self {
4240
Self {
4341
required_level,
44-
require_test_command_provenance: false,
45-
require_base_branch_freshness: false,
46-
require_recovery_attempt_context: false,
42+
requirements: Vec::new(),
4743
block_known_flakes: false,
4844
}
4945
}
@@ -52,15 +48,17 @@ impl GreenContract {
5248
pub fn merge_ready(required_level: GreenLevel) -> Self {
5349
Self {
5450
required_level,
55-
require_test_command_provenance: true,
56-
require_base_branch_freshness: true,
57-
require_recovery_attempt_context: true,
51+
requirements: vec![
52+
GreenContractRequirement::TestCommandProvenance,
53+
GreenContractRequirement::BaseBranchFreshness,
54+
GreenContractRequirement::RecoveryAttemptContext,
55+
],
5856
block_known_flakes: true,
5957
}
6058
}
6159

6260
#[must_use]
63-
pub fn evaluate(self, observed_level: Option<GreenLevel>) -> GreenContractOutcome {
61+
pub fn evaluate(&self, observed_level: Option<GreenLevel>) -> GreenContractOutcome {
6462
match observed_level {
6563
Some(level) if level >= self.required_level => GreenContractOutcome::Satisfied {
6664
required_level: self.required_level,
@@ -82,16 +80,23 @@ impl GreenContract {
8280
missing.push(GreenContractRequirement::RequiredLevel);
8381
}
8482

85-
if self.require_test_command_provenance && !evidence.has_passing_test_command() {
86-
missing.push(GreenContractRequirement::TestCommandProvenance);
87-
}
88-
89-
if self.require_base_branch_freshness && !evidence.base_branch_fresh {
90-
missing.push(GreenContractRequirement::BaseBranchFreshness);
91-
}
92-
93-
if self.require_recovery_attempt_context && !evidence.recovery_attempt_context_recorded {
94-
missing.push(GreenContractRequirement::RecoveryAttemptContext);
83+
for requirement in &self.requirements {
84+
match requirement {
85+
GreenContractRequirement::TestCommandProvenance
86+
if !evidence.has_passing_test_command() =>
87+
{
88+
missing.push(*requirement);
89+
}
90+
GreenContractRequirement::BaseBranchFreshness if !evidence.base_branch_fresh => {
91+
missing.push(*requirement);
92+
}
93+
GreenContractRequirement::RecoveryAttemptContext
94+
if !evidence.recovery_attempt_context_recorded =>
95+
{
96+
missing.push(*requirement);
97+
}
98+
_ => {}
99+
}
95100
}
96101

97102
if self.block_known_flakes {
@@ -119,7 +124,7 @@ impl GreenContract {
119124
}
120125

121126
#[must_use]
122-
pub fn is_satisfied_by(self, observed_level: GreenLevel) -> bool {
127+
pub fn is_satisfied_by(&self, observed_level: GreenLevel) -> bool {
123128
observed_level >= self.required_level
124129
}
125130
}

0 commit comments

Comments
 (0)