Skip to content

Make Harbor artifact transfer budgets configurable - #2567

Open
xeophon wants to merge 2 commits into
mainfrom
fix/harbor-artifact-budget
Open

Make Harbor artifact transfer budgets configurable#2567
xeophon wants to merge 2 commits into
mainfrom
fix/harbor-artifact-budget

Conversation

@xeophon

@xeophon xeophon commented Sep 8, 2026

Copy link
Copy Markdown
Member

Harbor tasks that produce trained checkpoints or VM disk files can exceed the fixed 32 MiB artifact-transfer cap and fail before separate grading. Add a per-task artifact budget so environments can provide enough space for their declared outputs.

HarborConfig.artifact_max_bytes is copied into TaskData and passed to the shared collector from both Harbor finalization and deferred rollout collection. The collector applies one total budget across all declared roots and the implicit /logs/artifacts/ directory, while retaining 32 MiB as the default.

On Prime VMs, bounded reads stream binary chunks into a capped buffer and close the process on success, failure, or cancellation. Artifact payloads remain transient and excluded from persisted traces.

This provides the artifact-budget support required by the Terminal-Bench 4 environment in prime-envs #800 and PTB Max #4, whose default budget is 4 GiB.


Note

Medium Risk
Raises configurable artifact memory on the host and changes Prime VM read semantics for bounded reads; defaults are unchanged but misconfigured large budgets could increase rollout memory use.

Overview
Adds a configurable total artifact archive budget (default still 32 MiB) so Harbor and generic rollouts can collect larger solver outputs—checkpoints, VM disks—without hitting a hard cap. HarborConfig / TaskData expose artifact_max_bytes, copied into each Harbor task and passed into collect() from rollout finalize and Harbor finalize; docs describe --env.taskset.artifact-max-bytes.

Prime VM bounded reads no longer pipe files through base64 + execute_command; they stream raw bytes via open_process and head -c, with independent limits on the sandbox read and host buffer—better suited to artifact collection at higher budgets.

Tests assert runtime state (including in-memory artifacts) stays off persisted traces when writing episodes; harbor docs note the same.

Reviewed by Cursor Bugbot for commit 0b6e2d8. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Make Harbor artifact transfer byte budget configurable via artifact_max_bytes

  • Adds a positive artifact_max_bytes field to TaskData and HarborConfig, defaulting to 32 MiB (MAX_ARTIFACT_BYTES), and propagates it through Harbor task parsing and non-deferred/rollout fallback artifact collection calls.
  • Updates the collect utility in artifacts.py to accept a keyword-only max_bytes budget and subtract each archive's length before collecting the next entry.
  • Rewrites PrimeRuntime._read in prime.py to stream raw binary via head -c into a BytesIO buffer instead of base64-decoding command output, raising SandboxError when the stream exceeds the limit or the process exits non-zero.
  • Extends the trace regression test in test_trace.py to verify binary artifacts in custom runtime state stay in memory and are excluded from persisted episode JSONL.
  • Behavioral Change: artifact collection now enforces the caller-selected total budget across all entries; callers that omit max_bytes retain the 32 MiB default. PrimeRuntime._read no longer uses base64 or an effectively-unbounded timeout.

Macroscope summarized 0b6e2d8.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T21:05:51.298159Z 0b6e2d8 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@macroscopeapp

macroscopeapp Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR changes the production artifact-transfer pipeline by allowing substantially larger archives and introducing a new Prime VM streaming path across both normal and deferred grading. The runtime cleanup contract for that path remains insufficiently established, so human review is warranted.

You can add or adjust custom eligibility rules. Learn more.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c05595bf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".



class HarborConfig(TasksetConfig):
artifact_max_bytes: int = Field(MAX_ARTIFACT_BYTES, gt=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.

P1 Badge Keep raw artifact bytes out of persisted episodes

When this limit is raised for the advertised trained-checkpoint or disk-image use case, collect() leaves the entire tar archive as bytes in trace.state.artifacts; neither IsolatedVerifierEnv.stage_verifier (verifiers/v1/envs/isolated_verifier/env.py:114-119) nor Harbor finalization clears it, and write_episode() (verifiers/v1/cli/output.py:147-153) subsequently applies Pydantic JSON serialization to the whole episode. Pydantic's default bytes serializer requires valid UTF-8, so typical binary archives make an otherwise successful rollout fail while saving its result; even UTF-8-compatible payloads produce multi-gigabyte trace records. Exclude or clear these transient payloads after restoration, or persist them separately, before allowing multi-gigabyte budgets.

Useful? React with 👍 / 👎.

existence.extend(output.splitlines())
collected: dict[str, bytes | None] = {}
budget = MAX_ARTIFACT_BYTES
budget = max_bytes

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 Avoid command-output transport for multi-gigabyte artifacts

With a multi-gigabyte budget on the default Prime VM runtime, _tar_out() passes this remaining value to Runtime.read, whose VM implementation (verifiers/v1/runtimes/prime.py:372-387) returns the complete archive through base64-encoded execute_command stdout before decoding it. A 4 GiB archive therefore holds more than 5.3 GiB of stdout plus the decoded 4 GiB payload simultaneously and can exhaust the host or command-output transport; the uncapped path immediately below already uses download_file specifically to avoid output limits and base64 overhead. Large configured budgets need a bounded streaming/download path rather than the command-output read path.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0b6e2d8. Configure here.

stderr = (stderr + chunk)[-500:]
if await process.wait():
raise SandboxError(stderr.decode(errors="replace").strip())
return data.getvalue()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

VM read cleanup calls missing aclose

High Severity

The new Prime VM bounded-read path wraps the SDK process in contextlib.aclosing, which always calls aclose() when leaving the block, including after a successful return. Elsewhere this process is used through wait, terminate, kill, and write_stdin; PrimeProcess never forwards aclose. If that method is absent, every successful Prime VM artifact read fails, so Harbor collection cannot transfer the larger checkpoints this change is meant to allow.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0b6e2d8. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant