Skip to content

Commit 1461247

Browse files
joeywangzroz-agent
andcommitted
Fix the finalize budget, hand the writer to the coordinator, drop scaffolding
Review follow-ups on the AgentDriver wiring. 1. Derive the finalize budget instead of passing `upload_timeout`. The coordinator's floor is `script_timeout + upload_timeout`, so passing `upload_timeout` alone made `remaining > floor` false for every possible configuration (defaults: 120s > 60s + 120s). Because this path `return`s past the legacy one-shot upload, enabling `PeriodicHandoffCheckpoints` meant no end-of-run snapshot at all: runs shorter than one checkpoint interval uploaded nothing, and longer runs lost everything since the last periodic tick. Use `checkpoint_coordinator::finalize_budget(script_timeout, upload_timeout)`, which owns the floor so the two cannot drift apart. 2. Give the coordinator the declarations writer. `run_snapshot_upload` flushes queued driver-side `file` appends before running the declarations script so none is in flight when the bash script starts appending to the same JSONL. The coordinator path skipped that, so checkpoints could miss the agent's most recent edits. The coordinator is gated on a superset of the writer's own conditions, so the handle is always present when the coordinator exists. 3. Remove the temporary `allow(dead_code)` scaffolding. Both earlier PRs in this stack noted their allows were "removable once that PR is merged on top of this one" — this is that PR. Removes the module-wide `#![allow(dead_code)]` in `checkpoint_coordinator` (which would otherwise permanently blind the lint over new async/networking code) plus the per-item allows in `snapshot` and `harness_support`, along with the now-stale comments explaining them. Verified with `cargo clippy -p warp --all-targets --tests -- -D warnings`. Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent 5daf6d9 commit 1461247

4 files changed

Lines changed: 17 additions & 34 deletions

File tree

