Skip to content

Wave 5: runtime-docker - #7

Merged
ThePlenkov merged 13 commits into
wave-4-runtime-hostfrom
wave-5-runtime-docker
Aug 11, 2026
Merged

ThePlenkov merged 13 commits into
wave-4-runtime-hostfrom
wave-5-runtime-docker

Conversation

@ThePlenkov

@ThePlenkov ThePlenkov commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

User description

Summary

  • DockerExecutor implementation: container lifecycle, image pulling, volume mounts, env/credential passing, artifact collection, cache management, timeout handling
  • Mockable docker-cli.ts seam for unit tests without Docker daemon
  • Integration tests skip if no Docker daemon (SVERKA_DOCKER guard)
  • 58 tests pass + 2 skipped (integration)
  • Spec 04 amended to match built runtime contract
  • Reviewer APPROVED after rework (sv-b3u): collectArtifacts + mount-socket throw test added

Test plan

  • bun run test (runtime-docker: 58 pass + 2 skip, full monorepo 16 projects green)
  • bun run typecheck (clean)
  • bun run build (green, dist index.mjs 11.40kB + index.d.mts 4.89kB)
  • bun run lint (clean)
  • reviewer approved (sv-1i2, after rework sv-b3u)

Stacked on #5

Generated with Devin


Summary by cubic

Adds DockerExecutor to @sverka/runtime-docker for secure, digest-verified container runs, and switches @sverka/core and @sverka/ir to deterministic SHA-256 op- IDs. Also hardens @sverka/runtime-host to treat process spawn failures as runtime errors.

  • Bug Fixes
    • Image verification uses RepoDigests, pulls if missing, applies timeouts, and raises clear ImageDigestError.
    • Artifact collection validates paths under request.artifactDir, rejects absolute destinations, uses workspace-relative sources, and blocks traversal with specific ContainerPolicyError codes.
    • Cache manager prevents path traversal, preserves input directory structure using the workspace root, skips missing outputs on collect, fixes ownership handling, and mounts under /cache.
    • Policy checks canonicalize paths/env for Docker socket detection; default non-root runAs is "1000:1000"; tightened secret denylist to avoid PUBLIC_KEY false positives and added specific codes for blocked socket and undeclared secrets.
    • Log truncation preserves a visible notice and caps output while streaming via the Docker CLI seam.
    • Planner preserves __matrixCombo, throws on unknown dependency IDs, suppresses finalize rejections, and unifies skipped outcomes; @sverka/ir re-exports computeOperationId, validates operation kinds, recomputes plan IDs ignoring identity fields, and accepts extra top-level fields in ID calculation.
    • Host executor treats spawn failures as runtime errors and avoids double-resolving on process error.

Written for commit c0e424f. Summary will update on new commits.

Review in cubic


CodeAnt-AI Description

Add secure Docker execution and content-addressed operation IDs

What Changed

  • Adds Docker execution with digest-pinned images, read-only containers, dropped capabilities, configurable CPU and memory limits, network controls, required timeouts, logs, and artifact collection
  • Blocks Docker socket access and undeclared secret-like environment variables
  • Adds persistent cache preparation and output collection, plus clear errors for policy violations and image digest mismatches
  • Replaces readable operation IDs with deterministic op- IDs based on operation content, including matrix values and commands; dependency references and duplicate detection use these IDs
  • Exposes shared canonical serialization and operation ID helpers, with tests covering matrix uniqueness, dependency ordering, serialization rules, and Docker behavior

Impact

✅ Sandboxed Docker execution
✅ Reproducible operation and matrix IDs
✅ Blocked Docker socket and undeclared secret access

🔄 Retrigger CodeAnt AI Review

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai

codeant-ai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed aa1a4ae Aug 11, 2026 · 10:54 10:54
✅ Incremental review completed 0e6f15f Aug 11, 2026 · 08:32 08:33
✅ Incremental review completed 3b9d973 Aug 11, 2026 · 06:24 06:24
✅ Incremental review completed dc7ff99 Aug 10, 2026 · 23:47 23:47
✅ Incremental review completed ecf9429 Aug 10, 2026 · 20:19 20:19

@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added Docker-based execution with security policies, timeouts, logging, artifact collection, and image digest verification.
    • Added persistent caching for execution inputs and outputs.
    • Added structured Docker errors and configuration options.
    • Exported the Docker runtime APIs and content-addressed operation ID utility.
  • Improvements
    • Operation IDs are now deterministic, content-addressed, and consistently generated across packages.
    • Matrix workflows support expanded combinations without the previous limit.
  • Documentation
    • Added comprehensive Docker runtime implementation and configuration documentation.
  • Tests
    • Expanded coverage for workflows, IDs, Docker execution, caching, security, artifacts, and integration scenarios.

Walkthrough

This PR adds content-addressed operation IDs shared by Core and IR, updates planning and DAG tests, and implements the Docker runtime package with execution policies, digest verification, caching, artifact handling, error types, CLI timeouts, exports, and tests.

Changes

Core planning and Docker runtime

