mirror of
https://github.com/instructkr/claw-code.git
synced 2026-05-19 05:51:26 +08:00
omx(team): auto-checkpoint worker-1 [1]
This commit is contained in:
@@ -449,18 +449,19 @@ pub fn compute_event_fingerprint(
|
|||||||
status: &LaneEventStatus,
|
status: &LaneEventStatus,
|
||||||
data: Option<&serde_json::Value>,
|
data: Option<&serde_json::Value>,
|
||||||
) -> String {
|
) -> String {
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use sha2::{Digest, Sha256};
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
let payload = serde_json::json!({
|
||||||
format!("{event:?}").hash(&mut hasher);
|
"event": event,
|
||||||
format!("{status:?}").hash(&mut hasher);
|
"status": status,
|
||||||
if let Some(d) = data {
|
"data": data,
|
||||||
serde_json::to_string(d)
|
});
|
||||||
.unwrap_or_default()
|
let canonical = serde_json::to_vec(&payload).unwrap_or_default();
|
||||||
.hash(&mut hasher);
|
let digest = Sha256::digest(canonical);
|
||||||
}
|
digest[..8]
|
||||||
format!("{:016x}", hasher.finish())
|
.iter()
|
||||||
|
.map(|byte| format!("{byte:02x}"))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Classification of event terminality for reconciliation.
|
/// Classification of event terminality for reconciliation.
|
||||||
@@ -1045,6 +1046,7 @@ impl LaneEvent {
|
|||||||
emitted_at,
|
emitted_at,
|
||||||
)
|
)
|
||||||
.with_optional_detail(detail)
|
.with_optional_detail(detail)
|
||||||
|
.with_terminal_fingerprint()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -1098,7 +1100,7 @@ impl LaneEvent {
|
|||||||
event =
|
event =
|
||||||
event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
||||||
}
|
}
|
||||||
event
|
event.with_terminal_fingerprint()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ship prepared — §4.44.5
|
/// Ship prepared — §4.44.5
|
||||||
@@ -1170,6 +1172,21 @@ impl LaneEvent {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_data(mut self, data: Value) -> Self {
|
pub fn with_data(mut self, data: Value) -> Self {
|
||||||
self.data = Some(data);
|
self.data = Some(data);
|
||||||
|
if is_terminal_event(self.event) {
|
||||||
|
self = self.with_terminal_fingerprint();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn with_terminal_fingerprint(mut self) -> Self {
|
||||||
|
if is_terminal_event(self.event) {
|
||||||
|
self.metadata.event_fingerprint = Some(compute_event_fingerprint(
|
||||||
|
&self.event,
|
||||||
|
&self.status,
|
||||||
|
self.data.as_ref(),
|
||||||
|
));
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1375,6 +1392,39 @@ mod tests {
|
|||||||
assert_eq!(round_trip.event, LaneEventName::ShipPushedMain);
|
assert_eq!(round_trip.event, LaneEventName::ShipPushedMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convenience_terminal_events_attach_and_refresh_fingerprints() {
|
||||||
|
let finished = LaneEvent::finished("2026-04-04T00:00:00Z", Some("done".to_string()));
|
||||||
|
let initial_fingerprint = finished
|
||||||
|
.metadata
|
||||||
|
.event_fingerprint
|
||||||
|
.clone()
|
||||||
|
.expect("finished events should carry terminal fingerprint");
|
||||||
|
|
||||||
|
let with_payload = finished.with_data(json!({"result": "ok", "attempt": 1}));
|
||||||
|
assert!(with_payload.metadata.event_fingerprint.is_some());
|
||||||
|
assert_ne!(
|
||||||
|
Some(initial_fingerprint),
|
||||||
|
with_payload.metadata.event_fingerprint,
|
||||||
|
"payload changes must refresh the actionable terminal fingerprint"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_style_finished_events_dedupe_after_payload_is_added() {
|
||||||
|
let first = LaneEvent::finished("2026-04-04T00:00:00Z", Some("done".to_string()))
|
||||||
|
.with_data(json!({"result": "ok"}));
|
||||||
|
let duplicate = LaneEvent::finished("2026-04-04T00:00:01Z", Some("done again".to_string()))
|
||||||
|
.with_data(json!({"result": "ok"}));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
first.metadata.event_fingerprint,
|
||||||
|
duplicate.metadata.event_fingerprint
|
||||||
|
);
|
||||||
|
let deduped = dedupe_terminal_events(&[first, duplicate]);
|
||||||
|
assert_eq!(deduped.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn commit_events_can_carry_worktree_and_supersession_metadata() {
|
fn commit_events_can_carry_worktree_and_supersession_metadata() {
|
||||||
let event = LaneEvent::commit_created(
|
let event = LaneEvent::commit_created(
|
||||||
|
|||||||
Reference in New Issue
Block a user