app/src/ai/agent_sdk/driver.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,10 @@ impl AgentDriver {
785785
client,
786786
id,
787787
working_dir.clone(),
788+
// Shared with the history subscription so every attempt can drain
789+
// queued `file` appends before the declarations script runs, exactly
790+
// as `run_snapshot_upload` does on the legacy path.
791+
snapshot_file_writer.clone(),
788792
ctx.spawner(),
789793
checkpoint_interval
790794
.unwrap_or(checkpoint_coordinator::DEFAULT_CHECKPOINT_INTERVAL),
@@ -3855,12 +3859,22 @@ impl AgentDriver {
38553859
}
38563860

38573861
// When the periodic checkpoint coordinator is active, it owns the entire
3858-
// end-of-run path: `finalize` regenerates declarations, runs one last
3859-
// best-effort attempt bounded by `upload_timeout`, and commits it as the
3862+
// end-of-run path: `finalize` drains the declarations writer, regenerates
3863+
// declarations, runs one last best-effort attempt, and commits it as the
38603864
// selected checkpoint. This replaces the legacy one-shot upload below so
38613865
// there is exactly one end-of-run snapshot path, not two.
3866+
//
3867+
// The budget must come from `finalize_budget`, not from `upload_timeout` alone:
3868+
// the coordinator's floor is `script_timeout + upload_timeout`, so a smaller
3869+
// budget silently skips the final attempt — and since this path `return`s past
3870+
// the legacy upload below, that would mean no end-of-run snapshot at all.
38623871
if let Some(coordinator) = checkpoint_coordinator {
3863-
coordinator.finalize(upload_timeout).await;
3872+
coordinator
3873+
.finalize(checkpoint_coordinator::finalize_budget(
3874+
script_timeout,
3875+
upload_timeout,
3876+
))
3877+
.await;
38643878
return;
38653879
}
38663880

app/src/ai/agent_sdk/driver/checkpoint_coordinator.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@
1616
//! This trades a small amount of latency (up to [`SAFE_BOUNDARY_POLL_INTERVAL`]) for
1717
//! avoiding new push-subscription wiring through the UI model graph.
1818
19-
// This module has no production caller yet -- `AgentDriver` wires
20-
// `CheckpointCoordinatorHandle::new` into its spawn/finalize lifecycle in a
21-
// follow-up, stacked PR. Until then, `CheckpointCoordinatorHandle::new` (and
22-
// everything it reaches) is unreachable outside `#[cfg(test)]`, which reaches the
23-
// same code through `new_for_test`. This module-level allow is temporary and should
24-
// be removable once that PR merges on top of this one.
25-
#![allow(dead_code)]
26-
2719
use std::path::PathBuf;
2820
use std::sync::Arc;
2921
use std::time::Duration;

app/src/ai/agent_sdk/driver/snapshot.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -670,11 +670,6 @@ struct SnapshotOutcome {
670670

671671
/// Outcome of one checkpoint attempt, as opposed to [`SnapshotOutcome`] which only tracks
672672
/// per-entry upload results within a single attempt.
673-
// The whole checkpoint pipeline below (through `run_checkpoint_pipeline`) has no
674-
// production caller yet -- the periodic checkpoint coordinator that drives it lands
675-
// in a follow-up, stacked PR. `#[allow(dead_code)]` is temporary and should be
676-
// removable once that PR is merged on top of this one.
677-
#[allow(dead_code)]
678673
#[derive(Debug)]
679674
pub(super) enum CheckpointResult {
680675
/// Every required object (blobs plus manifest) for `generation` uploaded successfully
@@ -710,14 +705,11 @@ enum PipelineMode {
710705
Legacy,
711706
/// Periodic or finalization checkpoint attempt: the server stores each requested file
712707
/// as `checkpoint_<generation>__<filename>` and does not charge the cumulative quota.
713-
// Not constructed until the coordinator PR (see the allow(dead_code) note above).
714-
#[allow(dead_code)]
715708
Checkpoint(CheckpointGeneration),
716709
}
717710

718711
/// Monotonic disambiguator for [`mint_generation`] so two attempts minted within the same
719712
/// millisecond (e.g. in tests, or on a very fast retry) never collide.
720-
#[allow(dead_code)]
721713
static GENERATION_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
722714

723715
/// Mint a new checkpoint generation identifier.
@@ -731,7 +723,6 @@ static GENERATION_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::Ato
731723
///
732724
/// Format: `<millis-since-epoch>-<counter>`. This satisfies the server's
733725
/// `[A-Za-z0-9._-]{1,128}` charset and never contains the reserved `__` separator.
734-
#[allow(dead_code)]
735726
pub(super) fn mint_generation() -> CheckpointGeneration {
736727
let millis = std::time::SystemTime::now()
737728
.duration_since(std::time::UNIX_EPOCH)
@@ -749,7 +740,6 @@ pub(super) fn mint_generation() -> CheckpointGeneration {
749740
/// building, and the upload-targets request; the server itself derives the storage name for
750741
/// each presigned upload target from that logical filename plus the request's `generation`
751742
/// field, so no client-side renaming is needed before that point.
752-
#[allow(dead_code)]
753743
fn storage_name(generation: &CheckpointGeneration, logical: &str) -> String {
754744
debug_assert!(
755745
!logical.contains("__"),
@@ -1012,7 +1002,6 @@ async fn run_pipeline(
10121002
/// Unlike [`upload_snapshot_from_declarations_file`], a missing/empty/unusable declarations
10131003
/// file is reported as [`CheckpointResult::Skipped`] rather than `None`, since the coordinator
10141004
/// needs to distinguish "nothing to do" from "tried and failed" to drive its state machine.
1015-
#[allow(dead_code)]
10161005
pub(super) async fn run_checkpoint_from_declarations_file(
10171006
path: &Path,
10181007
client: Arc<dyn HarnessSupportClient>,
@@ -1036,7 +1025,6 @@ pub(super) async fn run_checkpoint_from_declarations_file(
10361025
/// Upload and commit an already-gathered payload under `generation`. Split out from
10371026
/// [`run_checkpoint_from_declarations_file`] so a caller retrying the exact same attempt (as
10381027
/// opposed to gathering fresh) can reuse both the payload and the generation.
1039-
#[allow(dead_code)]
10401028
async fn run_checkpoint_pipeline(
10411029
client: Arc<dyn HarnessSupportClient>,
10421030
generation: CheckpointGeneration,

app/src/server/server_api/harness_support.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,6 @@ impl CheckpointGeneration {
150150
/// enforced it, so "validated" was aspirational. `snapshot::mint_generation` is the only
151151
/// production caller and provably satisfies it, hence a `debug_assert!` rather than a
152152
/// fallible constructor.
153-
// Only called by `snapshot::mint_generation`, which is itself unused until the
154-
// periodic checkpoint coordinator (a follow-up, stacked PR) lands.
155-
#[allow(dead_code)]
156153
pub(crate) fn from_validated(value: String) -> Self {
157154
debug_assert!(
158155
Self::is_valid(&value),
@@ -161,7 +158,6 @@ impl CheckpointGeneration {
161158
Self(value)
162159
}
163160

164-
#[allow(dead_code)]
165161
pub fn as_str(&self) -> &str {
166162
&self.0
167163
}
@@ -181,17 +177,13 @@ impl std::fmt::Display for CheckpointGeneration {
181177
/// server persists `objects` verbatim as the commit marker and later selection returns
182178
/// exactly that set, never every object sharing the generation prefix. See
183179
/// `docs/remote-2111-checkpoint-spec.md` (warp-server) for the full protocol.
184-
// Not constructed until the periodic checkpoint coordinator (a follow-up, stacked PR)
185-
// starts calling `HarnessSupportClient::commit_snapshot`.
186-
#[allow(dead_code)]
187180
#[derive(Debug, Clone, serde::Serialize)]
188181
pub struct CommitSnapshotRequest {
189182
pub generation: String,
190183
pub manifest_object: String,
191184
pub objects: Vec<String>,
192185
}
193186

194-
#[allow(dead_code)]
195187
#[derive(Debug, Clone, serde::Deserialize)]
196188
pub struct CommitSnapshotResponse {
197189
pub generation: String,
@@ -367,9 +359,6 @@ pub trait HarnessSupportClient: 'static + Send + Sync {
367359
/// (including `request.manifest_object`) has itself uploaded successfully; the
368360
/// server verifies existence and per-attempt size limits before this becomes the
369361
/// selected checkpoint.
370-
// Not called until the periodic checkpoint coordinator (a follow-up, stacked PR)
371-
// lands.
372-
#[allow(dead_code)]
373362
async fn commit_snapshot(
374363
&self,
375364
request: &CommitSnapshotRequest,

0 commit comments

Comments
 (0)