Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion rust_hft/alpha-harness/domain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,8 @@ pub fn runtime_stage_is_healthy(
}
if event.outcome == AttributionOutcome::Healthy
&& event.kind == AttributionKind::PortfolioSnapshot
&& (mode != AttributionMode::LiveSmall
|| portfolio_snapshot_has_authoritative_truth(event))
Comment on lines +1360 to +1361

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire the LiveSmall gate into the eligibility path

This gate only runs when some caller passes AttributionMode::LiveSmall, but the LiveSmall eligibility workflow never does: close_runtime_stages checks runtime_stage_is_healthy for Paper and Shadow, then advances LiveSmallEligible solely from live_small_eligibility_approval (rust_hft/alpha-harness/app/src/loop_control.rs lines 337, 354, and 371-390), and the existing e2e test even completes with no LiveSmall attribution events. As a result, missing or stale reconciliation metrics still do not block the actual LiveSmall promotion path unless that path is also changed to require a healthy LiveSmall stage.

Useful? React with 👍 / 👎.

{
if let Some(strategy_id) = event.strategy_id.as_ref() {
health
Expand Down Expand Up @@ -1387,6 +1389,27 @@ pub fn runtime_stage_is_healthy(
})
}

const MAX_RUNTIME_RECONCILIATION_AGE_US: f64 = 30_000_000.0;

fn portfolio_snapshot_has_authoritative_truth(event: &RuntimeAttributionEvent) -> bool {
let metric_is_one = |name: &str| {
event
.metrics
.get(name)
.is_some_and(|value| value.is_finite() && *value >= 1.0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require exact truth flags for LiveSmall metrics

When a signed feedback producer emits a value above 1.0 for one of these boolean truth flags, such as a count or percentage instead of the normalized 0.0/1.0 coverage values used by the runtime producer, RuntimeAttributionEvent::validate only checks finiteness, so this helper treats the malformed metric as authoritative and can mark a LiveSmall snapshot healthy. Since these metrics are the fail-closed promotion evidence, require the exact complete value rather than accepting any value greater than it.

Useful? React with 👍 / 👎.

};
let age_is_fresh = event
.metrics
.get("venue_reconciliation_age_us")
.is_some_and(|value| {
value.is_finite() && *value >= 0.0 && *value <= MAX_RUNTIME_RECONCILIATION_AGE_US

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expire old LiveSmall snapshots at decision time

When LiveSmall health is evaluated after feedback was written, for example after importing a log and resuming the loop minutes later, this check still treats the snapshot as fresh forever because it only verifies the persisted venue_reconciliation_age_us value from observation time and never compares event.observed_at to the current decision time; runtime_attributions_for_mission returns all historical attribution events for the mission. A single once-fresh snapshot can therefore remain promotion evidence indefinitely, so the health gate needs a decision-time expiry or a current-time parameter instead of relying only on the recorded reconciliation age.

Useful? React with 👍 / 👎.

});
metric_is_one("authoritative_account_snapshot_coverage")
&& metric_is_one("venue_reconciliation_complete")
&& metric_is_one("venue_reconciliation_healthy")
&& age_is_fresh
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LearningDirective {
pub directive_id: String,
Expand Down Expand Up @@ -2569,7 +2592,7 @@ mod tests {
}

#[test]
fn runtime_health_requires_activation_fill_and_snapshot_from_one_strategy() {
fn runtime_health_requires_reconciliation_truth_only_for_live_small() {
let now = Utc::now();
let scoped = |id: &str,
kind: AttributionKind,
Expand Down Expand Up @@ -2619,6 +2642,34 @@ mod tests {
"candidate-1",
AttributionMode::Shadow
));

let mut live_small_events = events.clone();
for event in &mut live_small_events {
event.mode = AttributionMode::LiveSmall;
}
assert!(!runtime_stage_is_healthy(
&live_small_events,
"candidate-1",
AttributionMode::LiveSmall
));
let snapshot = live_small_events.last_mut().unwrap();
snapshot
.metrics
.insert("authoritative_account_snapshot_coverage".to_string(), 1.0);
snapshot
.metrics
.insert("venue_reconciliation_complete".to_string(), 1.0);
snapshot
.metrics
.insert("venue_reconciliation_healthy".to_string(), 1.0);
snapshot
.metrics
.insert("venue_reconciliation_age_us".to_string(), 1_000.0);
assert!(runtime_stage_is_healthy(
&live_small_events,
"candidate-1",
AttributionMode::LiveSmall
));
}

#[test]
Expand Down
Loading