Layer / File(s) Summary
Runtime Docker contract and implementation plan
engdocs/architecture/wave-05-runtime-docker-plan.md, specs/04-runtime-docker/spec.md
Defines Docker execution policies, request-scoped mounts, credential handling, caching, testing, and acceptance requirements.
Content-addressed operation planning
packages/core/src/internal/ids.ts, packages/core/src/internal/plan.ts, packages/core/src/internal/canonical.ts, packages/core/src/index.ts, packages/ir/src/ids.ts, packages/ir/src/internal/canonical.ts, packages/core/src/__tests__/*, packages/ir/src/__tests__/*
Operation IDs now use canonical JSON and SHA-256 content addressing. Planning resolves aliases, dependencies, and matrix context with the new IDs.
Docker process and image foundation
packages/runtime-docker/src/config.ts, packages/runtime-docker/src/errors.ts, packages/runtime-docker/src/internal/docker-cli.ts, packages/runtime-docker/src/image.ts, packages/runtime-docker/src/__tests__/errors.test.ts, packages/runtime-docker/src/__tests__/image.test.ts
Adds Docker configuration, error types, timeout-aware CLI execution, and image digest verification with pull-and-recheck behavior.
Docker execution, caching, and package surface
packages/runtime-docker/src/docker-executor.ts, packages/runtime-docker/src/cache.ts, packages/runtime-docker/src/index.ts, packages/runtime-docker/src/__tests__/*, packages/runtime-docker/project.json
Adds policy-compliant container execution, cache preparation and collection, artifact handling, public exports, integration coverage, and the updated lint command.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DockerExecutor
  participant DockerCacheManager
  participant verifyImageDigest
  participant runDocker
  participant DockerCLI
  DockerExecutor->>DockerCacheManager: prepare cache inputs
  DockerExecutor->>verifyImageDigest: verify expected image digest
  verifyImageDigest->>runDocker: inspect or pull image
  DockerExecutor->>runDocker: execute container command
  runDocker->>DockerCLI: spawn Docker process
  DockerCLI-->>runDocker: return logs and exit status
  DockerExecutor->>DockerCacheManager: collect execution outputs
Loading

Possibly related PRs

  • sverka-dev/sverka#2: Provides the content-addressed operation ID and canonical serialization foundations reused here.
  • sverka-dev/sverka#3: Defines the runtime executor, request, and result contracts implemented by DockerExecutor.
  • sverka-dev/sverka#5: Implements a related runtime executor against the same execution contracts.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.77% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title identifies the main runtime-docker addition and is concise, although it omits the related operation ID changes.
Description check ✅ Passed The description clearly covers the Docker executor, operation ID changes, security policies, tests, and validation results.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wave-5-runtime-docker

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

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 9, 2026
@baz-reviewer

baz-reviewer Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Merger

⚠️ Re-evaluating...

Commit c0e424f · Updated 2026-08-11 15:30 UTC

Review this PR on Baz | Customize your next review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Running ultrareview automatically — Because this PR lands the security-sensitive Docker executor (isolation flags, credential allowlisting, image digest pinning, mount policy, cache/artifact paths), where a subtle bug could leak secrets, escape the sandbox, or break every run, a deeper review is warranted.. I'll post findings when complete.

@codacy-production

codacy-production Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 155 complexity · 6 duplication

Metric Results
Complexity 155
Duplication 6

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add runtime-docker package with DockerExecutor, policies, and testable docker CLI seam

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Implement DockerExecutor for running pinned images with strict container security policy.
• Add mockable docker CLI seam plus unit/integration coverage with daemon-guarded tests.
• Update spec/plan docs to match runtime contract and build/test commands.
Diagram

graph TD
  SVC["Scheduler / Runtime"] --> EXE["DockerExecutor"] --> CLI["docker-cli seam"] --> DKR{{"Docker daemon"}}
  EXE --> IMG["verifyImageDigest"] --> CLI
  EXE --> FS[("Workspace/Artifacts FS")]
  EXE --> CCH["DockerCacheManager"] --> FS

  subgraph Legend
    direction LR
    _svc(["Service"]) ~~~ _mod["Module"] ~~~ _db[("Filesystem")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Docker Engine API (dockerode) instead of docker CLI
  • ➕ Avoids CLI parsing and shelling out; structured errors/streams
  • ➕ Potentially better performance and richer lifecycle controls
  • ➖ Adds a heavier dependency and API/daemon compatibility surface
  • ➖ Harder to mock cleanly without standing up API fakes
  • ➖ May require additional auth/connection handling across environments
2. Reuse runtime-host spawn utilities directly (shared process runner)
  • ➕ Reduces duplicated timeout/log-capture logic across executors
  • ➕ Centralizes process execution hardening in one place
  • ➖ Tighter coupling between runtime-host and runtime-docker packages
  • ➖ Still needs a test seam boundary; shared code may become a layering issue

Recommendation: Current approach (docker CLI wrapper as the single seam + pure arg/env builders) is a good fit for portability and unit-testability. Consider extracting shared spawn/timeout/log-truncation helpers later if runtime-host and runtime-docker diverge or bugs appear in both.

Files changed (17) +1720 / -18

Enhancement (7) +536 / -0
cache.tsImplement filesystem-backed DockerCacheManager +49/-0

Implement filesystem-backed DockerCacheManager

• Introduces a CacheManager interface and DockerCacheManager implementation to prepare cache directories keyed by operation and to collect output files back into a persistent cacheDir.

packages/runtime-docker/src/cache.ts

config.tsDefine DockerExecutorConfig +18/-0

Define DockerExecutorConfig

• Adds the executor-wide configuration interface including dockerPath/dockerHost overrides, runAs, cacheDir, and maxLogBytes; clarifies workspace and artifactDir are per-request.

packages/runtime-docker/src/config.ts

docker-executor.tsImplement DockerExecutor with policy enforcement, logs, and artifacts +293/-0

Implement DockerExecutor with policy enforcement, logs, and artifacts

• Implements the Executor contract: validates docker operations, enforces mandatory timeout and digest presence, builds docker run args/env with network and secret/socket policies, executes via runDocker, truncates logs, and collects declared artifacts without altering run status on artifact errors.

packages/runtime-docker/src/docker-executor.ts

errors.tsAdd runtime-docker error hierarchy +31/-0

Add runtime-docker error hierarchy

• Defines DockerExecutorError and two specializations: ImageDigestError for digest mismatches and ContainerPolicyError for policy violations, each with stable codes and optional context.

packages/runtime-docker/src/errors.ts

image.tsAdd verifyImageDigest helper +46/-0

Add verifyImageDigest helper

• Implements digest verification by inspecting local image ID, pulling when missing, and throwing ImageDigestError with expected/actual digest context on mismatch.

packages/runtime-docker/src/image.ts

index.tsExpose runtime-docker public API surface +7/-0

Expose runtime-docker public API surface

• Exports the executor, config type, digest verification helper, cache manager interface/implementation, and error types from the package entrypoint.

packages/runtime-docker/src/index.ts

docker-cli.tsAdd mockable docker CLI runner with timeout enforcement +92/-0

Add mockable docker CLI runner with timeout enforcement

• Introduces the single side-effect seam for spawning the docker CLI, capturing stdout/stderr, returning exit codes, applying a timeout with SIGTERM/SIGKILL grace, and supporting DOCKER_HOST/dockerPath overrides.

packages/runtime-docker/src/internal/docker-cli.ts

Tests (7) +845 / -0
cache.test.tsAdd DockerCacheManager unit tests +79/-0

Add DockerCacheManager unit tests

• Adds coverage for cache directory creation, copying declared inputs, collecting outputs back to the persistent cacheDir, and restore-from-cache behavior when sources are missing.

packages/runtime-docker/src/tests/cache.test.ts

docker-executor.test.tsAdd comprehensive DockerExecutor unit tests with mocked docker CLI +455/-0

Add comprehensive DockerExecutor unit tests with mocked docker CLI

• Validates canExecute, docker run argument construction (policy, mounts, network), timeout handling, digest presence checks, secret allowlist behavior, log capture/truncation, artifact collection, and edge cases. Uses a mocked 'runDocker' seam to avoid requiring a Docker daemon.

packages/runtime-docker/src/tests/docker-executor.test.ts

errors.test.tsAdd error hierarchy tests for runtime-docker +57/-0

Add error hierarchy tests for runtime-docker

• Verifies error base fields (name/code/context) and subclass behavior for ImageDigestError and ContainerPolicyError including instanceof relationships.

packages/runtime-docker/src/tests/errors.test.ts

fixtures.tsAdd test fixtures for PlanOperation and ExecuteRequest +64/-0

Add test fixtures for PlanOperation and ExecuteRequest

• Provides helpers to build minimal docker operations/requests and a default DockerExecutorConfig for consistent unit test setup.

packages/runtime-docker/src/tests/helpers/fixtures.ts

image.test.tsAdd verifyImageDigest tests using mocked docker CLI +97/-0

Add verifyImageDigest tests using mocked docker CLI

• Covers digest match, mismatch error context, pull-on-missing behavior, and mismatch after pull, ensuring verifyImageDigest interacts with docker inspect/pull as expected.

packages/runtime-docker/src/tests/image.test.ts

integration.test.tsAdd daemon-guarded DockerExecutor integration tests +42/-0

Add daemon-guarded DockerExecutor integration tests

• Adds opt-in integration tests (guarded by SVERKA_DOCKER) that run simple commands in busybox and validate success/failure outcomes using a real Docker daemon.

packages/runtime-docker/src/tests/integration.test.ts

public-api.test.tsAdd public API export tests +51/-0

Add public API export tests

• Ensures the package exports the executor, cache manager, digest verifier, and error classes; includes compile-time checks for exported types.

packages/runtime-docker/src/tests/public-api.test.ts

Documentation (2) +338 / -17
wave-05-runtime-docker-plan.mdAdd Wave 5 implementation plan for runtime-docker +321/-0

Add Wave 5 implementation plan for runtime-docker

• Introduces a detailed build-and-test plan for the Docker executor, including spec amendments, file layout, TDD sequencing, and edge-case policy rules (secrets allowlist, socket denial, timeouts, artifacts).

engdocs/architecture/wave-05-runtime-docker-plan.md

spec.mdAmend runtime-docker spec to match built runtime request contract +17/-17

Amend runtime-docker spec to match built runtime request contract

• Updates DockerExecutorConfig and command examples to use request.workspace/request.artifactDir, clarifies credentials/value sourcing via request.*, and corrects test commands to 'bun run test'.

specs/04-runtime-docker/spec.md

Other (1) +1 / -1
project.jsonRelax runtime-docker lint command invocation +1/-1

Relax runtime-docker lint command invocation

• Updates the Nx lint command to run eslint against 'src' without specifying extensions, aligning with the repo’s eslint configuration expectations.

packages/runtime-docker/project.json

@amazon-q-developer amazon-q-developer 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.

Summary

This PR implements a Docker runtime executor with strong security policies. However, 6 critical defects must be fixed before merge:

Critical Issues (Must Fix)

  1. Docker socket bypass vulnerabilities (2 locations): Simple substring checks can be evaded via path traversals or symlinks, allowing container escape
  2. Secret regex false positives: Incorrectly blocks legitimate variables like MY_KEY_VALUE
  3. Unhandled cache collection crash: Missing try-catch will terminate execution on file errors
  4. Cache path collision bug: Incorrect path calculation causes cache key conflicts
  5. Artifact path traversal vulnerability: Absolute paths allow arbitrary host file exfiltration

All issues have specific fixes provided. The test coverage is excellent (58 tests), but these logic errors and security vulnerabilities must be addressed before the code is production-ready.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/cache.ts Outdated
Comment thread packages/runtime-docker/src/cache.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/__tests__/helpers/fixtures.ts
Comment thread packages/runtime-docker/src/__tests__/integration.test.ts

@codacy-production codacy-production 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.

Pull Request Overview

The pull request implements the @sverka/runtime-docker package but is currently not up to standards due to a significant volume of quality issues and critical logic errors.

The most pressing issues involve the DockerExecutor implementation: it includes an unsupported --timeout flag that will cause all container executions to fail, and it misses a mandatory architectural step to call verifyImageDigest before execution. Furthermore, a security risk was identified in artifact collection where absolute paths could allow the exfiltration of host files.

Logic gaps in DockerCacheManager will cause file collisions and failures with directories, as the implementation flattens input structures and uses non-recursive copy operations.

About this PR

  • Artifact collection allows absolute paths, which permits the exfiltration of arbitrary host files readable by the executor process, potentially bypassing the intended workspace boundary. Artifact paths should be strictly validated as relative to the workspace.
  • The DockerCacheManager.prepare implementation flattens input files into the root of the cache directory, which will cause collisions for different files that share a basename (e.g., 'src/index.ts' and 'test/index.ts').

Test suggestions

  • Found recommended test scenario: DockerExecutor.canExecute identifies docker operations and rejects others
  • Found recommended test scenario: buildDockerArgs correctly constructs CLI flags for security policy and resource limits
  • Found recommended test scenario: Network policy correctly maps to Docker network modes
  • Missing recommended test scenario: Execution fails with ContainerPolicyError if timeout or image digest is missing
  • Found recommended test scenario: Environment variables are filtered by credential declarations and secret-like patterns are blocked
  • Found recommended test scenario: Docker socket references in mounts or env vars trigger a policy violation error
  • Found recommended test scenario: verifyImageDigest correctly handles inspect failures by pulling the image
  • Found recommended test scenario: Logs exceeding maxLogBytes are truncated and appended with a notice
  • Found recommended test scenario: Artifacts are copied from the workspace to the artifactDir after execution
  • Missing recommended test scenario: DockerCacheManager preserves output structure and correctly restores cached inputs
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Missing recommended test scenario: Execution fails with ContainerPolicyError if timeout or image digest is missing
2. Missing recommended test scenario: DockerCacheManager preserves output structure and correctly restores cached inputs

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/cache.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/__tests__/integration.test.ts Outdated
Comment thread packages/runtime-docker/src/cache.ts
Comment thread packages/runtime-docker/src/config.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/internal/docker-cli.ts
@qodo-code-review

qodo-code-review Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. verifyImageDigest never called before container run ✓ Resolved 🐞 Bug ⛨ Security
Description
DockerExecutor.execute() only checks that op.executor.imageDigest is *present*, but never calls
verifyImageDigest() to confirm the locally available/pulled image actually matches that declared
digest before spawning the container via runDocker(). This defeats the supply-chain integrity
guarantee described in the spec and plan ('If the pulled image's digest does not match the declared
digest, ImageDigestError is raised... The container is not started'), allowing a tampered or stale
image tagged with the same name to run unchecked.
Code

packages/runtime-docker/src/docker-executor.ts[R161-181]

+    // 3. Validate image digest presence.
+    if (op.executor.imageDigest === undefined) {
+      throw new ContainerPolicyError(
+        "docker operations require executor.imageDigest",
+        { image: op.executor.image },
+      );
+    }
+
+    // 4. Build args + env (validates secrets + socket before any spawn).
+    const args = this.buildDockerArgs(request);
+
+    // 5. Spawn.
+    const result = await runDocker(args, {
+      timeoutSeconds: op.timeoutSeconds,
+      ...(this.config.dockerPath !== undefined
+        ? { dockerPath: this.config.dockerPath }
+        : {}),
+      ...(this.config.dockerHost !== undefined
+        ? { dockerHost: this.config.dockerHost }
+        : {}),
+    });
Evidence
image.ts exports verifyImageDigest (docker inspect + pull + digest comparison) and it is exercised
only from image.test.ts. docker-executor.ts imports only ContainerPolicyError and runDocker from
internal/docker-cli.js — verifyImageDigest is never imported or invoked anywhere in execute(). The
plan explicitly lists digest mismatch as a pre-spawn check ('Digest mismatch — ImageDigestError with
both digests in context', 'Missing digest — ContainerPolicyError before spawn') implying
verification, not just presence, must occur before runDocker.

packages/runtime-docker/src/docker-executor.ts[161-181]
packages/runtime-docker/src/image.ts[13-46]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
DockerExecutor.execute() checks only that `op.executor.imageDigest` is defined, but never calls the `verifyImageDigest` function to confirm the actual local/pulled image digest matches the declared digest before running the container. This bypasses the intended supply-chain integrity check.

## Issue Context
`verifyImageDigest(image, expectedDigest, config)` is implemented in `image.ts` and does `docker inspect`/`docker pull` + digest comparison, throwing `ImageDigestError` on mismatch. It is currently only invoked from its own unit tests, never from the executor's `execute()` path.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[161-181]
- packages/runtime-docker/src/image.ts[13-46]

Call `verifyImageDigest(op.executor.image, op.executor.imageDigest, this.config)` after the presence check (step 3) and before `buildDockerArgs`/`runDocker` (steps 4-5), letting `ImageDigestError` propagate. Add/extend a docker-executor test asserting `runDocker` is never invoked when the mocked digest check fails.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Artifacts can read host files ✓ Resolved 🐞 Bug ⛨ Security
Description
Absolute artifact paths are copied directly by the host executor process, allowing a plan to collect
any host-readable file rather than only files produced in its workspace. This bypasses the container
boundary and can exfiltrate host credentials or configuration.
Code

packages/runtime-docker/src/docker-executor.ts[R275-278]

+    for (const artifact of op.artifacts) {
+      const src = isAbsolute(artifact.path)
+        ? artifact.path
+        : join(workspace, artifact.path);
Evidence
The artifact declaration exposes an unrestricted string path, IR validation has no artifact
confinement rule, and collectArtifacts uses absolute paths directly in host-side copyFile.

packages/runtime-docker/src/docker-executor.ts[275-283]
packages/ir/src/plan.ts[90-94]
packages/ir/src/validate.ts[281-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Absolute artifact paths are accepted as host filesystem sources without checking that they reside under the execution workspace.

## Issue Context
Require artifact sources to be relative, resolve them against the canonical workspace, and reject traversal or symlink resolution outside that workspace before reading.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[275-283]
- packages/ir/src/plan.ts[90-94]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. SIGKILL fallback never runs ✓ Resolved 🐞 Bug ☼ Reliability
Description
After a successful SIGTERM, child.killed indicates signal delivery rather than process exit, so
the grace callback skips SIGKILL even when the Docker CLI remains alive. Because the promise
resolves only on error or close, such executions can hang indefinitely despite their timeout.
Code

packages/runtime-docker/src/internal/docker-cli.ts[R58-61]

+      setTimeout(() => {
+        if (!child.killed) {
+          child.kill("SIGKILL");
+        }
Evidence
The fallback is gated on !child.killed, while Node documents that this property only records
successful signal delivery and does not indicate termination.

packages/runtime-docker/src/internal/docker-cli.ts[55-63]
packages/runtime-docker/src/internal/docker-cli.ts[72-90]
🌐 Node states that subprocess.killed indicates whether a signal was successfully received and does not indicate that the child process terminated.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The timeout escalation checks `child.killed`, which becomes true after successful signal delivery and does not mean the process exited.

## Issue Context
Track an `exited`/`closed` flag set by the exit or close event, send `SIGKILL` after the grace period unless that flag is set, and clear the grace timer when the child closes.

## Fix Focus Areas
- packages/runtime-docker/src/internal/docker-cli.ts[55-63]
- packages/runtime-docker/src/internal/docker-cli.ts[72-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View action required (4)
4. Secrets exposed in arguments 🐞 Bug ⛨ Security
Description
Resolved credential values are serialized as --env KEY=value Docker arguments, exposing them to
host process inspection, wrappers, and command-line audit logging. Pass secret values through a
mechanism that does not place them in child-process argv.
Code

packages/runtime-docker/src/docker-executor.ts[R82-84]

+    for (const [k, v] of Object.entries(env)) {
+      args.push("--env", `${k}=${v}`);
+    }
Evidence
Declared values from request.credentials are copied into the environment map, formatted as
KEY=value arguments, and supplied directly to spawn.

packages/runtime-docker/src/docker-executor.ts[114-120]
packages/runtime-docker/src/docker-executor.ts[80-84]
packages/runtime-docker/src/internal/docker-cli.ts[50-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Container credential values are embedded directly in the Docker CLI process arguments.

## Issue Context
`buildEnv` includes resolved `request.credentials`, and `runDocker` passes the generated argument array directly to `spawn`. Use a secure environment-file or another Docker/API mechanism that avoids argv disclosure, with restrictive permissions and reliable cleanup if a temporary file is used.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[80-84]
- packages/runtime-docker/src/docker-executor.ts[114-120]
- packages/runtime-docker/src/internal/docker-cli.ts[50-53]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Unsupported Docker timeout flag ✓ Resolved 🐞 Bug ≡ Correctness
Description
buildDockerArgs unconditionally passes --timeout to docker run, but that command has no such
option, so every real execution fails before the container starts. The timeout is already enforced
by runDocker, so this invalid CLI argument should be removed.
Code

packages/runtime-docker/src/docker-executor.ts[R54-55]

+      "--timeout",
+      String(op.timeoutSeconds),
Evidence
The changed code constructs a docker run invocation and adds --timeout; Docker's official run
reference lists --stop-timeout but no --timeout option.

packages/runtime-docker/src/docker-executor.ts[42-58]
🌐 The official docker run option table provides --stop-timeout, not --timeout.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`DockerExecutor.buildDockerArgs` adds an unsupported `--timeout` option to `docker run`, causing all real Docker executions to fail during argument parsing.

## Issue Context
The executor already passes `timeoutSeconds` to `runDocker`, which owns host-side timeout enforcement. Docker supports `--stop-timeout` for container stop behavior, not a runtime duration limit.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[54-55]
- packages/runtime-docker/src/internal/docker-cli.ts[55-63]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Artifact destination escapes directory ✓ Resolved 🐞 Bug ⛨ Security
Description
artifact.name or the fallback artifact.path is joined to artifactDir without a containment
check, so values containing .. can create or overwrite files outside the artifact directory.
Validate the resolved destination before creating directories or copying data.
Code

packages/runtime-docker/src/docker-executor.ts[R279-282]

+      const dest = join(artifactDir, artifact.name ?? artifact.path);
+      try {
+        await mkdir(dirname(dest), { recursive: true });
+        await copyFile(src, dest);
Evidence
The destination is constructed from unvalidated artifact metadata with join, which normalizes ..
but does not sandbox the result, and the executor immediately creates parent directories and writes
the file.

packages/runtime-docker/src/docker-executor.ts[279-283]
packages/ir/src/plan.ts[90-94]
packages/ir/src/validate.ts[281-325]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Artifact destination metadata can traverse outside `request.artifactDir` through parent-directory components or absolute-like values.

## Issue Context
Resolve the destination beneath a canonical artifact root and reject any result outside it before `mkdir` or `copyFile`; apply the check to both `artifact.name` and fallback `artifact.path`, and account for symlinked path components.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[279-283]
- packages/ir/src/plan.ts[90-94]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Digest verifier compares wrong digest ⊘ Outdated 🐞 Bug ≡ Correctness
Description
verifyImageDigest compares the image configuration .Id with the declared manifest/repository
digest used by image@digest, so valid pinned images are rejected. Verification must inspect and
normalize the matching RepoDigests entry or otherwise resolve the manifest digest.
Code

packages/runtime-docker/src/image.ts[R28-30]

+  let inspectResult = await runDocker(
+    ["inspect", "--format={{.Id}}", image],
+    opts,
Evidence
The IR defines imageDigest for container image pinning and the executor uses it in image@digest;
Docker's API documentation states that image Id is a configuration digest and differs from
manifest digests in RepoDigests.

packages/runtime-docker/src/image.ts[28-40]
packages/runtime-docker/src/docker-executor.ts[253-259]
packages/ir/src/plan.ts[59-63]
🌐 Docker's InspectResponse documentation says ID is calculated from image configuration and explicitly differs from RepoDigests, which contain image manifest digests.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`verifyImageDigest` reads Docker image `.Id`, which is a configuration digest, and compares it with the operation's repository/manifest digest.

## Issue Context
The declared digest is used in an `image@sha256:...` reference. Docker documents `.Id` and `RepoDigests` as different digest types.

## Fix Focus Areas
- packages/runtime-docker/src/image.ts[28-40]
- packages/runtime-docker/src/docker-executor.ts[253-259]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

8. Cache prepare collapses nested input paths ✓ Resolved 🐞 Bug ≡ Correctness
Description
DockerCacheManager.prepare() computes the destination as `join(target, relative(dirname(input),
input))`, which always evaluates to just the input's basename (since relative(dirname(x), x) ===
basename(x)), so any two declared inputs from different subdirectories but the same filename
silently overwrite each other in the cache and lose their directory structure — inconsistent with
collect(), which preserves full relative paths via relative(sourceDir, output).
Code

packages/runtime-docker/src/cache.ts[R26-28]

+    for (const input of inputs) {
+      const dest = join(target, relative(dirname(input), input));
+      await mkdir(dirname(dest), { recursive: true });
Evidence
For any path input, relative(dirname(input), input) reduces to the basename of input, e.g.
relative('/a/b', '/a/b/c.txt') === 'c.txt'. Declared cache inputs like 'src/a/util.ts' and
'src/b/util.ts' both map to '<cacheDir>/<key>/util.ts', so the second copy silently clobbers the
first. This contradicts the class docstring claiming prepare/collect 'preserv[e] relative path
structure', and diverges from collect()'s own path handling (line 43: `relative(sourceDir,
output)`), which does preserve subpaths.

packages/runtime-docker/src/cache.ts[15-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`DockerCacheManager.prepare()` computes the cache destination path using `relative(dirname(input), input)`, which always yields just the basename of the input file, discarding directory structure and causing filename collisions between inputs from different subdirectories.

## Issue Context
`collect()` in the same file preserves full relative paths via `relative(sourceDir, output)`. `prepare()` should use an analogous, well-defined root (e.g. accept a `sourceDir`/workspace root parameter, or require `inputs` be passed as paths relative to a known root) so the two methods use a consistent addressing scheme and avoid basename collisions.

## Fix Focus Areas
- packages/runtime-docker/src/cache.ts[23-39]

Change `prepare` to take (or derive) a base directory and compute `dest = join(target, relative(baseDir, input))` instead of `relative(dirname(input), input)`. Add a test with two inputs sharing a basename in different subdirectories to confirm both are preserved distinctly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

9. Docker CLI spawn-error exit code dropped 🐞 Bug ◔ Observability
Description
docker-executor.ts only includes exitCode in the ExecuteResult when result.exitCode >= 0, but
internal/docker-cli.ts deliberately returns exitCode: -1 for spawn errors and null-close events;
this filter silently discards that diagnostic sentinel, unlike the sibling runtime-host package
which always surfaces the code via code !== null.
Code

packages/runtime-docker/src/docker-executor.ts[193]

+        ...(result.exitCode >= 0 ? { exitCode: result.exitCode } : {}),
Evidence
internal/docker-cli.ts sets exitCode: -1 explicitly on child.on('error', ...) (line 77) and on
close with a null code (code ?? -1, line 87) to signal an abnormal spawn/exit.
docker-executor.ts's result.exitCode >= 0 guard (lines 193, 204) strips this sentinel from the
returned ExecuteResult, whereas runtime-host's host-executor.ts uses code !== null and always
keeps the code (including negative values) in the result, so the same class of failure is
diagnosable in one package but not the other.

packages/runtime-docker/src/internal/docker-cli.ts[72-90]
packages/runtime-docker/src/docker-executor.ts[188-212]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docker-executor.ts` only attaches `exitCode` to the `ExecuteResult` when it is `>= 0`, discarding the `-1` sentinel that `internal/docker-cli.ts` uses to signal spawn errors or abnormal termination, reducing diagnosability of Docker CLI launch failures.

## Issue Context
The sibling `runtime-host` package's `host-executor.ts` always includes the exit code (via `code !== null`) even when negative, keeping the same information visible to callers/schedulers.

## Fix Focus Areas
- packages/runtime-docker/src/docker-executor.ts[188-212]

Replace the `result.exitCode >= 0` guard with a check appropriate to distinguish 'no code available' from 'negative sentinel', e.g. always include `exitCode: result.exitCode`, or align semantics/tests with host-executor's pattern.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Web pages:
  +17 more
Review mode: 🧠 Deep: This introduces substantial security-sensitive Docker execution logic across many independent paths—container policy, secrets, image integrity, timeouts, mounts, caching, artifacts, and CLI lifecycle—making multiple subtle defects plausible.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/image.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/internal/docker-cli.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/cache.ts
Comment thread packages/runtime-docker/src/docker-executor.ts

@cubic-dev-ai cubic-dev-ai 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.

Ultrareview completed in 9m 13s

All reported issues were addressed across 17 files

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread packages/runtime-docker/src/cache.ts Outdated
Comment thread packages/runtime-docker/src/internal/docker-cli.ts
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/internal/docker-cli.ts Outdated
Comment thread packages/runtime-docker/src/cache.ts
Comment thread packages/runtime-docker/src/docker-executor.ts Outdated
Comment thread packages/runtime-docker/src/docker-executor.ts
Comment thread packages/runtime-docker/src/__tests__/cache.test.ts
Comment thread engdocs/architecture/wave-05-runtime-docker-plan.md
This was referenced Aug 9, 2026
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from 611136e to ecf9429 Compare August 10, 2026 20:19
@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Aug 10, 2026
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from ecf9429 to 20bf781 Compare August 10, 2026 20:26
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from 20bf781 to 318ec73 Compare August 10, 2026 20:44
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from 318ec73 to 6aebc91 Compare August 10, 2026 20:52
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from 6aebc91 to 9d1ab14 Compare August 10, 2026 20:57
ThePlenkov and others added 4 commits August 11, 2026 10:50
Core's assignId/assignIds used human-readable string derivation
(kind:name-or-command-or-index with collision counter) and passed
user spec.id through as-is, violating ADR-006. Now uses SHA-256
content-addressed ids: op-<64 hex> via node:crypto.createHash over
canonical JSON of {kind, name, context}, with spec.id folded into
hash context as userId.

- Created packages/core/src/internal/canonical.ts (computeOperationId)
- Rewrote packages/core/src/internal/ids.ts + plan.ts for SHA-256
- Exported computeOperationId from core public index.ts
- Fixed all tests to assert op-<64hex> format (90 core tests pass)
- Added cross-consistency test: core === ir computeOperationId (87 ir tests)
- Full monorepo: 16 projects test+typecheck+build green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… surrogates) and update runtime-modes expectations

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from fafdb9e to aa1a4ae Compare August 11, 2026 10:54
@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Aug 11, 2026
fix: Core ID assignment — SHA-256 content-addressed IDs (ADR-006)
An error occurred while trying to automatically change base from wave-4-runtime-host to wave-3-runtime August 11, 2026 12:36
ThePlenkov and others added 2 commits August 11, 2026 12:44
DockerExecutor implementation: container lifecycle, image pulling,
volume mounts, env/credential passing, artifact collection, cache
management, timeout handling. Mockable docker-cli.ts seam for unit
tests without Docker daemon. Integration tests skip if no Docker.
58 tests pass + 2 skipped (integration). Spec 04 amended to match
built runtime contract. Reviewer APPROVED after rework (sv-b3u):
collectArtifacts + mount-socket throw test added.

<details>
- 58 tests pass + 2 skipped (integration)
- typecheck clean
- build green (dist index.mjs 11.40kB + index.d.mts 4.89kB)
- lint clean
- reviewer approved (sv-1i2)
</details>

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@ThePlenkov
ThePlenkov force-pushed the wave-5-runtime-docker branch from aa1a4ae to 818a725 Compare August 11, 2026 12:45

@coderabbitai coderabbitai 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.

Actionable comments posted: 16

🤖 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 `@packages/core/src/__tests__/matrix.test.ts`:
- Around line 28-39: Update the multi-dimension cartesian-product test to use
two values for both node and os, then assert four operations with distinct ids
and the expected matrix environment values. Restore coverage for duplicate
matrix values, asserting the duplicate-id CompositionError from assignIds, and
for values with identical text but different types (1, "1", true), asserting
they receive distinct ids via canonicalStringify.

In `@packages/core/src/internal/plan.ts`:
- Line 489: Update the dependency handling in resolveEdges, detectCycles, and
topoSort so missing byId entries are not silently skipped. Replace the
byId.has(dep) guards with an explicit invariant failure that raises
CompositionError, while preserving normal traversal and ordering for resolved
dependencies.
- Around line 164-177: Preserve each matrix child’s __matrixCombo when
flattenArtifacts rebuilds nodes via makeNodeWith, including artifact-predecessor
and propagated-join paths. Thread the combination through flattenArtifacts,
assignIds, and contextFor, or retain it as typed node metadata, so contextFor
includes matrix values and generated operation IDs remain distinct.

In `@packages/ir/src/__tests__/core-consistency.test.ts`:
- Around line 5-27: Update the ADR-006 consistency test to stop comparing
coreComputeOperationId with irComputeOperationId, since the latter is
re-exported from core and cannot detect drift. Replace the self-comparison cases
with assertions against committed, precomputed golden hash values for each
input, and revise the test comment to describe the shared algorithm and
golden-value regression coverage accurately.

In `@packages/runtime-docker/src/__tests__/integration.test.ts`:
- Around line 13-16: Update the integration test setup around the busybox image
configuration and both test cases so no dummy digest is used. Require
SVERKA_BUSYBOX_DIGEST whenever SVERKA_DOCKER enables the suite, or skip the
entire suite unless both environment variables are set; preserve normal behavior
when integration tests are disabled.

In `@packages/runtime-docker/src/cache.ts`:
- Around line 8-12: Update the CacheManager.collect contract and its
implementations/call sites to receive or otherwise retain the cache key used by
prepare. Ensure collected outputs are written beneath the same <cacheDir>/<key>
directory returned by prepare, while preserving relative output paths within
that key-scoped directory.
- Around line 23-46: Update prepare and collect to resolve each target path and
reject any candidate that escapes cacheDir; validate the resolved prepare target
derived from key and each collect destination derived from sourceDir/output
before creating directories or copying files. Preserve valid nested paths, and
add traversal tests covering escaping key values and outputs outside sourceDir.

In `@packages/runtime-docker/src/config.ts`:
- Around line 12-13: Make the runAs property in the configuration type optional
to match its documented default, then apply "1000:1000" within DockerExecutor
whenever the value is absent. Preserve explicitly provided runAs values and
update the executor’s container configuration path accordingly.

In `@packages/runtime-docker/src/docker-executor.ts`:
- Around line 272-275: Update truncateLogs so truncated output, including
TRUNCATION_NOTICE, never exceeds maxLogBytes. Reserve the notice length when
calculating the slice boundary, and handle limits smaller than the notice by
returning only the allowed number of characters without exceeding the configured
limit.
- Around line 143-149: Update DockerExecutor.execute and its cache flow to use
the configured DockerExecutorConfig.cacheDir consistently instead of mounting
request.cacheDir. Establish a single cache ownership contract, call
DockerCacheManager.prepare() for the operation’s key before runContainer(), and
call DockerCacheManager.collect() for declared outputs after execution while
preserving result finalization.
- Line 289: Validate the destination derived in the artifact-copy flow around
dest before mkdir() or copyFile() runs: resolve artifactDir and the
artifact.name ?? artifact.path target, then reject targets outside the resolved
artifactDir, including traversal in both named and unnamed artifacts. Add tests
covering traversal through artifact.name and artifact.path while preserving
valid destinations.

In `@packages/runtime-docker/src/errors.ts`:
- Around line 26-29: Update ContainerPolicyError to accept a specific policy
error code and pass that code to DockerExecutorError instead of always using
CONTAINER_POLICY_VIOLATION. Revise every ContainerPolicyError call site to
provide the appropriate code, including MISSING_TIMEOUT, MISSING_DIGEST,
UNDECLARED_SECRET, and DOCKER_SOCKET_DENIED, and update tests to assert those
codes.

In `@packages/runtime-docker/src/image.ts`:
- Around line 28-36: Update the image inspection flow around runDocker so a pull
occurs only when the inspect result confirms the image is absent, and return
immediately for timedOut results. Validate the pull result before re-inspecting,
stopping on pull failure, and include Docker’s error output in the raised error
context instead of allowing an empty digest mismatch.
- Around line 28-44: Update the image verification flow around verifyImageDigest
to compare the registry manifest digest from RepoDigests, or inspect the
immutable image@expectedDigest reference, instead of comparing docker inspect’s
.Id configuration digest. Ensure DockerExecutor invokes verifyImageDigest on the
execution path before using the image, while preserving the existing
pull-and-reinspect behavior and ImageDigestError reporting.

In `@packages/runtime-docker/src/internal/docker-cli.ts`:
- Around line 65-70: Update DockerExecutor.runDocker and its stdout/stderr data
handlers to accept the configured maxLogBytes limit, append output only while
the accumulated byte count remains within that limit, and record that truncation
occurred once the limit is exceeded. Continue consuming both child streams after
truncation, and preserve the existing DockerExecutor.truncateLogs behavior for
final output handling.
- Around line 55-62: Update the timeout handling around the child process and
its "close" handler to track whether the child has actually closed, rather than
checking child.killed. Clear the grace-period timer when the close handler runs,
and send SIGKILL after GRACE_PERIOD_MS only when the tracked closed state
remains false.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 333ff9e5-c755-4078-8339-eba48ef0bc40

📥 Commits

Reviewing files that changed from the base of the PR and between 5908184 and 818a725.

📒 Files selected for processing (31)
  • engdocs/architecture/wave-05-runtime-docker-plan.md
  • packages/core/src/__tests__/composables/workflow.test.ts
  • packages/core/src/__tests__/dag.test.ts
  • packages/core/src/__tests__/ids.test.ts
  • packages/core/src/__tests__/laziness.test.ts
  • packages/core/src/__tests__/matrix.test.ts
  • packages/core/src/__tests__/public-api.test.ts
  • packages/core/src/__tests__/runtime-modes.test.ts
  • packages/core/src/index.ts
  • packages/core/src/internal/canonical.ts
  • packages/core/src/internal/ids.ts
  • packages/core/src/internal/plan.ts
  • packages/ir/src/__tests__/core-consistency.test.ts
  • packages/ir/src/ids.ts
  • packages/ir/src/internal/canonical.ts
  • packages/runtime-docker/project.json
  • packages/runtime-docker/src/__tests__/cache.test.ts
  • packages/runtime-docker/src/__tests__/docker-executor.test.ts
  • packages/runtime-docker/src/__tests__/errors.test.ts
  • packages/runtime-docker/src/__tests__/helpers/fixtures.ts
  • packages/runtime-docker/src/__tests__/image.test.ts
  • packages/runtime-docker/src/__tests__/integration.test.ts
  • packages/runtime-docker/src/__tests__/public-api.test.ts
  • packages/runtime-docker/src/cache.ts
  • packages/runtime-docker/src/config.ts
  • packages/runtime-docker/src/docker-executor.ts
  • packages/runtime-docker/src/errors.ts
  • packages/runtime-docker/src/image.ts
  • packages/runtime-docker/src/index.ts
  • packages/runtime-docker/src/internal/docker-cli.ts
  • specs/04-runtime-docker/spec.md
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Codacy Static Code Analysis
🧰 Additional context used
🪛 ast-grep (0.45.1)
packages/runtime-docker/src/internal/docker-cli.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 markdownlint-cli2 (0.23.2)
engdocs/architecture/wave-05-runtime-docker-plan.md

[warning] 77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 119-119: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 130-130: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 137-137: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 138-138: Ordered list item prefix
Expected: 1; Actual: 3; Style: 1/2/3

(MD029, ol-prefix)


[warning] 140-140: Ordered list item prefix
Expected: 2; Actual: 4; Style: 1/2/3

(MD029, ol-prefix)


[warning] 142-142: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 143-143: Ordered list item prefix
Expected: 1; Actual: 5; Style: 1/1/1

(MD029, ol-prefix)


[warning] 150-150: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 151-151: Ordered list item prefix
Expected: 1; Actual: 6; Style: 1/2/3

(MD029, ol-prefix)


[warning] 157-157: Ordered list item prefix
Expected: 2; Actual: 7; Style: 1/2/3

(MD029, ol-prefix)


[warning] 166-166: Ordered list item prefix
Expected: 3; Actual: 8; Style: 1/2/3

(MD029, ol-prefix)


[warning] 170-170: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 171-171: Ordered list item prefix
Expected: 1; Actual: 9; Style: 1/2/3

(MD029, ol-prefix)


[warning] 175-175: Ordered list item prefix
Expected: 2; Actual: 10; Style: 1/2/3

(MD029, ol-prefix)


[warning] 178-178: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 179-179: Ordered list item prefix
Expected: 1; Actual: 11; Style: 1/2/3

(MD029, ol-prefix)


[warning] 184-184: Ordered list item prefix
Expected: 2; Actual: 12; Style: 1/2/3

(MD029, ol-prefix)


[warning] 187-187: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 188-188: Ordered list item prefix
Expected: 1; Actual: 13; Style: 1/2/3

(MD029, ol-prefix)


[warning] 194-194: Ordered list item prefix
Expected: 2; Actual: 14; Style: 1/2/3

(MD029, ol-prefix)


[warning] 197-197: Ordered list item prefix
Expected: 3; Actual: 15; Style: 1/2/3

(MD029, ol-prefix)


[warning] 200-200: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 201-201: Ordered list item prefix
Expected: 1; Actual: 16; Style: 1/2/3

(MD029, ol-prefix)


[warning] 211-211: Ordered list item prefix
Expected: 2; Actual: 17; Style: 1/2/3

(MD029, ol-prefix)


[warning] 215-215: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 216-216: Ordered list item prefix
Expected: 1; Actual: 18; Style: 1/2/3

(MD029, ol-prefix)


[warning] 222-222: Ordered list item prefix
Expected: 2; Actual: 19; Style: 1/2/3

(MD029, ol-prefix)


[warning] 225-225: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 226-226: Ordered list item prefix
Expected: 1; Actual: 20; Style: 1/2/3

(MD029, ol-prefix)


[warning] 234-234: Ordered list item prefix
Expected: 2; Actual: 21; Style: 1/2/3

(MD029, ol-prefix)


[warning] 238-238: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 239-239: Ordered list item prefix
Expected: 1; Actual: 22; Style: 1/1/1

(MD029, ol-prefix)


[warning] 245-245: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 246-246: Ordered list item prefix
Expected: 1; Actual: 23; Style: 1/2/3

(MD029, ol-prefix)


[warning] 250-250: Ordered list item prefix
Expected: 2; Actual: 24; Style: 1/2/3

(MD029, ol-prefix)


[warning] 251-251: Ordered list item prefix
Expected: 3; Actual: 25; Style: 1/2/3

(MD029, ol-prefix)

🔇 Additional comments (25)
packages/runtime-docker/src/docker-executor.ts (2)

54-55: Remove the unsupported Docker timeout flag.

docker run does not support --timeout. The Docker CLI command will fail before it starts the container.


143-149: Verify the image digest before starting the container.

execute() never calls verifyImageDigest(). The digest verification utility is therefore bypassed.

packages/core/src/__tests__/composables/workflow.test.ts (1)

36-45: LGTM!

packages/core/src/__tests__/dag.test.ts (1)

9-10: LGTM!

Also applies to: 12-28, 31-45, 47-58, 68-77, 85-87

packages/core/src/__tests__/laziness.test.ts (1)

58-62: LGTM!

packages/runtime-docker/src/config.ts (1)

1-11: LGTM!

Also applies to: 14-18

packages/runtime-docker/src/internal/docker-cli.ts (1)

1-53: LGTM!

Also applies to: 72-91

packages/runtime-docker/src/image.ts (1)

18-26: LGTM!

packages/runtime-docker/src/__tests__/image.test.ts (1)

1-97: LGTM!

packages/runtime-docker/src/__tests__/helpers/fixtures.ts (1)

1-64: LGTM!

packages/runtime-docker/src/__tests__/public-api.test.ts (1)

1-51: LGTM!

packages/runtime-docker/src/index.ts (1)

2-8: LGTM!

packages/core/src/__tests__/ids.test.ts (1)

1-67: LGTM!

packages/core/src/__tests__/matrix.test.ts (1)

6-26: LGTM!

Also applies to: 48-52

packages/core/src/__tests__/public-api.test.ts (1)

46-72: LGTM!

packages/core/src/__tests__/runtime-modes.test.ts (1)

38-41: LGTM!

Also applies to: 51-52, 75-95, 104-112, 128-130, 143-152, 165-173

packages/core/src/index.ts (1)

28-28: LGTM!

packages/core/src/internal/canonical.ts (1)

2-18: LGTM!

Also applies to: 68-70, 108-113, 144-144

packages/core/src/internal/ids.ts (1)

1-28: LGTM!

packages/core/src/internal/plan.ts (4)

327-357: LGTM!


402-442: LGTM!


4-4: LGTM!

Also applies to: 29-41, 88-97


371-390: 🎯 Functional Correctness

Do not expand contextFor to include the full operation spec.

The documented operation-ID contract uses matrix values, userId, command, and args. Other operation fields are included in the emitted operation and therefore affect computePlanId; they are not inputs to computeOperationId.

			> Likely an incorrect or invalid review comment.
packages/ir/src/internal/canonical.ts (1)

1-8: LGTM!

packages/ir/src/ids.ts (1)

2-2: 🗄️ Data Integrity & Integration

No dependency change is required. packages/ir/package.json declares @sverka/core under dependencies, so published consumers can resolve the runtime import.

			> Likely an incorrect or invalid review comment.

Comment thread packages/core/src/__tests__/matrix.test.ts
Comment thread packages/core/src/internal/plan.ts
Comment thread packages/core/src/internal/plan.ts
Comment thread packages/ir/src/__tests__/core-consistency.test.ts
Comment thread packages/runtime-docker/src/__tests__/integration.test.ts
Comment thread packages/runtime-docker/src/errors.ts
Comment thread packages/runtime-docker/src/image.ts Outdated
Comment thread packages/runtime-docker/src/image.ts Outdated
Comment thread packages/runtime-docker/src/internal/docker-cli.ts
Comment thread packages/runtime-docker/src/internal/docker-cli.ts
devin-ai-integration Bot and others added 5 commits August 11, 2026 13:11
…wave-5

- matrix tests: strengthen multi-dim test and add duplicate/type tests
- plan.ts: preserve __matrixCombo in makeNodeWith; throw on unknown topo deps
- core-consistency: assert ir re-export and pin golden op- hashes
- runtime-docker: wire DockerCacheManager, fix cache ownership and path traversal
- runtime-docker: runAs default, specific ContainerPolicyError codes
- runtime-docker: log truncation reserves notice length
- runtime-docker: artifact collection validates destinations under artifactDir
- runtime-docker: image digest verification uses RepoDigests with timeout/pull checks
- docker-cli: track child close, cap output while streaming

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
…, cache paths, and artifacts

- Canonicalize paths before checking for docker.sock mount/env references.

- Reject absolute artifact paths and keep workspace-relative source resolution.

- Preserve cache input directory structure using the workspace root.

- Skip missing cache outputs during collect instead of aborting.

- Tighten SECRET_DENYLIST to avoid PUBLIC_KEY false positives.

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
- plan.ts: split topoSort into buildDependencyGraph + kahnSort helpers.

- docker-executor.ts: split buildDockerArgs into buildBaseArgs/buildMountArgs.

- image.ts: extract dockerOptions, dockerInspect, dockerPull, and assertInspectOk helpers.

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
- core/project.json: remove --passWithNoTests from test target
- core/ids.test.ts: add mixed-type context test
- core/plan.ts: suppress finalize rejection and extract outcomeForSkipped
- ir/validate.ts: recompute id by omitting identity fields, remove validateAcyclic early return, validate operation kind
- ir/validate.test.ts: remove unused plan constant, add extra top-level field test
- ir/core-consistency.test.ts: use OperationKind type and remove as never casts

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
…mplexity

Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

@ThePlenkov
ThePlenkov merged commit 3e0abc6 into wave-4-runtime-host Aug 11, 2026
4 checks passed
@ThePlenkov
ThePlenkov deleted the wave-5-runtime-docker branch September 23, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

baz: needs review size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant