Skip to content
Merged
Show file tree
Hide file tree
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
89 changes: 89 additions & 0 deletions crates/design-challenge-task/src/emit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! Design leaf-emit scheduling (late-tempo filler + catch-up).

/// How many blocks before epoch end the NotAttempted filler may run.
///
/// Wider than the historical 48-block window so `base-real-seal` (10 min) still
/// has time to seal after design emits, while leaving most of the epoch for
/// `award_round` to land Score leaves first (first-write-wins).
pub const DESIGN_EMIT_LATE_BLOCKS: u64 = 96;

/// Planned design leaf emission for one emitter tick.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DesignEmitPlan {
/// Epoch label for the leaf set.
pub epoch: u64,
/// Metagraph pin block (epoch start).
pub pin_block: u64,
}

/// Decide whether/which epoch the design filler should emit.
///
/// - Catch up `last_emitted+1` when behind by more than one epoch (repairs the
/// end-of-epoch relabel race that skipped alternate epochs in prod).
/// - Otherwise wait until the last [`DESIGN_EMIT_LATE_BLOCKS`] of the current
/// epoch so admin awards can submit Score leaves first.
#[must_use]
pub fn design_emit_plan(
last_emitted: u64,
current_epoch: u64,
blocks_since_last_step: u64,
tempo: u64,
current_last_epoch_block: u64,
) -> Option<DesignEmitPlan> {
if current_epoch == 0 {
return None;
}
let tempo = tempo.max(1);
if last_emitted >= current_epoch {
return None;
}
// Sequential catch-up for skipped epochs (award path / boundary race).
if last_emitted + 1 < current_epoch {
let target = last_emitted + 1;
let epochs_back = current_epoch.saturating_sub(target);
let pin_block = current_last_epoch_block.saturating_sub(epochs_back.saturating_mul(tempo));
return Some(DesignEmitPlan {
epoch: target,
pin_block,
});
}
// Current epoch: late-tempo filler only.
if blocks_since_last_step.saturating_add(DESIGN_EMIT_LATE_BLOCKS) < tempo {
return None;
}
Some(DesignEmitPlan {
epoch: current_epoch,
pin_block: current_last_epoch_block,
})
}

#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;

#[test]
fn emit_plan_waits_until_late_tempo_for_current_epoch() {
assert!(design_emit_plan(10, 11, 200, 360, 1000).is_none());
let p = design_emit_plan(10, 11, 360 - DESIGN_EMIT_LATE_BLOCKS, 360, 1000).unwrap();
assert_eq!(
p,
DesignEmitPlan {
epoch: 11,
pin_block: 1000
}
);
}

#[test]
fn emit_plan_catches_up_skipped_epochs_without_waiting() {
let p = design_emit_plan(24412, 24423, 50, 360, 8_815_687).unwrap();
assert_eq!(p.epoch, 24413);
assert_eq!(p.pin_block, 8_815_687 - (24423 - 24413) * 360);
}

#[test]
fn emit_plan_noop_when_already_emitted_current() {
assert!(design_emit_plan(11, 11, 350, 360, 1000).is_none());
}
}
23 changes: 22 additions & 1 deletion crates/design-challenge-task/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#![forbid(unsafe_code)]
#![allow(clippy::missing_errors_doc, clippy::doc_markdown)]

mod emit;
mod score;
pub use emit::{design_emit_plan, DesignEmitPlan, DESIGN_EMIT_LATE_BLOCKS};
pub use score::{
not_attempted, round_win_delta, score_window, to_leaf, window_start, ScorePlan, WindowScorePlan,
};
Expand Down Expand Up @@ -77,7 +79,8 @@ pub const fn unscored_epochs_elapsed(start_epoch: u64, current_epoch: u64) -> bo
current_epoch.saturating_sub(start_epoch) >= UNSCORED_EPOCH_LIMIT
}

