InflateRust
A long-horizon OpenReward environment: the agent builds a multi-format
(de)compression toolkit from scratch in Rust (/workspace) — a DEFLATE/gzip
codec (decode + encode) plus a bzip2 decoder. Grading is server-side:
the inflate-runner producer (in the sandbox) runs the built artifact and
emits its raw behaviour, and the env server grades that against answers held off
the agent-readable sandbox (only the conformance corpus INPUTS are baked into the
sandbox image; the expected outputs live on the server image). Published as
GeneralReasoning/InflateRust.
See DESIGN.md for the full design and build/PROTOCOL.md for the artifact
invocation + grading contract.
What the agent builds
One binary (inflate) with five modes on stdin→stdout:
- decode
inflate(raw DEFLATE, RFC 1951) /gunzip(gzip, RFC 1952) /bunzip2(bzip2 — a second algorithm: BWT⁻¹ + MTF + RLE + multi-table Huffman) - encode
deflate(raw DEFLATE) /gzip(gzip member)
No (de)compression libraries — implemented by hand (std-only, network-blocked).
Safe Rust only: every run_tests does a controlled clean build with
-F unsafe_code, and rejects build scripts, proc-macros, native #[link]
libraries, .cargo/ config, and binaries that can launch other programs
(std::process). The sandbox image ships no Python or codec CLIs.
Decoders must be strict (consume the whole input; reject trailing garbage)
and graceful (reject corrupt input with a non-zero exit, never crash).
Grading (463 cases)
- Decode — byte-exact: stdout compared to the reference output (stored
hash-only, so no answer leaks). The
negative+ largefuzztiers (hundreds of malformed streams) must be rejected cleanly — a crash/panic FAILS. - Encode — round-trip + ratio: the harness decompresses the artifact's output
with reference zlib, checks it equals the original and that its size is
within a per-case threshold. Ratio-dominated tiers (trivial output earns
little):
valid/shrink(token floor) →near(≤1.1× zlib-6) →zlib(≤ zlib-6) →z9(≤ zlib-9) →zopfli(≤ optimal — the unbounded ceiling). - Multi-MB inputs under a ~10s/case timeout force an efficient implementation.
Reward
run_tests always grades the full corpus. Per-build delta: +1 per case
the current build passes that the previous build did not, −1 per regression. It
telescopes to the credited pass count of the build the agent finishes with. paths is a
display filter only (it never changes the score). finished=True only when a
single build passes every case at once.
Fresh-case cap. Each build also runs 12 newly generated, never-reused inputs
per group (stored/fixed/dynamic/gzip/bzip2 decode, deflate/gzip encode). A tier is
credited at most ⌈(fresh pass rate + 12.5%) × tier size⌉, so a lookup table of
the public inputs earns almost nothing. A real decoder isn't capped. A near-zlib
encoder's ratio tiers can move by about one case between builds (the reference: 430 in
15 of 16 identical builds, 429 once). Every case's result is written to
/tmp/run_tests/last.tsv in the sandbox.
Reject credit. exit 1 passes all 188 fuzz/negative cases, so they are credited
per decode mode (inflate, gunzip, bunzip2) in proportion to that mode's credited
valid-stream pass rate: credited_reject_M = min(reject_passed_M, ceil(valid_rate_M × reject_total_M)). An always-reject binary scores 0, a decoder that fully handles a mode
is credited all of that mode's reject cases, and a build that passes everything is
credited everything. The valid rate is taken after the fresh-case cap, so baked valid
passes don't unlock reject credit.
Layout
| File | Role |
|---|---|
inflaterust.py | the InflateRust(Environment) class, tools, reward, anti-cheat |
server.py | Server([InflateRust]).run() |
Dockerfile | lightweight env-server image (built by OpenReward on repo push) |
sandbox.Dockerfile | sandbox image (toolchain + corpus + harness; no Python/codec tools); Docker Hub generalreasoning/inflaterust, pinned by tag |
harness/ | the inflate-runner grading binary (Rust; flate2 round-trips encode output; detects crashes so fuzz requires clean rejection) |
build/filter_corpus.py | self-generates the deterministic corpus + thresholds (seeded; zlib/gzip/bz2 + zopflipy oracles; no clone/network) |
build/PROTOCOL.md | the artifact invocation + grading contract |
golden_tests.py | no-Docker invariant tests (telescoping, full-corpus scoring, anti-cheat) |
test_agent.py | end-to-end smoke test against a running server |
reference_solution/ | known-good toolkit (inflate + bzip2 decoder + LZ77/Huffman compressor; catch_unwind for fuzz-safety) |
Develop / test
# No-Docker invariant tests:
uv run python -m pytest golden_tests.py -v
Deploy. The env-server rebuilds when this repo is pushed (OpenReward CI).
The sandbox image is built + pushed to GHCR (:latest + immutable
:sha-<gitsha>) by .github/workflows/build-sandbox.yml on a path-filtered push
to main (touching sandbox.Dockerfile/harness//build/). SANDBOX_IMAGE in
inflaterust.py is pinned to the :sha-<gitsha> tag — NOT :latest — because
OpenReward's GHCR pull-through mirror caches :latest stale; bump the pin when the
corpus/harness change.
Validation
Reference toolkit graded server-side (producer in the sandbox, grade_case
on the host), full 463 corpus: 437/463 — all decode tiers 100% (incl. bzip2
59/59, gzip 59/59), fuzz 179/179 (clean rejection); the misses are the top
encode-ratio tiers (zlib/z9/zopfli), the intended unbounded ceiling
(matching/beating tuned/optimal zlib). An empty stub scores ~0. (Identical to the
prior in-sandbox grade — the producer/grade split preserves grading exactly.)
History: v1 (decode-only) was solved 236/236 by a weak model; v2 (added encoder)
held kimi to 228/239, stalling at the ratio ceiling; v3 (this version) adds
the bzip2 decoder, the fuzz-robustness wall, the zopfli ceiling, and
ratio-dominated encode — a genuinely harder multi-algorithm marathon. See
reference_solution/README.md for details.