Skip to content

Upload all skill files concurrently - #2540

Open
xeophon wants to merge 2 commits into
mainfrom
perf/ordered-skill-uploads
Open

Upload all skill files concurrently#2540
xeophon wants to merge 2 commits into
mainfrom
perf/ordered-skill-uploads

Conversation

@xeophon

@xeophon xeophon commented Sep 5, 2026

Copy link
Copy Markdown
Member

Upload all configured skill files concurrently through the existing runtime write API, then restore executable bits in one chmod call. Harness setup waits for every upload to settle before returning or propagating an upload error.

File and skill upload order is unspecified. Configurations with colliding destination paths must not depend on which write wins. File contents are buffered in memory for the upload phase; the runtime client's connection pool manages network connections.

Warm Prime VM measurements, including the runtime upload-directory improvement already merged in #2539:

Workload Current main Batches of eight All concurrent
Install 32 files 6.099 s 1.447 s 1.009 s
Install 128 files Not measured 4.425 s 1.705 s
Install three skills, 37 files Not measured 2.539 s 1.062 s
Full warm Codex setup, 32 skill files 7.776 s 3.238 s 2.776 s

One temporary vm=True Prime VM (python:3.11-slim, 1 CPU, 2 GiB), one warmup round and six interleaved measured rounds per workload. All variants use the full Prime runtime from main at 284bcfdfb9fa. Provisioning and initial program downloads are excluded; the setup measurement includes the full existing warm Codex setup path.

This comparison measures setup latency on one VM, including up to 128 concurrent uploads. It does not measure model inference or throughput across many simultaneous rollouts.
Observed all-concurrent install/multi range: 0.950–1.061 s.
Observed all-concurrent install/large range: 1.614–1.846 s.
Observed all-concurrent setup/multi range: 2.498–4.926 s.


Note

Medium Risk
Concurrent runtime writes can change failure timing and load on the sandbox API compared to sequential uploads, though paths and post-upload chmod behavior stay the same.

Overview
Harness.install_skills no longer awaits each skill file upload one at a time. It first collects every (target, bytes) pair and executable path, then runs all runtime.write calls concurrently with asyncio.gather, failing only after all uploads settle (re-raising the first exception from the batch).

Executable chmod now runs once for the full rollout after uploads complete, instead of per skill folder. File discovery drops sorted() on rglob, so iteration order is no longer fixed but destinations are unchanged.

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

Note

Batch skill uploads in Harness.install_skills via tar archives

  • Replaces single-file runtime writes with ordered tar batches for runtimes that support live processes. Batches contain at most 32 files or 1 MiB of file sizes.
  • Adds the _EXTRACT_SKILLS helper script to extract uploaded archives in the runtime and remove the temporary archive path on exit.
  • Singleton batches or runtimes without live-process support continue to use direct writes.
  • Risk: Multi-file extraction failures now raise SandboxError, attempting shielded process termination and temporary archive removal.
📊 Macroscope summarized b34cf2a. 1 file reviewed, 2 issues evaluated, 0 issues filtered, 2 comments posted

🗂️ Filtered Issues

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 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-05T10:04:19.206264Z b34cf2a 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 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes every skill-supporting harness setup from ordered per-file writes to unbounded concurrent writes while buffering the entire skill tree, affecting runtime resource usage and overwrite ordering. Unresolved comments identify concrete memory/process pressure and nondeterministic collision risks that require human review.

Not approved because:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@xeophon xeophon changed the title Batch skill uploads through ordered archives Upload all skill files concurrently Sep 5, 2026
@xeophon
xeophon requested a review from hallerite September 5, 2026 10:02
Comment thread verifiers/v1/harness.py
# `write` moves bytes, not modes; restore the execute bits scripts need.
await runtime.run(["chmod", "+x", *executables], {})
# Settle every upload before returning or surfacing an error.
results = await asyncio.gather(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High v1/harness.py:119

When two configured skills have the same basename (or the same folder is listed twice), duplicate destinations are written concurrently, so the loaded skill can contain a nondeterministic mix of the skills rather than the later configured skill deterministically overwriting the earlier one. Replace the concurrent asyncio.gather with ordered writes so duplicate targets are resolved consistently.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @verifiers/v1/harness.py around line 119:

When two configured skills have the same basename (or the same folder is listed twice), duplicate destinations are written concurrently, so the loaded skill can contain a nondeterministic mix of the skills rather than the later configured skill deterministically overwriting the earlier one. Replace the concurrent `asyncio.gather` with ordered writes so duplicate targets are resolved consistently.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The ordering change is intentional. This PR makes file and skill upload order unspecified, including writes from different configured folders that share a destination. Skills are expected to use distinct destination paths; preserving the previous last-configured-wins behavior for collisions is outside the intended contract of this change. The PR description explicitly documents this, and we are keeping concurrent uploads.

Comment thread verifiers/v1/harness.py
"""Upload each `config.skills` folder into `runtime` at `dest/<folder name>` —
the program's fixed skill discovery location, which a supporting harness's
`setup` passes."""
uploads = []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High v1/harness.py:103

install_skills loads every skill file into memory and starts a concurrent write task for each file, so a large skill tree can exhaust runner memory or Docker exec process resources during setup. Write each file before reading or scheduling the next one, as the previous sequential loop did.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @verifiers/v1/harness.py around line 103:

`install_skills` loads every skill file into memory and starts a concurrent write task for each file, so a large skill tree can exhaust runner memory or Docker `exec` process resources during setup. Write each file before reading or scheduling the next one, as the previous sequential loop did.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed that this increases peak resource usage: all selected file contents remain in memory, and Docker can start one exec process per write. This is an accepted tradeoff of the all-concurrent design. The SDK connection pool limits connections, not buffered bytes or Docker processes. The PR description documents the buffering; the measurements cover one Prime VM with up to 128 concurrent uploads, not large trees across many rollouts or Docker. We are keeping the all-concurrent implementation with those limits on the performance claim.

@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: b34cf2a337

ℹ️ 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".

Comment thread verifiers/v1/harness.py
Comment on lines +119 to +121
results = await asyncio.gather(
*(runtime.write(target, data) for target, data in uploads),
return_exceptions=True,

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 Bound concurrent skill writes

When a skill contains many files, especially while multiple rollouts are being set up, this creates one in-flight runtime.write per file with no concurrency limit, after retaining every file's bytes in uploads. For example, DockerRuntime.write starts a separate docker exec process for every call, while remote runtimes issue an RPC per call, so a large tree multiplied by eval concurrency can exhaust processes, connections, provider limits, or host memory and fail setup. Upload in bounded batches or through a semaphore instead of gathering the entire tree at once.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a real limitation of the chosen all-concurrent design. Peak buffered memory scales with the total selected file bytes, and concurrent rollout setup can multiply the upload/process pressure. The SDK connection pool does not provide a general resource bound. We explicitly accept that tradeoff for this PR and document the buffering and the single-VM measurement scope. The benchmark does not establish safety for arbitrarily large skill trees or many simultaneous rollouts; the implementation will remain all-concurrent.

Comment thread verifiers/v1/harness.py
Comment on lines +119 to +120
results = await asyncio.gather(
*(runtime.write(target, data) for target, data in uploads),

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 Preserve overwrite order for colliding skill paths

When config.skills contains directories with the same basename and relative filename, both uploads target the same destination, and the config currently permits this. gather preserves result ordering but not write completion ordering, so whichever remote upload finishes last now wins nondeterministically; the previous sequential awaits made the later configured skill deterministically overwrite the earlier one. Reject duplicate targets or serialize writes that collide.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct: gather preserves result ordering, not write completion ordering. This PR intentionally leaves completion order unspecified for both files and skills, including duplicate destination paths. Distinct destinations are the expected configuration; there is no last-source-wins guarantee for collisions in the proposed behavior. The PR description documents that decision, so we are not adding collision serialization or validation here.

@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 b34cf2a. Configure here.

Comment thread verifiers/v1/harness.py
raise result
if executables:
# `write` moves bytes, not modes; restore the execute bits scripts need.
await runtime.run(["chmod", "+x", *executables], {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Skill uploads lose archive batching

High Severity

install_skills now issues one runtime.write per skill file through an unbounded asyncio.gather, instead of the ordered tar batches this change set out to use. That restores the per-file remote-call cost measured on Prime and can fan out well past the previous 32-file and 1 MiB caps, so large skills may overload the runtime or hang a cancelled write with no extract process to stop.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b34cf2a. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The final design intentionally uses ordinary concurrent uploads; retaining the archive batches and their caps is no longer a requirement. There is still one write per file, but overlapping transfers reduced the measured 32-file installation median from 6.099 s on main to 1.009 s. The increased memory and upload fan-out are accepted tradeoffs, documented in the PR description.

There is no extraction process in this path. Cancellation uses the existing runtime.write implementations. In the Prime probe, cancelling 128 uploads left no active client upload tasks; this does not establish Docker cancellation behavior or guarantee rollback of bytes already accepted by the service. The absence of an extraction process alone does not demonstrate a cancellation failure.

@xeophon
xeophon enabled auto-merge (squash) September 6, 2026 10:47
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