fn now_ms() -> u64 {
#[must_use]
pub fn now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| u64::try_from(d.as_millis()).unwrap_or(u64::MAX))
Expand Down Expand Up @@ -320,6 +323,24 @@ pub const fn rounds_for_day(day_index: u64) -> (u64, u64) {
(start, start + ROUNDS_PER_DAY - 1)
}

/// Cap harness log payload stored in stage-event detail (JSON).
pub const MAX_LOG_CHARS: usize = 65_536;

#[must_use]
pub fn now_secs() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs())
}

#[must_use]
pub fn clip_logs(text: &str) -> String {
if text.len() <= MAX_LOG_CHARS {
return text.to_owned();
}
format!("...[truncated]\n{}", &text[text.len() - MAX_LOG_CHARS..])
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
36 changes: 31 additions & 5 deletions crates/design-challenge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ pub use challenge_common::{
GatewayClient, GatewayClientConfig, LeafEmitError,
};
pub use design_challenge_task::{
agent_run_timeout_secs, awaiting_admin_unscored_expired, daily_run_quota,
agent_run_timeout_secs, awaiting_admin_unscored_expired, daily_run_quota, design_emit_plan,
manual_daily_run_quota, prompts_per_round, reject_awaiting_admin_run, round_id_at, round_secs,
round_win_delta, rounds_per_day_effective, scheduled_daily_run_cap, scheduled_runs_per_day,
score_window, unscored_epochs_elapsed, window_start, ScorePlan, WindowScorePlan, CHALLENGE_ID,
CHALLENGE_ID_BYTES, MANUAL_DAILY_RUN_QUOTA, PROMPTS_PER_ROUND, ROUNDS_PER_DAY, ROUND_SECS,
SCORE_MAX, SCORING_VERSION, SCORING_WINDOW_ROUNDS, UNSCORED_EPOCH_LIMIT,
score_window, unscored_epochs_elapsed, window_start, DesignEmitPlan, ScorePlan,
WindowScorePlan, CHALLENGE_ID, CHALLENGE_ID_BYTES, DESIGN_EMIT_LATE_BLOCKS,
MANUAL_DAILY_RUN_QUOTA, PROMPTS_PER_ROUND, ROUNDS_PER_DAY, ROUND_SECS, SCORE_MAX,
SCORING_VERSION, SCORING_WINDOW_ROUNDS, UNSCORED_EPOCH_LIMIT,
};
pub use design_http::{
design_router, mark_awaiting, mark_awaiting_admin, record_epoch, AdminAwardHook, AppState,
Expand All @@ -46,7 +47,6 @@ pub use host_sim::{
};
pub use orchestrator::{ErrorClass, Orchestrator, OrchestratorConfig};

/// Crate identity smoke.
#[must_use]
pub fn crate_name() -> &'static str {
"design-challenge"
Expand All @@ -62,4 +62,30 @@ mod tests {
assert_eq!(CHALLENGE_ID, "design");
assert_eq!(SCORING_VERSION, 3);
}

#[test]
fn emit_plan_waits_until_late_tempo_for_current_epoch() {
assert!(design_emit_plan(10, 11, 200, 360, 1000).is_none());
let p = design_emit_plan(10, 11, 360 - DESIGN_EMIT_LATE_BLOCKS, 360, 1000).unwrap();
assert_eq!(
p,
DesignEmitPlan {
epoch: 11,
pin_block: 1000
}
);
}

#[test]
fn emit_plan_catches_up_skipped_epochs_without_waiting() {
// Prod failure mode: award/boundary race skipped 24413 while chain is 24423.
let p = design_emit_plan(24412, 24423, 50, 360, 8_815_687).unwrap();
assert_eq!(p.epoch, 24413);
assert_eq!(p.pin_block, 8_815_687 - (24423 - 24413) * 360);
}

#[test]
fn emit_plan_noop_when_already_emitted_current() {
assert!(design_emit_plan(11, 11, 350, 360, 1000).is_none());
}
}
Loading
Loading