Skip to content

Define evaluator-sandbox image contract and docker:build - #93

Merged
igmarin merged 2 commits into
mainfrom
feat/issue-87-image-contract
Aug 8, 2026
Merged

Define evaluator-sandbox image contract and docker:build#93
igmarin merged 2 commits into
mainfrom
feat/issue-87-image-contract

Conversation

@igmarin

@igmarin igmarin commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Document MVP evaluator-sandbox image contract in docs/docker.md
  • Align Dockerfile comments with --network none runtime model
  • Add rake docker:build tagging evaluator-sandbox:<VERSION> and :latest
  • Unit tests for contract docs / tag format / packaging

Closes #87

Base: stacks on #92 (packaging).
Milestone: container-isolation-v1

Stack

  1. BE | Package Docker build context in gem (#86) #92 packaging (BE | Package Docker build context in gem #86)
  2. This PR image contract (BE | Define evaluator-sandbox image contract + build #87)
  3. BE | Activate container lifecycle with safe fallbacks #88 activation (next)
  4. BE | Live Docker integration tests (opt-in) #89 live tests
  5. BE | CI job for container integration #90 CI
  6. BE | Docs + CHANGELOG for shipped container isolation #91 docs flip

Test plan

  • bundle exec ruby -Itest test/evaluator/docker_image_contract_test.rb
  • packaging tests still green on parent
  • CI green
  • Optional: bundle exec rake docker:build when Docker daemon available

Summary by CodeRabbit

  • New Features

    • Added a streamlined task for building the evaluator sandbox Docker image.
    • Images are tagged with both the project version and latest.
    • Documented Docker image configuration, runtime assumptions, isolation behavior, and multi-architecture considerations.
  • Documentation

    • Clarified how the long-running sandbox supports repeated command execution.
    • Documented supported Ruby and Git requirements and current scope limitations.
  • Tests

    • Added coverage validating Docker configuration, version tags, documentation, and package inclusion.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR defines the evaluator sandbox image contract, documents its runtime assumptions, adds a docker:build task with versioned and latest tags, and adds tests for Dockerfile content, documentation, image naming, and gem packaging.

Changes

Evaluator sandbox image

Layer / File(s) Summary
Image contract and runtime model
lib/skill_bench/execution/docker/Dockerfile, docs/docker.md
The Dockerfile comments and documentation define the image contents, runtime settings, disabled network, long-lived process model, and architecture guidance.
Versioned build and contract validation
Rakefile, test/evaluator/docker_image_contract_test.rb
The docker:build task validates the Docker context and Dockerfile, then applies versioned and latest tags. Tests verify the Dockerfile, documentation, image naming, and gem packaging.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • Issue 83 — The PR adds Docker context packaging coverage and tests for the Dockerfile and documentation.

Possibly related PRs

  • igmarin/ruby-skill-bench#92 — This PR builds on the same Docker context and Dockerfile by adding the build task, documentation, and contract tests.

Poem

A rabbit checked the Docker file,
Then tagged two images in style.
Versioned and latest, side by side,
With quiet network locked inside.
“Hop hop,” the sandbox builds today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR addresses issue #87 by documenting the image contract, build tags, network model, runtime process, local build path, packaging, and multi-architecture notes.
Out of Scope Changes check ✅ Passed All changes support the evaluator-sandbox image contract, reproducible Docker builds, documentation, packaging, or related contract tests.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: defining the evaluator-sandbox image contract and adding the docker:build task.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@igmarin igmarin added enhancement New feature or request security Security hardening labels Aug 8, 2026
@igmarin
igmarin force-pushed the feat/issue-87-image-contract branch from 0908e55 to 996bbce Compare August 8, 2026 04:58
Base automatically changed from feat/issue-86-package-docker-context to main August 8, 2026 15:26
igmarin added 2 commits August 8, 2026 09:26
Document the MVP image contract, align the Dockerfile comments with
runtime network-none isolation, add rake docker:build to tag
evaluator-sandbox with the gem version and latest, and cover the
contract with unit tests (Closes #87).
CI failed on Layout/TrailingEmptyLines for the docker:build task block.
@igmarin
igmarin force-pushed the feat/issue-87-image-contract branch from 996bbce to 5e088c8 Compare August 8, 2026 15:26
@igmarin
igmarin marked this pull request as ready for review August 8, 2026 15:26
@igmarin
igmarin merged commit 6b913f7 into main Aug 8, 2026
4 of 5 checks passed
@igmarin
igmarin deleted the feat/issue-87-image-contract branch August 8, 2026 15:27

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/docker.md`:
- Around line 23-27: Update the Docker build instruction near the
repository-root command to apply only to a repository checkout, or ensure the
package includes the Rakefile and its required lib files before claiming
installed packages can run it. Do not imply that any install containing the
packaged context supports bundle exec rake docker:build.
- Line 14: Update the Docker documentation’s “Preinstalled” row to explicitly
list the required supported shell, preferably /bin/sh, instead of relying on the
vague “minimal Debian tools” wording. Add or update the corresponding contract
assertion to verify that this exact shell is available.
- Line 13: Update the Base entry in the Docker documentation to reference the
approved immutable digest for the desired ruby:3.4-bookworm image instead of the
mutable tag, preserving the documented Ruby version and architecture alignment.

In `@lib/skill_bench/execution/docker/Dockerfile`:
- Line 5: Correct the misspelled capability name in the Dockerfile contract
comment from CHOUN to CHOWN, matching Sandbox#start_container and docs/docker.md
without changing the documented flags.

In `@test/evaluator/docker_image_contract_test.rb`:
- Around line 27-32: Update test_version_tag_format_matches_gem_version to
exercise the docker:build task rather than constructing the expected tag from
SkillBench constants. Stub the Docker command, invoke docker:build with its real
arguments, and assert that both the versioned and latest tags are generated
correctly.
- Around line 34-40: Update test_gemspec_packages_docker_docs_optional to
reflect the complete Docker context, renaming it accordingly and asserting that
spec.files includes Dockerfile, .dockerignore, and docs/docker.md. Keep the
existing gemspec loading setup unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a4adcc87-e4c5-4660-beb3-dc50a0648978

📥 Commits

Reviewing files that changed from the base of the PR and between dfb8f76 and 5e088c8.

📒 Files selected for processing (4)
  • Rakefile
  • docs/docker.md
  • lib/skill_bench/execution/docker/Dockerfile
  • test/evaluator/docker_image_contract_test.rb

Comment thread docs/docker.md
|------|--------|
| Image name | `evaluator-sandbox` (`Constants::Sandbox::DOCKER_IMAGE_NAME`) |
| Tags | `evaluator-sandbox:<SkillBench::VERSION>` and `evaluator-sandbox:latest` |
| Base | `ruby:3.4-bookworm` (aligns with CI Ruby 3.4; 3.3 hosts still run evals via host Ruby for the orchestrator) |

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

docker buildx imagetools inspect ruby:3.4-bookworm

Repository: igmarin/ruby-skill-bench

Length of output: 206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked files matching Docker/docs\n'
git ls-files | rg '(^|/)(Dockerfile|docker\.md|\.dockerignore$|compose)' || true

printf '\ndocs/docker.md context\n'
if [ -f docs/docker.md ]; then
  nl -ba docs/docker.md | sed -n '1,80p'
fi

printf '\nDockerfiles\n'
fd -a 'Dockerfile$' . | while read -r f; do
  echo "--- $f"
  nl -ba "$f" | sed -n '1,120p'
done

Repository: igmarin/ruby-skill-bench

Length of output: 367


🌐 Web query:

Docker Hub ruby 3.4-bookworm image multi-architecture digest FROM ruby:3.4-bookworm mutable tag documentation reproducibility

💡 Result:

The ruby:3.4-bookworm tag is a mutable reference, meaning it can point to different container images over time as the base image is rebuilt (e.g., for security patches) [1][2][3]. Because it is mutable, it does not guarantee reproducibility [1][4][3]. To achieve reproducibility, you must reference the image by its immutable manifest digest (e.g., ruby:3.4-bookworm@sha256:e46ac468c9e87ff9209709c5b73ac689688ee82839e2a18af33d14d3577d1340) [1][5][3]. Key points regarding your query: 1. Multi-Architecture Digests: The ruby:3.4-bookworm tag points to a manifest list (index), which contains platform-specific digests for architectures like amd64, arm64v8, and others [6][7][8]. While the tag is mutable, the specific manifest digest (e.g., sha256:e46ac468...) uniquely identifies the content [9][10][5]. You can view the current digest for this tag on Docker Hub via the image's "Tag" or "Layers" view [9][10][6]. 2. Tag Mutability: Official image tags like 3.4-bookworm are intended to provide convenience by pointing to the latest version of that specific Ruby/OS combination [7][1]. However, this implies they will drift [1][2]. Reliance on these tags in production can lead to environment drift where different builds or nodes inadvertently use different versions of the base image [5][3]. 3. Reproducibility Best Practices: - Always pin to a digest (repository@sha256:...) in production deployment manifests and build definitions to ensure identical image usage across environments [1][5][3]. - If using tags is required for ergonomics, acknowledge the mutability risk and consider enforcing tag immutability policies in your private registry if you re-tag or mirror images [2][5]. - For complete reproducibility, combine base image digest pinning with locked application dependencies (e.g., lock files) and controlled system package versions [3]. Current data confirms that the ruby:3.4-bookworm tag is regularly updated by the maintainers [9][10], reinforcing the necessity of using digests for stable, reproducible deployments [5][3].

Citations:


Pin the Docker base image to a digest.

ruby:3.4-bookworm is a mutable tag, so evaluator-sandbox:<SkillBench::VERSION> can be rebuilt from a different base image over time. Use an approved immutable registry digest that resolves to the desired architecture and update it through a deliberate process.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docker.md` at line 13, Update the Base entry in the Docker documentation
to reference the approved immutable digest for the desired ruby:3.4-bookworm
image instead of the mutable tag, preserving the documented Ruby version and
architecture alignment.

Comment thread docs/docker.md
| Image name | `evaluator-sandbox` (`Constants::Sandbox::DOCKER_IMAGE_NAME`) |
| Tags | `evaluator-sandbox:<SkillBench::VERSION>` and `evaluator-sandbox:latest` |
| Base | `ruby:3.4-bookworm` (aligns with CI Ruby 3.4; 3.3 hosts still run evals via host Ruby for the orchestrator) |
| Preinstalled | Ruby (from base), `git`, minimal Debian tools from base image |

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Name the required shell explicitly.

The MVP contract requires a shell, but this row only says “minimal Debian tools from base image.” Name /bin/sh or the exact supported shell. Add a matching contract assertion.

Proposed documentation update
-| Preinstalled | Ruby (from base), `git`, minimal Debian tools from base image |
+| Preinstalled | Ruby (from base), `/bin/sh`, `git`, and minimal Debian tools from base image |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docker.md` at line 14, Update the Docker documentation’s “Preinstalled”
row to explicitly list the required supported shell, preferably /bin/sh, instead
of relying on the vague “minimal Debian tools” wording. Add or update the
corresponding contract assertion to verify that this exact shell is available.

Comment thread docs/docker.md
Comment on lines +23 to +27
From the repo root (or any install that includes the packaged context):

```bash
bundle exec rake docker:build
```

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restrict this build instruction to a repository checkout, or package the task.

Rakefile Lines 69-75 load lib/... with require_relative. The supplied gemspec file list includes the Docker context and documentation, but not Rakefile. An installed package cannot run bundle exec rake docker:build from this paragraph as written.

Proposed documentation update
-From the repo root (or any install that includes the packaged context):
+From a repository checkout:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
From the repo root (or any install that includes the packaged context):
```bash
bundle exec rake docker:build
```
From a repository checkout:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docker.md` around lines 23 - 27, Update the Docker build instruction
near the repository-root command to apply only to a repository checkout, or
ensure the package includes the Rakefile and its required lib files before
claiming installed packages can run it. Do not imply that any install containing
the packaged context supports bundle exec rake docker:build.

#
# Purpose: long-lived container for `docker exec` of allowlisted agent commands.
# Runtime flags (from Sandbox#start_container): --network none, non-root uid/gid,
# --security-opt no-new-privileges, --cap-drop ALL (+ CHOUN/DAC_OVERRIDE).

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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Correct the capability name.

The comment says CHOUN. Sandbox#start_container and docs/docker.md use CHOWN. Keep the Dockerfile contract comment aligned with the flags that docker run uses.

Proposed fix
-#   --security-opt no-new-privileges, --cap-drop ALL (+ CHOUN/DAC_OVERRIDE).
+#   --security-opt no-new-privileges, --cap-drop ALL (+ CHOWN/DAC_OVERRIDE).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# --security-opt no-new-privileges, --cap-drop ALL (+ CHOUN/DAC_OVERRIDE).
# --security-opt no-new-privileges, --cap-drop ALL (+ CHOWN/DAC_OVERRIDE).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/skill_bench/execution/docker/Dockerfile` at line 5, Correct the
misspelled capability name in the Dockerfile contract comment from CHOUN to
CHOWN, matching Sandbox#start_container and docs/docker.md without changing the
documented flags.

Comment on lines +27 to +32
def test_version_tag_format_matches_gem_version
image = SkillBench::Constants::Sandbox::DOCKER_IMAGE_NAME
versioned = "#{image}:#{SkillBench::VERSION}"

assert_equal "evaluator-sandbox:#{SkillBench::VERSION}", versioned
end

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Exercise the build task in the tag test.

The test constructs versioned from the same constants that it asserts. It does not detect a missing version tag, a missing latest tag, or incorrect Rakefile arguments. Stub the Docker command while invoking docker:build, or extract and test a pure tag-building helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/evaluator/docker_image_contract_test.rb` around lines 27 - 32, Update
test_version_tag_format_matches_gem_version to exercise the docker:build task
rather than constructing the expected tag from SkillBench constants. Stub the
Docker command, invoke docker:build with its real arguments, and assert that
both the versioned and latest tags are generated correctly.

Comment on lines +34 to +40
def test_gemspec_packages_docker_docs_optional
# Dockerfile packaging is required (#86); docs/docker.md is repo docs.
gemspec_path = File.expand_path('../../ruby-skill-bench.gemspec', __dir__)
spec = Gem::Specification.load(gemspec_path)

assert_includes spec.files, 'docs/docker.md'
end

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the complete packaged Docker context.

This test checks only docs/docker.md. The gemspec also packages Dockerfile and .dockerignore, but the test does not protect those files. Rename the test and assert all required context files.

Proposed test update
-  def test_gemspec_packages_docker_docs_optional
-    # Dockerfile packaging is required (`#86`); docs/docker.md is repo docs.
+  def test_gemspec_packages_docker_context_and_docs
+    # The Docker context and documentation are required by the package contract.
     gemspec_path = File.expand_path('../../ruby-skill-bench.gemspec', __dir__)
     spec = Gem::Specification.load(gemspec_path)

+    assert_includes spec.files, 'lib/skill_bench/execution/docker/Dockerfile'
+    assert_includes spec.files, 'lib/skill_bench/execution/docker/.dockerignore'
     assert_includes spec.files, 'docs/docker.md'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_gemspec_packages_docker_docs_optional
# Dockerfile packaging is required (#86); docs/docker.md is repo docs.
gemspec_path = File.expand_path('../../ruby-skill-bench.gemspec', __dir__)
spec = Gem::Specification.load(gemspec_path)
assert_includes spec.files, 'docs/docker.md'
end
def test_gemspec_packages_docker_context_and_docs
# The Docker context and documentation are required by the package contract.
gemspec_path = File.expand_path('../../ruby-skill-bench.gemspec', __dir__)
spec = Gem::Specification.load(gemspec_path)
assert_includes spec.files, 'lib/skill_bench/execution/docker/Dockerfile'
assert_includes spec.files, 'lib/skill_bench/execution/docker/.dockerignore'
assert_includes spec.files, 'docs/docker.md'
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/evaluator/docker_image_contract_test.rb` around lines 34 - 40, Update
test_gemspec_packages_docker_docs_optional to reflect the complete Docker
context, renaming it accordingly and asserting that spec.files includes
Dockerfile, .dockerignore, and docs/docker.md. Keep the existing gemspec loading
setup unchanged.

igmarin added a commit that referenced this pull request Aug 8, 2026
Summary
Prefer Docker isolation when context + daemon are available
ensure_image builds only when versioned tag is missing (skip rebuild)
Use evaluator-sandbox:<VERSION> for run; tag :latest on build
container_id stays nil when Docker is unavailable (fail-closed host unchanged)
Unit tests cover availability, skip-build, security flags, and run paths
Closes #88

Base: #93 (image contract).
Milestone: container-isolation-v1

Stack
BE | Package Docker build context in gem (#86) #92 packaging (BE | Package Docker build context in gem #86)
Define evaluator-sandbox image contract and docker:build #93 image contract (BE | Define evaluator-sandbox image contract + build #87)
This PR activation (BE | Activate container lifecycle with safe fallbacks #88)
BE | Live Docker integration tests (opt-in) #89 live tests (next)
BE | CI job for container integration #90 CI
BE | Docs + CHANGELOG for shipped container isolation #91 docs
Pre-push
Lifecycle unit tests green (9)
Related sandbox / run_command / packaging tests green
RuboCop + YARD coverage clean on touched files
rs-guard: deepseek call hung; self-review: security flags preserved, fail-closed host, no allow_host default change
Test plan
 lifecycle unit tests
 sandbox + packaging + run_command
 CI green
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request security Security hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BE | Define evaluator-sandbox image contract + build

1 participant