Accept legacy review return events
This commit is contained in:
+96
-12
@@ -775,18 +775,23 @@ fn hydrate_session(
|
||||
record.sequence
|
||||
)
|
||||
})?;
|
||||
ensure_record_commitment(&record, &commitment.id)?;
|
||||
if !review_completed {
|
||||
return Err(anyhow!(
|
||||
"review completion is required before returning to planning at sequence {}",
|
||||
record.sequence
|
||||
));
|
||||
}
|
||||
if commitment.state != CommitmentState::Completed {
|
||||
return Err(anyhow!(
|
||||
"reviewed commitment must be terminal before returning to planning at sequence {}",
|
||||
record.sequence
|
||||
));
|
||||
let legacy_unlinked_review_return = record.commitment_id.is_none()
|
||||
&& !review_completed
|
||||
&& commitment.state == CommitmentState::Active;
|
||||
if !legacy_unlinked_review_return {
|
||||
ensure_record_commitment(&record, &commitment.id)?;
|
||||
if !review_completed {
|
||||
return Err(anyhow!(
|
||||
"review completion is required before returning to planning at sequence {}",
|
||||
record.sequence
|
||||
));
|
||||
}
|
||||
if commitment.state != CommitmentState::Completed {
|
||||
return Err(anyhow!(
|
||||
"reviewed commitment must be terminal before returning to planning at sequence {}",
|
||||
record.sequence
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -2318,6 +2323,85 @@ mod tests {
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replay_accepts_legacy_unlinked_review_to_planning() {
|
||||
let path = temp_log_path("legacy-review-return");
|
||||
let first_commitment_id = "commitment-legacy-first";
|
||||
let second_commitment_id = "commitment-legacy-second";
|
||||
{
|
||||
let mut log = EventLog::open(&path).unwrap();
|
||||
log.append_batch([
|
||||
PendingEventRecord {
|
||||
event_type: EventType::RuntimeTransition,
|
||||
runtime_state: RuntimeState::Planning,
|
||||
commitment_id: None,
|
||||
payload_json: json!({
|
||||
"schema_version": POLICY_SCHEMA_VERSION,
|
||||
"action": "enter_planning",
|
||||
"from": "locked",
|
||||
"to": "planning",
|
||||
}),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::CommitmentCreated,
|
||||
runtime_state: RuntimeState::Active,
|
||||
commitment_id: Some(first_commitment_id.to_string()),
|
||||
payload_json: commitment_payload(first_commitment_id, CommitmentState::Active),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::PolicyApplied,
|
||||
runtime_state: RuntimeState::Active,
|
||||
commitment_id: Some(first_commitment_id.to_string()),
|
||||
payload_json: policy_payload(first_commitment_id),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::RuntimeTransition,
|
||||
runtime_state: RuntimeState::Review,
|
||||
commitment_id: Some(first_commitment_id.to_string()),
|
||||
payload_json: json!({
|
||||
"schema_version": POLICY_SCHEMA_VERSION,
|
||||
"action": "complete_for_review",
|
||||
"from": "active",
|
||||
"to": "review",
|
||||
}),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::RuntimeTransition,
|
||||
runtime_state: RuntimeState::Planning,
|
||||
commitment_id: None,
|
||||
payload_json: json!({
|
||||
"schema_version": POLICY_SCHEMA_VERSION,
|
||||
"action": "return_to_planning_after_review",
|
||||
"from": "review",
|
||||
"to": "planning",
|
||||
}),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::CommitmentCreated,
|
||||
runtime_state: RuntimeState::Active,
|
||||
commitment_id: Some(second_commitment_id.to_string()),
|
||||
payload_json: commitment_payload(second_commitment_id, CommitmentState::Active),
|
||||
},
|
||||
PendingEventRecord {
|
||||
event_type: EventType::PolicyApplied,
|
||||
runtime_state: RuntimeState::Active,
|
||||
commitment_id: Some(second_commitment_id.to_string()),
|
||||
payload_json: policy_payload(second_commitment_id),
|
||||
},
|
||||
])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let reopened = SessionController::new(&path).unwrap();
|
||||
|
||||
assert_eq!(reopened.runtime_state(), RuntimeState::Active);
|
||||
assert_eq!(
|
||||
reopened.active_commitment().unwrap().id,
|
||||
second_commitment_id
|
||||
);
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replay_tolerates_review_completed_without_changing_runtime() {
|
||||
let path = temp_log_path("review-completed-no-transition");
|
||||
|
||||
Reference in New Issue
Block a user