Gate: approved by werkrevisor; merge waits for 2 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind: mixed.
Reasons
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:102: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint writing rules violated in the changed docs: .claude/skills/architecture/SKILL.md:115: more than one sentence on a line; .claude/skills/architecture/SKILL.md:115: line longer than 400 characters (1869)
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
hint the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62702, duration 736.1 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 553, cost 0.0085 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 11.9 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).
Reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
<!-- werkrevisor:838c9e9f7497c814a6e5211faa01489bfc519188 -->
<!-- werkrevisor-verdict:GREEN -->
<!-- werkrevisor-blocks:d3097b1777564a39c5f2d9349dfc0cad,1b496c217f3722a4bfcdaa02a1724870,b09058f768ab4e6c3b386fc15a809582,5a9f1c5a58fa76b9e565ff7a6643cb08,0ec703740f00bc603d2a1b2b97347e7e,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,c9a7f6fbe12a5a5cb1402f4774eb4275,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,fff05df0b66e2445b4aacefd7ec806e0,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,dd398a77c56361e3d9aa8e74d538ce64,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,3cbc2547cf8090fdfe9ff0b5abe1d4b6,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,537361ae9c870e3de8083f5a43ecc0c5,60b32b8bf195fff7f1cfe69ca6124eee,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,36690286c9a85883aacbddae4cef279a,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,347df3ff98d5fc23af50889c28d3f4f4,f42246df6780c5f5ddb3fbfb67e38df5 -->
<!-- werkrevisor-held-by-line-comments -->
<!-- werkrevisor-folded -->
<details><summary>🟢 werkrevisor GREEN for <code>838c9e9</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7775">the result for <code>49978c0</code></a></summary>
## 🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 2 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind: `mixed`.
### Reasons
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:102: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** writing rules violated in the changed docs: .claude/skills/architecture/SKILL.md:115: more than one sentence on a line; .claude/skills/architecture/SKILL.md:115: line longer than 400 characters (1869)
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
- **hint** the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62702, duration 736.1 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 553, cost 0.0085 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 11.9 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).<br>
Reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
</sub>
</details>
werkrevisor: GREEN / PASS for 838c9e9f7497c814a6e5211faa01489bfc519188.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `838c9e9f7497c814a6e5211faa01489bfc519188`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:838c9e9f7497c814a6e5211faa01489bfc519188 -->
On context close (e.g. systemd SIGTERM), a `ContextClosedEvent` listener in `BuildExecutor` terminates the process trees of all executing builds and waits (bounded) until their results are persisted as INTERRUPTED — a shutdown is never recorded as FAILED. Builds still queued stay PENDING and start no process. Both are re-enqueued by the watcher's startup recovery; INTERRUPTED therefore publishes as Gitea state `pending`, not `failure` (`GiteaStateMapping`).
The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.
The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label) — in the build's workspace, from the real paths of the Dockerfile and the context, which must stay inside it, and under Werkator's own tag `werkator-build/<scope>:<inputs>` per repository and branch (one scope for the pull-request target builds), so a branch never replaces another build's image (PR#168) — maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.
[high] Image scopes collide for distinct repository paths (Codex review)
The scope uses ArtifactKeys.repoKey, which replaces path separators and other characters with underscores. Thus /srv/repos/team/service and /srv/repos/team_service produce identical scopes for the same branch. With identical Dockerfile contents and configured paths, they also produce identical image tags, even when their configured docker.image names differ. Because context file contents are excluded from the inputs hash, the second repository accepts the first repository's image and runs its copied files. Hash the original repository path rather than its lossy sanitized key.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Image scopes collide for distinct repository paths** (Codex review)
The scope uses ArtifactKeys.repoKey, which replaces path separators and other characters with underscores. Thus /srv/repos/team/service and /srv/repos/team_service produce identical scopes for the same branch. With identical Dockerfile contents and configured paths, they also produce identical image tags, even when their configured docker.image names differ. Because context file contents are excluded from the inputs hash, the second repository accepts the first repository's image and runs its copied files. Hash the original repository path rather than its lossy sanitized key.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 2e2ae5b: the scope hashes the repository's path as it is, repoDir.toAbsolutePath().normalize(), no longer ArtifactKeys.repoKey. Test: DockerBuildRunnerUnitTest, Scenario#168.03, with team/service and team_service; it fails without the fix.
Fixed in `2e2ae5b`: the scope hashes the repository's path as it is, `repoDir.toAbsolutePath().normalize()`, no longer `ArtifactKeys.repoKey`. Test: `DockerBuildRunnerUnitTest`, Scenario#168.03, with `team/service` and `team_service`; it fails without the fix.
This changes the security boundary between branch-controlled Docker builds, host files, and builds holding secrets. A mistake could expose credentials or execute an attacker-controlled image, so human review is required.
Findings
[high] Image scopes collide for distinct repository paths at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:207, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 330606 (cached 264448) / out 2339, 74.4 s, 13 commands; reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188`.
<!-- werkrevisor-codex:838c9e9f7497c814a6e5211faa01489bfc519188 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker builds, host files, and builds holding secrets. A mistake could expose credentials or execute an attacker-controlled image, so human review is required.
### Findings
1. **[high] Image scopes collide for distinct repository paths** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:207`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 330606 (cached 264448) / out 2339, 74.4 s, 13 commands; reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188`.
</sub>
hint approval of werkrevisor for 838c9e9 not carried over: 1 approved block(s) are missing
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:102: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
hint 1 finding(s) of low severity or rated minor by Jev: src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:287
hint model recommends merge with caution
Findings
[medium] Shared Target Image at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:203-207, ORANGE, as a line comment
[low] Misleading Image Comment at src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:287, hint, as a line comment
9 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63131, calls 1, tokens in 60374 / out 34990 (reasoning included in out), duration 330.9 s, cost 0.0601 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2292, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 893, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 15.0 s, model_review 314.1 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).
Reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
<!-- werkrevisor:49978c00fe8e588b07ba99f28a30973496aa7a08 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:2f75c5e451144ffd118777525a3ea08a,1b496c217f3722a4bfcdaa02a1724870,b09058f768ab4e6c3b386fc15a809582,5a9f1c5a58fa76b9e565ff7a6643cb08,0ec703740f00bc603d2a1b2b97347e7e,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,c9a7f6fbe12a5a5cb1402f4774eb4275,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,fff05df0b66e2445b4aacefd7ec806e0,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,dd398a77c56361e3d9aa8e74d538ce64,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,3cbc2547cf8090fdfe9ff0b5abe1d4b6,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,537361ae9c870e3de8083f5a43ecc0c5,60b32b8bf195fff7f1cfe69ca6124eee,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,36690286c9a85883aacbddae4cef279a,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,347df3ff98d5fc23af50889c28d3f4f4,f42246df6780c5f5ddb3fbfb67e38df5 -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>49978c0</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7805">the result for <code>002f698</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:203-207
- **YELLOW** spec: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.03 given_when_then_misused (0.62 > yellow_above = 0.6)
- **hint** approval of werkrevisor for `838c9e9` not carried over: 1 approved block(s) are missing
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:102: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 finding(s) of low severity or rated minor by Jev: src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:287
- **hint** model recommends merge with caution
### Findings
1. **[medium] Shared Target Image** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:203-207`, ORANGE, as a line comment
2. **[low] Misleading Image Comment** at `src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:287`, hint, as a line comment
### Classifier
| Unit | Question | Problem | Confidence | Verdict |
|---|---|---|---|---|
| docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.03 | given_when_then_misused | 0.62 | – | YELLOW |
9 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63131, calls 1, tokens in 60374 / out 34990 (reasoning included in out), duration 330.9 s, cost 0.0601 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2292, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 893, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 15.0 s, model_review 314.1 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).<br>
Reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for 49978c00fe8e588b07ba99f28a30973496aa7a08.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `49978c00fe8e588b07ba99f28a30973496aa7a08`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:49978c00fe8e588b07ba99f28a30973496aa7a08 -->
The pull-request-target scope is per repository only, so every pull-request-target build of that repository shares one image, while such a build runs in a fresh workspace of the base commit the forge reports. Two pull requests into different base branches (e.g. main and release) whose Dockerfiles are identical therefore produce the same inputs hash and the same tag, and the second build runs an image that was baked from the first base branch's context files: the staleness check only covers the Dockerfile content and the two path strings, never the context contents. Include the base branch or base commit in the pull-request-target scope (or hash the context) so a build never runs another base branch's files.
**[medium] Shared Target Image** (werkrevisor)
ORANGE; Jev: real 0.85, severity 3.0 of 3
The pull-request-target scope is per repository only, so every pull-request-target build of that repository shares one image, while such a build runs in a fresh workspace of the base commit the forge reports. Two pull requests into different base branches (e.g. `main` and `release`) whose Dockerfiles are identical therefore produce the same inputs hash and the same tag, and the second build runs an image that was baked from the first base branch's context files: the staleness check only covers the Dockerfile content and the two path strings, never the context contents. Include the base branch or base commit in the pull-request-target scope (or hash the context) so a build never runs another base branch's files.
Fixed in 2e2ae5b: a pull-request target's scope holds its base branch (PullRequestTarget.BASE_REF_ENV), so builds into main and release never share an image. Per base branch rather than per base commit, as a branch scope keeps its image across its own commits. Test: DockerBuildRunnerUnitTest, Scenario#168.03; it fails without the fix.
Fixed in `2e2ae5b`: a pull-request target's scope holds its base branch (`PullRequestTarget.BASE_REF_ENV`), so builds into `main` and `release` never share an image. Per base branch rather than per base commit, as a branch scope keeps its image across its own commits. Test: `DockerBuildRunnerUnitTest`, Scenario#168.03; it fails without the fix.
The generated template still calls docker.image the "image for the build container", but with a dockerfile set the build now runs the image under werkator-build/<scope>:<inputs> and docker.image is only written into the label org.werkator.image (as docs/configuration.md and the release notes state). The template should say that, otherwise an operator reading the generated config expects image to name the image the build actually runs in.
**[low] Misleading Image Comment** (werkrevisor)
hint
The generated template still calls `docker.image` the "image for the build container", but with a `dockerfile` set the build now runs the image under `werkator-build/<scope>:<inputs>` and `docker.image` is only written into the label `org.werkator.image` (as `docs/configuration.md` and the release notes state). The template should say that, otherwise an operator reading the generated config expects `image` to name the image the build actually runs in.
[high] Repository image scopes collide after path sanitization (Codex review)
ArtifactKeys.repoKey replaces separators and other characters with underscores: /srv/repos/team/repo and /srv/repos/team_repo both become _srv_repos_team_repo. With the same branch, Dockerfile and configured paths, these repositories now receive the same image tag, even when their configured docker.image names differ. Since context contents are not hashed, the second repository runs the first repository’s image. Hash the unsanitized repository identity when constructing the scope.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Repository image scopes collide after path sanitization** (Codex review)
ArtifactKeys.repoKey replaces separators and other characters with underscores: /srv/repos/team/repo and /srv/repos/team_repo both become _srv_repos_team_repo. With the same branch, Dockerfile and configured paths, these repositories now receive the same image tag, even when their configured docker.image names differ. Since context contents are not hashed, the second repository runs the first repository’s image. Hash the unsanitized repository identity when constructing the scope.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed already in fb9da57, after the head this review saw: the scope hashes repoDir.toAbsolutePath().normalize(), not ArtifactKeys.repoKey. Test: DockerBuildRunnerUnitTest, Scenario#168.03, with team/service and team_service.
Fixed already in `fb9da57`, after the head this review saw: the scope hashes `repoDir.toAbsolutePath().normalize()`, not `ArtifactKeys.repoKey`. Test: `DockerBuildRunnerUnitTest`, Scenario#168.03, with `team/service` and `team_service`.
[medium] Reverting a Dockerfile can resurrect an outdated build context (Codex review)
The new tags retain an image for each historical Dockerfile hash, but the hash excludes files copied from the context. Build Dockerfile A with script version 1, change to Dockerfile B, then restore A while updating the script to version 2: inspection finds the old A tag and skips rebuilding, so the image still contains version 1. Previously B replaced the configured tag, so restoring A triggered a rebuild. Historical image reuse must account for context changes.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] Reverting a Dockerfile can resurrect an outdated build context** (Codex review)
The new tags retain an image for each historical Dockerfile hash, but the hash excludes files copied from the context. Build Dockerfile A with script version 1, change to Dockerfile B, then restore A while updating the script to version 2: inspection finds the old A tag and skips rebuilding, so the image still contains version 1. Previously B replaced the configured tag, so restoring A triggered a rebuild. Historical image reuse must account for context changes.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in fdf8f1d: one tag per scope and docker.image, werkator-build/<scope>:current, never one per inputs hash; the inputs hash stays in the label and decides the rebuild, as before. A restored Dockerfile finds the tag replaced and is built again. Test: DockerBuildRunnerUnitTest, "a changed Dockerfile replaces the branch's image rather than keeping one per version"; it fails with the tag per inputs hash.
Fixed in `fdf8f1d`: one tag per scope and `docker.image`, `werkator-build/<scope>:current`, never one per inputs hash; the inputs hash stays in the label and decides the rebuild, as before. A restored Dockerfile finds the tag replaced and is built again. Test: `DockerBuildRunnerUnitTest`, "a changed Dockerfile replaces the branch's image rather than keeping one per version"; it fails with the tag per inputs hash.
This changes the security boundary between branch-controlled Docker builds and the host, including image isolation for builds holding secrets. Mistakes could expose credentials or execute another repository’s code.
Findings
[high] Repository image scopes collide after path sanitization at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72, as a line comment
[medium] Reverting a Dockerfile can resurrect an outdated build context at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:147, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 330727 (cached 260864) / out 2412, 89.2 s, 14 commands; reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08`.
<!-- werkrevisor-codex:49978c00fe8e588b07ba99f28a30973496aa7a08 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including image isolation for builds holding secrets. Mistakes could expose credentials or execute another repository’s code.
### Findings
1. **[high] Repository image scopes collide after path sanitization** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72`, as a line comment
2. **[medium] Reverting a Dockerfile can resurrect an outdated build context** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:147`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 330727 (cached 260864) / out 2412, 89.2 s, 14 commands; reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08`.
</sub>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:105: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
hint the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 63460, duration 744.5 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8519, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67135 / out 916, cost 0.0079 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14979, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.1 s, model_review 723.0 s, config: defaults + host + repository (.werkrevisor.toml at main 7fabc11).
Reviewed head `002f698cfe190bd472050274eaad43445088ba7c` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
<!-- werkrevisor:002f698cfe190bd472050274eaad43445088ba7c -->
<!-- werkrevisor-verdict:YELLOW -->
<!-- werkrevisor-blocks:2f75c5e451144ffd118777525a3ea08a,f414a0d2ed718a47a6b172650d7ae51e,fefe16d5165f31a3f193398adf9cee85,b09058f768ab4e6c3b386fc15a809582,298d2179f72b31e8d9665bdfbfed5c05,483558cfaf942976b0c4070ab54764a4,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,fc8a000727eaf19021b752ad740555a6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,fff05df0b66e2445b4aacefd7ec806e0,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,a4f4ed3aedb62eafa6112aa879a30951,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,3cbc2547cf8090fdfe9ff0b5abe1d4b6,bd948604e493a2b62253646f56b2988d,9669a1c5c7cf1ced37498d5e8f0572c2,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,537361ae9c870e3de8083f5a43ecc0c5,60b32b8bf195fff7f1cfe69ca6124eee,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,1125cc78c4d59715a90106ffb862e85a,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,ee84a2128c5323c0a5188634ded14296,f42246df6780c5f5ddb3fbfb67e38df5 -->
<!-- werkrevisor-folded -->
<details><summary>🟡 werkrevisor YELLOW for <code>002f698</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7815">the result for <code>fdf8f1d</code></a></summary>
## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind: `mixed`.
### Reasons
- **YELLOW** risky paths changed: src/main/kotlin/de/javagil/werkator/build/PullRequestTarget.kt (**/*PullRequestTarget*)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:105: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
- **hint** the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
4 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 63460, duration 744.5 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8519, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67135 / out 916, cost 0.0079 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14979, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.1 s, model_review 723.0 s, config: defaults + host + repository (.werkrevisor.toml at main 7fabc11).<br>
Reviewed head `002f698cfe190bd472050274eaad43445088ba7c` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
</sub>
</details>
[high] Pull-request target images can execute an attacker-controlled Dockerfile (Codex review)
Passing workingDir to ensureImage also moves target builds' Dockerfiles from the primary checkout to the PR's base branch. PullRequestTargets accepts any same-repository base branch, without checking protection or a trusted allowlist; ConfigLoader even rejects branch selectors for these triggers. When a target definition uses dockerfile, a contributor can create a malicious base branch and open a PR into it. Its Dockerfile can install an ENTRYPOINT that reads the host-configured docker.env secrets when runArgs starts the container. The separate image scope prevents tag collisions but does not establish that the base branch is trusted.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Pull-request target images can execute an attacker-controlled Dockerfile** (Codex review)
Passing workingDir to ensureImage also moves target builds' Dockerfiles from the primary checkout to the PR's base branch. PullRequestTargets accepts any same-repository base branch, without checking protection or a trusted allowlist; ConfigLoader even rejects branch selectors for these triggers. When a target definition uses dockerfile, a contributor can create a malicious base branch and open a PR into it. Its Dockerfile can install an ENTRYPOINT that reads the host-configured docker.env secrets when runArgs starts the container. The separate image scope prevents tag collisions but does not establish that the base branch is trusted.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 71a2385: a pull-request target build builds no image any more. ConfigLoader.checkPullRequestTargetSandbox refuses docker.dockerfile on such a definition, also inherited from builds.default, and DockerBuildRunner.start refuses it again; the build runs in the host's docker.image as it is. The image scope per base branch is gone with it.
The older gap stays: a target build whose command runs files of its workspace, e.g. ./gradlew, runs those of any base branch. That needs a host list of allowed base branches and its pull_request_target counterpart; it is listed as a follow-up in the PR document.
Tests: PullRequestTargetConfigUnitTest and DockerBuildRunnerUnitTest, Scenario#168.04; both fail without the fix.
Fixed in `71a2385`: a pull-request target build builds no image any more. `ConfigLoader.checkPullRequestTargetSandbox` refuses `docker.dockerfile` on such a definition, also inherited from `builds.default`, and `DockerBuildRunner.start` refuses it again; the build runs in the host's `docker.image` as it is. The image scope per base branch is gone with it.
The older gap stays: a target build whose command runs files of its workspace, e.g. `./gradlew`, runs those of any base branch. That needs a host list of allowed base branches and its `pull_request_target` counterpart; it is listed as a follow-up in the PR document.
Tests: `PullRequestTargetConfigUnitTest` and `DockerBuildRunnerUnitTest`, Scenario#168.04; both fail without the fix.
This changes the security boundary between branch-controlled Docker inputs and host files or secrets available to trusted builds. Mistakes can expose credentials or compromise review gates, so human review is required.
Findings
[high] Pull-request target images can execute an attacker-controlled Dockerfile at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 504674 (cached 423168) / out 2497, 87.8 s, 15 commands; reviewed head `002f698cfe190bd472050274eaad43445088ba7c`.
<!-- werkrevisor-codex:002f698cfe190bd472050274eaad43445088ba7c -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker inputs and host files or secrets available to trusted builds. Mistakes can expose credentials or compromise review gates, so human review is required.
### Findings
1. **[high] Pull-request target images can execute an attacker-controlled Dockerfile** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 504674 (cached 423168) / out 2497, 87.8 s, 15 commands; reviewed head `002f698cfe190bd472050274eaad43445088ba7c`.
</sub>
Gate: no human review requested; merge conflict with main: the author rebases the branch onto main and pushes.
Change kind: mixed.
Reasons
ORANGE 2 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:145-155, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:206-216
YELLOW model reports a security concern: Pull-request target isolation: The same generated tag can be reused or overwritten across target definitions or base commits, so a target holding host secrets can run an image built from a workspace other than its forge-reported base commit.
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:108: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.01)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Stale Image at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:145-155, ORANGE, as a line comment
[high] Target Collision at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:206-216, ORANGE, as a line comment
Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63943, calls 1, tokens in 61159 / out 44681 (reasoning included in out), duration 447.4 s, cost 0.0719 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8587, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 4881, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67766 / out 521, cost 0.0058 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15362, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 431.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 7fabc11).
Reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
<!-- werkrevisor:fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:a5965626de132ffdcbf8dea85f89af60,f414a0d2ed718a47a6b172650d7ae51e,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,5fb67030595b3f68694921ea8f31529a,c56aa603b9c8a4f520e250dabed6e0e3,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,fc8a000727eaf19021b752ad740555a6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,bda3794ab50840abd946a9442fff7e3f,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,a4f4ed3aedb62eafa6112aa879a30951,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f0cf64cd776ad3ba545ae44821bcabac,bd948604e493a2b62253646f56b2988d,9669a1c5c7cf1ced37498d5e8f0572c2,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,537361ae9c870e3de8083f5a43ecc0c5,0fcea482c9b87aac198cd013d37a5563,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,84702f0dd6ac3c4439e7100915e62945,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,4d9698040feb8daa059e4389877a4205,f42246df6780c5f5ddb3fbfb67e38df5 -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>fdf8f1d</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7833">the result for <code>71a2385</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Gate: no human review requested; merge conflict with main: the author rebases the branch onto main and pushes.
Change kind: `mixed`.
### Reasons
- **ORANGE** 2 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:145-155, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:206-216
- **YELLOW** risky paths changed: src/main/kotlin/de/javagil/werkator/build/PullRequestTarget.kt (**/*PullRequestTarget*)
- **YELLOW** model reports a security concern: Pull-request target isolation: The same generated tag can be reused or overwritten across target definitions or base commits, so a target holding host secrets can run an image built from a workspace other than its forge-reported base commit.
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:108: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.01)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Stale Image** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:145-155`, ORANGE, as a line comment
2. **[high] Target Collision** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:206-216`, ORANGE, as a line comment
### Classifier
10 of 10 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63943, calls 1, tokens in 61159 / out 44681 (reasoning included in out), duration 447.4 s, cost 0.0719 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8587, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 4881, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67766 / out 521, cost 0.0058 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15362, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 431.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 7fabc11).<br>
Reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27 -->
The new branch-scoped tag is reused whenever the inputs label matches, but inputsHash covers only the Dockerfile bytes and the two configured path strings. If a later commit changes files in docker.context while keeping the Dockerfile unchanged, docker build is skipped and docker run uses the image containing the previous commit's files, which is common with COPY . .. The freshness decision must also account for the context contents or rebuild using Docker's cache on every run.
**[high] Stale Image** (werkrevisor)
ORANGE; Jev: real 0.89, severity 3.0 of 3
The new branch-scoped tag is reused whenever the inputs label matches, but `inputsHash` covers only the Dockerfile bytes and the two configured path strings. If a later commit changes files in `docker.context` while keeping the Dockerfile unchanged, `docker build` is skipped and `docker run` uses the image containing the previous commit's files, which is common with `COPY . .`. The freshness decision must also account for the context contents or rebuild using Docker's cache on every run.
Unchanged on purpose: this is the staleness rule Werkator always had (legacy ensure_docker_build_image). Before this PR the one tag docker.image was skipped on the same inputs label in the same way, so a commit that changed only context files ran the old image too. This PR moves where the image is built and under which tag, not when it is rebuilt.
Within one branch's scope the stale files are that branch's own, so no trust boundary is crossed. Rebuilding on every run, or hashing the context, changes build time for every Docker build and is a decision of its own; I leave it to a follow-up.
Unchanged on purpose: this is the staleness rule Werkator always had (legacy `ensure_docker_build_image`). Before this PR the one tag `docker.image` was skipped on the same inputs label in the same way, so a commit that changed only context files ran the old image too. This PR moves where the image is built and under which tag, not when it is rebuilt.
Within one branch's scope the stale files are that branch's own, so no trust boundary is crossed. Rebuilding on every run, or hashing the context, changes build time for every Docker build and is a decision of its own; I leave it to a follow-up.
The scope of a pull-request target contains only the repository path, base branch, and docker.image; it does not contain the target build identity or base SHA. Two target definitions on the same base that inherit the same image name but use different Dockerfiles or contexts therefore get the same tag. Since target builds are serialized per head branch and build name, those definitions can run concurrently, and one docker build can replace the image the other starts. Include the build identity and base commit in the scope or cache key.
**[high] Target Collision** (werkrevisor)
ORANGE; Jev: real 0.80, severity 3.0 of 3
The scope of a pull-request target contains only the repository path, base branch, and `docker.image`; it does not contain the target build identity or base SHA. Two target definitions on the same base that inherit the same image name but use different Dockerfiles or contexts therefore get the same tag. Since target builds are serialized per head branch and build name, those definitions can run concurrently, and one `docker build` can replace the image the other starts. Include the build identity and base commit in the scope or cache key.
Gone with 71a2385: a pull-request target build builds no image any more (Codex finding 7806), so there is no target scope left to collide; imageScope takes the repository path and the branch only.
Gone with `71a2385`: a pull-request target build builds no image any more (Codex finding 7806), so there is no target scope left to collide; `imageScope` takes the repository path and the branch only.
This changes security boundaries governing host filesystem access during Docker builds and image isolation from privileged pull-request target builds. A mistake could expose host credentials or execute branch-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 336702 (cached 265856) / out 1606, 70.3 s, 6 commands; reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27`.
<!-- werkrevisor-codex:fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes security boundaries governing host filesystem access during Docker builds and image isolation from privileged pull-request target builds. A mistake could expose host credentials or execute branch-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 336702 (cached 265856) / out 1606, 70.3 s, 6 commands; reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27`.
</sub>
docker.dockerfile and docker.context are branch settings, and docker build ran in the primary
checkout without checking them: a branch could send .git/werkator or the home of the service
account to the daemon and copy the tokens into its own image. Both are now refused at load time
outside the workspace, and resolved in the workspace by real path before docker build.
An image built from a Dockerfile gets Werkator's own tag per repository and branch, with one
scope for the pull-request target builds, so no branch replaces the image another build runs in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of Codex: ArtifactKeys.repoKey maps team/service and team_service to one key, so two
repositories could share an image. Review of werkrevisor: pull-request target builds into
different base branches shared one image, though the context's files differ between bases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of Codex: with a tag per inputs hash, an image kept for an older Dockerfile came back
with older context files once that Dockerfile was restored. One tag per scope is replaced
instead, and the inputs hash decides the rebuild in its label, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of Codex: since docker build runs in the workspace, a pull-request target build would
build its image from the pull request's base branch, which any contributor can create; its
ENTRYPOINT would read the host's docker.env. ConfigLoader refuses docker.dockerfile on such a
definition, inherited too, and the Docker runner refuses it again. The image scope per base
branch, added for the review of werkrevisor, is no longer needed and goes.
Rebased onto main with PR#169, so the version is 1.43.10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:124: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
hint 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:151-152
hint model recommends merge with caution
Findings
[low] Version mismatch at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120
hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code was bumped to 1.43.10 and the new entry in src/main/resources/templates/releases.html is v1.43.10. The PR document is the mandatory per-PR record and must agree with the code it describes, so please correct the version there (the PR title carries the same outdated version).
[low] Shared image scope at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:151-152, hint, as a line comment
10 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 66062, calls 1, tokens in 63135 / out 45913 (reasoning included in out), duration 487.4 s, cost 0.0740 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9794, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72564 / out 831, cost 0.0065 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15724, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 17.8 s, model_review 467.9 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.66.0.
<!-- werkrevisor:71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040 -->
<!-- werkrevisor-verdict:YELLOW -->
<!-- werkrevisor-blocks:190cdf091c07865419bfb7de29f41fc0,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,619d50cc462ee13c53df862b79970ab7,12cbfbf4670bd174cb9baec9c81f7549,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,bda3794ab50840abd946a9442fff7e3f,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f0cf64cd776ad3ba545ae44821bcabac,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,655626cf6c34a27a61314ca32d9e377b,537361ae9c870e3de8083f5a43ecc0c5,3f33ebab97ec2295f196be4b8683fecc,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,84702f0dd6ac3c4439e7100915e62945,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,0cdde49c80cad4475fe81e9b3f99f1fa,f42246df6780c5f5ddb3fbfb67e38df5,3148e223cbb9528754295f30fbca5e5c -->
<!-- werkrevisor-folded -->
<details><summary>🟡 werkrevisor YELLOW for <code>71a2385</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7858">the result for <code>d2a6457</code></a></summary>
## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind: `mixed`.
### Reasons
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **YELLOW** spec: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.04 given_when_then_misused (0.63 > yellow_above = 0.6)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:124: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:151-152
- **hint** model recommends merge with caution
### Findings
1. **[low] Version mismatch** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120`
hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code was bumped to 1.43.10 and the new entry in `src/main/resources/templates/releases.html` is v1.43.10. The PR document is the mandatory per-PR record and must agree with the code it describes, so please correct the version there (the PR title carries the same outdated version).
2. **[low] Shared image scope** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:151-152`, hint, as a line comment
### Classifier
| Unit | Question | Problem | Confidence | Verdict |
|---|---|---|---|---|
| docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.04 | given_when_then_misused | 0.63 | – | YELLOW |
10 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 66062, calls 1, tokens in 63135 / out 45913 (reasoning included in out), duration 487.4 s, cost 0.0740 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9794, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72564 / out 831, cost 0.0065 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15724, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 17.8 s, model_review 467.9 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.66.0.
</sub>
</details>
werkrevisor: YELLOW / HUMAN_REVIEW for 71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040.
The result comment of werkrevisor holds the details.
werkrevisor: YELLOW / HUMAN_REVIEW for `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040 -->
The tag of a built image is derived from the repository path, the branch and docker.image only. Two build definitions of one branch that share the same docker.image (the usual case, when both inherit it from builds.default) but build from different Dockerfiles therefore share the tag werkator-build/<scope>:current: every build replaces the image of the other definition and has to rebuild it, and the claim in this comment and in the PR document that "two definitions of one branch keep an image each" does not hold. Including the build definition's name in the scope would give every definition its own image.
**[low] Shared image scope** (werkrevisor)
hint
The tag of a built image is derived from the repository path, the branch and `docker.image` only. Two build definitions of one branch that share the same `docker.image` (the usual case, when both inherit it from `builds.default`) but build from different Dockerfiles therefore share the tag `werkator-build/<scope>:current`: every build replaces the image of the other definition and has to rebuild it, and the claim in this comment and in the PR document that "two definitions of one branch keep an image each" does not hold. Including the build definition's name in the scope would give every definition its own image.
Fixed in d2a6457: the scope holds docker.dockerfile and docker.context beside docker.image, so two definitions of one branch keep an image each unless they build the same; a changed file under the same path still replaces the image. Test: DockerBuildRunnerUnitTest, Scenario#168.03, with a second Dockerfile under the same image; it fails without the fix.
Fixed in `d2a6457`: the scope holds `docker.dockerfile` and `docker.context` beside `docker.image`, so two definitions of one branch keep an image each unless they build the same; a changed file under the same path still replaces the image. Test: `DockerBuildRunnerUnitTest`, Scenario#168.03, with a second Dockerfile under the same image; it fails without the fix.
This changes the security boundary between branch-controlled Docker builds and the host, including path containment and image isolation. A mistake could expose host credentials or let a branch poison images used by privileged builds, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 264761 (cached 198016) / out 1761, 60.9 s, 11 commands; reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040`.
<!-- werkrevisor-codex:71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including path containment and image isolation. A mistake could expose host credentials or let a branch poison images used by privileged builds, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 264761 (cached 198016) / out 1761, 60.9 s, 11 commands; reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040`.
</sub>
ai-review/assessment human review required; a human approval unlocks the merge; the approval of mi came before this result; review requested again, please approve again after reading it; merge already scheduled
Review of werkrevisor: two definitions of one branch under the same docker.image but with
different Dockerfiles shared one tag and replaced each other's image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:124: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
hint 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120
hint model recommends changes before merge
Findings
[medium] Stale Version at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120
hint; Jev: real 0.89, severity 1.5 of 3
The PR document states Version 1.43.8, while build.gradle.kts and the new release-notes entry both use 1.43.10.
The mandatory PR snapshot therefore identifies a release that this PR does not produce.
Update the PR document and PR title to the version actually shipped.
10 of 11 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65676, calls 1, tokens in 62781 / out 52926 (reasoning included in out), duration 457.6 s, cost 0.0823 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9780, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2415, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 745, cost 0.0022 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 13.3 s, model_review 442.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
<!-- werkrevisor:d2a64572c52565e1615b749baad2dc4e1a717f4a -->
<!-- werkrevisor-verdict:YELLOW -->
<!-- werkrevisor-blocks:190cdf091c07865419bfb7de29f41fc0,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,a4e9c6edbcbc80b137a2ccc84132c25e,06bd73cf5b15ab6e40e4e1258a0ec66e,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,46da7c1d916624ae54cff1ac6348e804,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f0cf64cd776ad3ba545ae44821bcabac,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,655626cf6c34a27a61314ca32d9e377b,537361ae9c870e3de8083f5a43ecc0c5,3f33ebab97ec2295f196be4b8683fecc,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,6b77ab4c0bfe32eb3c05f987952cbdf3,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,330e2bd0c2592c162f53021767ae5c3e,f42246df6780c5f5ddb3fbfb67e38df5,3148e223cbb9528754295f30fbca5e5c -->
<!-- werkrevisor-folded -->
<details><summary>🟡 werkrevisor YELLOW for <code>d2a6457</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7867">the result for <code>5307c46</code></a></summary>
## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Gate: the approval of mi came before this result; review requested again, please approve again after reading it; merge already scheduled.
Change kind: `mixed`.
### Reasons
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **YELLOW** spec: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.04 hard_to_read (0.55 > yellow_above = 0.5)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:124: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.01, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120
- **hint** model recommends changes before merge
### Findings
1. **[medium] Stale Version** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120`
hint; Jev: real 0.89, severity 1.5 of 3
The PR document states `Version 1.43.8`, while `build.gradle.kts` and the new release-notes entry both use `1.43.10`.
The mandatory PR snapshot therefore identifies a release that this PR does not produce.
Update the PR document and PR title to the version actually shipped.
### Classifier
| Unit | Question | Problem | Confidence | Verdict |
|---|---|---|---|---|
| docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.04 | hard_to_read | 0.55 | 0.33 | YELLOW |
10 of 11 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65676, calls 1, tokens in 62781 / out 52926 (reasoning included in out), duration 457.6 s, cost 0.0823 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9780, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2415, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 745, cost 0.0022 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 13.3 s, model_review 442.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
</sub>
</details>
ai-reviewer
requested review from mi 2026-10-24 21:50:53 +02:00
This changes the security boundary for branch-controlled Docker builds: host filesystem access, image isolation between branches, and images used by builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 353816 (cached 301696) / out 2202, 68.5 s, 13 commands; reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a`.
<!-- werkrevisor-codex:d2a64572c52565e1615b749baad2dc4e1a717f4a -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary for branch-controlled Docker builds: host filesystem access, image isolation between branches, and images used by builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 353816 (cached 301696) / out 2202, 68.5 s, 13 commands; reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a`.
</sub>
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
ORANGE 2 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:205-232, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:153-163
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:125: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
hint 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:121
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Format Break at src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:205-232, ORANGE, as a line comment
[medium] Stale Image at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:153-163, ORANGE, as a line comment
[low] Stale Version at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:121
hint
The Solution section says the PR is version 1.43.8, while build.gradle.kts and the new release-notes entry are version 1.43.10. Update the PR document so it identifies the version that will actually be shipped.
10 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65741, calls 1, tokens in 62839 / out 51731 (reasoning included in out), duration 449.1 s, cost 0.0809 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9802, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 5289, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 732, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 432.7 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
<!-- werkrevisor:5307c468655e09eafebaca4d250ae7d44a346f45 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:190cdf091c07865419bfb7de29f41fc0,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,a4e9c6edbcbc80b137a2ccc84132c25e,f6279688718b1ea6dd723d92c1a57cae,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,46da7c1d916624ae54cff1ac6348e804,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f0cf64cd776ad3ba545ae44821bcabac,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,655626cf6c34a27a61314ca32d9e377b,537361ae9c870e3de8083f5a43ecc0c5,3f33ebab97ec2295f196be4b8683fecc,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,6b77ab4c0bfe32eb3c05f987952cbdf3,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,330e2bd0c2592c162f53021767ae5c3e,f42246df6780c5f5ddb3fbfb67e38df5,3148e223cbb9528754295f30fbca5e5c -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>5307c46</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7887">the result for <code>9d41074</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 2 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:205-232, src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:153-163
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **YELLOW** spec: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.02 given_when_then_misused (0.64 > yellow_above = 0.6)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:125: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.02, risk 0.00, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:121
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Format Break** at `src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:205-232`, ORANGE, as a line comment
2. **[medium] Stale Image** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:153-163`, ORANGE, as a line comment
3. **[low] Stale Version** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:121`
hint
The Solution section says the PR is version 1.43.8, while `build.gradle.kts` and the new release-notes entry are version 1.43.10. Update the PR document so it identifies the version that will actually be shipped.
### Classifier
| Unit | Question | Problem | Confidence | Verdict |
|---|---|---|---|---|
| docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md Scenario#168.02 | given_when_then_misused | 0.64 | – | YELLOW |
10 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65741, calls 1, tokens in 62839 / out 51731 (reasoning included in out), duration 449.1 s, cost 0.0809 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9802, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 5289, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 732, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 432.7 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for 5307c468655e09eafebaca4d250ae7d44a346f45.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `5307c468655e09eafebaca4d250ae7d44a346f45`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:5307c468655e09eafebaca4d250ae7d44a346f45 -->
The tag scope includes the mutable docker.dockerfile and docker.context, so changing either retains the old scoped tag. If a later commit switches it back, that old tag still has a matching inputs label; because context files are not included in the label, the runner can reuse an image containing the old context even though those files changed in between. Include sufficient commit or context identity in the freshness decision, or overwrite and remove the previous scope, and add an A-to-B-to-A regression test.
**[medium] Stale Image** (werkrevisor)
ORANGE; Jev: real 0.70, severity 2.9 of 3
The tag scope includes the mutable `docker.dockerfile` and `docker.context`, so changing either retains the old scoped tag. If a later commit switches it back, that old tag still has a matching inputs label; because context files are not included in the label, the runner can reuse an image containing the old context even though those files changed in between. Include sufficient commit or context identity in the freshness decision, or overwrite and remove the previous scope, and add an A-to-B-to-A regression test.
Fixed in 9d41074: the scope is the repository path, the branch and docker.image again, never the Dockerfile or the context. Switching from A to B and back to A finds the one tag replaced by B and builds A again. Two definitions of one branch under the same image name replace each other's image; that costs a rebuild, as before this PR, and is never stale. Test: DockerBuildRunnerUnitTest, "a changed Dockerfile replaces the branch's image rather than keeping one per version", now with A, B and the first Dockerfile again; it fails with the scope of the previous commit.
Fixed in `9d41074`: the scope is the repository path, the branch and `docker.image` again, never the Dockerfile or the context. Switching from A to B and back to A finds the one tag replaced by B and builds A again. Two definitions of one branch under the same image name replace each other's image; that costs a rebuild, as before this PR, and is never stale. Test: `DockerBuildRunnerUnitTest`, "a changed Dockerfile replaces the branch's image rather than keeping one per version", now with A, B and the first Dockerfile again; it fails with the scope of the previous commit.
This makes configurations that were valid in 1.43.9 invalid by rejecting absolute Docker paths and paths containing .., but ConfigVersions.FORMAT_BROKE_IN is not advanced for this release. A file declaring werkator.version.since: 1.43.9 therefore passes version enforcement and fails later with a path error instead of being rejected through the version and migration guard. Advance the format-break marker and keep the version-dependent artifacts synchronized, as required by the configuration-version invariant.
**[high] Format Break** (werkrevisor)
ORANGE; Jev: real 0.76, severity 3.0 of 3
This makes configurations that were valid in 1.43.9 invalid by rejecting absolute Docker paths and paths containing `..`, but `ConfigVersions.FORMAT_BROKE_IN` is not advanced for this release. A file declaring `werkator.version.since: 1.43.9` therefore passes version enforcement and fails later with a path error instead of being rejected through the version and migration guard. Advance the format-break marker and keep the version-dependent artifacts synchronized, as required by the configuration-version invariant.
No change, on purpose. ConfigVersions.FORMAT_BROKE_IN refuses every file whose werkator.version.since lies below it (ConfigVersions.verdict: since < broke && version >= broke). Setting it to 1.43.10 would refuse every existing configuration that declares a since, including the committed .werkator.yml of every branch, so every build would fail after the deployment. The marker is for a change of the format as a whole; AGENTS.md keeps it for the removal of branches and bwrap.
This PR refuses only absolute paths and paths with .. in docker.dockerfile and docker.context, which a host or a branch could only use to reach outside the workspace. The message names the key and the path. PR#167 refused such entries in artifactDirs the same way.
No change, on purpose. `ConfigVersions.FORMAT_BROKE_IN` refuses every file whose `werkator.version.since` lies below it (`ConfigVersions.verdict`: `since < broke && version >= broke`). Setting it to 1.43.10 would refuse every existing configuration that declares a `since`, including the committed `.werkator.yml` of every branch, so every build would fail after the deployment. The marker is for a change of the format as a whole; AGENTS.md keeps it for the removal of `branches` and `bwrap`.
This PR refuses only absolute paths and paths with `..` in `docker.dockerfile` and `docker.context`, which a host or a branch could only use to reach outside the workspace. The message names the key and the path. PR#167 refused such entries in `artifactDirs` the same way.
[high] Restoring a Dockerfile path can resurrect a stale image (Codex review)
Including Dockerfile and context paths in the tag preserves separate historical images. If one definition switches from Dockerfile.a to Dockerfile.b and then back to unchanged Dockerfile.a, while files copied by Dockerfile.a have changed, the original image's inputs label still matches because context contents are not hashed. The build silently runs with the old copied files. Previously, Dockerfile.b replaced the same tag, so switching back triggered a rebuild. The added test covers changing Dockerfile contents at one path, but misses this path-switch sequence.
Fix it, or reply why it does not hold, and resolve this comment.
**[high] Restoring a Dockerfile path can resurrect a stale image** (Codex review)
Including Dockerfile and context paths in the tag preserves separate historical images. If one definition switches from Dockerfile.a to Dockerfile.b and then back to unchanged Dockerfile.a, while files copied by Dockerfile.a have changed, the original image's inputs label still matches because context contents are not hashed. The build silently runs with the old copied files. Previously, Dockerfile.b replaced the same tag, so switching back triggered a rebuild. The added test covers changing Dockerfile contents at one path, but misses this path-switch sequence.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 9d41074, together with werkrevisor's finding 7869: the scope holds neither the Dockerfile nor the context path any more, only the repository path, the branch and docker.image. Switching from Dockerfile to Dockerfile.docs replaces the one tag, so switching back builds again. The test "a changed Dockerfile replaces the branch's image rather than keeping one per version" now covers this path-switch sequence and fails with the previous scope.
Fixed in `9d41074`, together with werkrevisor's finding 7869: the scope holds neither the Dockerfile nor the context path any more, only the repository path, the branch and `docker.image`. Switching from `Dockerfile` to `Dockerfile.docs` replaces the one tag, so switching back builds again. The test "a changed Dockerfile replaces the branch's image rather than keeping one per version" now covers this path-switch sequence and fails with the previous scope.
This changes the security boundary between branch-controlled Docker builds and host files, shared images, and secret-bearing pull-request target builds. A mistake could expose credentials or execute attacker-controlled code with secrets, so human review is required.
Findings
[high] Restoring a Dockerfile path can resurrect a stale image at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:155, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 385951 (cached 322176) / out 2717, 90.7 s, 15 commands; reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45`.
<!-- werkrevisor-codex:5307c468655e09eafebaca4d250ae7d44a346f45 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker builds and host files, shared images, and secret-bearing pull-request target builds. A mistake could expose credentials or execute attacker-controlled code with secrets, so human review is required.
### Findings
1. **[high] Restoring a Dockerfile path can resurrect a stale image** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:155`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 385951 (cached 322176) / out 2717, 90.7 s, 15 commands; reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45`.
</sub>
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
Review of werkrevisor: with the Dockerfile and context in the scope, switching from A to B and
back to A found A's old image, with older context files the inputs label cannot see. One tag per
branch and image name is replaced instead; two definitions under one image name rebuild, as before.
Scenario#168.02 now holds one situation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:127: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
hint 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:123
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Stale Image at src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:149-160, ORANGE, as a line comment
[low] Stale Version at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:123
hint
The PR document says the release is version 1.43.8, but the changed build.gradle.kts and the top release-notes entry identify 1.43.10. The PR title also repeats 1.43.8. Update the document and title so the mandatory PR record identifies the release this change actually creates.
Classifier
11 of 11 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65075, calls 1, tokens in 62226 / out 47187 (reasoning included in out), duration 433.0 s, cost 0.0753 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9782, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2537, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72792 / out 1131, cost 0.0024 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15864, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 19.3 s, model_review 411.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
<!-- werkrevisor:9d4107488f87b30fe92a750f0076203fecd6ebae -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:190cdf091c07865419bfb7de29f41fc0,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,619d50cc462ee13c53df862b79970ab7,3406c1d832c2eae51cc1121e2a72156e,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,163c9b38e4589f9e42823e97c4a8df5a,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,945bb3904fdd1bb24ae8f1fe4471cc7a,ca3237865509aa8797de919b6ceb681d,df1bf7f6b48e47f265cd294eab01dd6d,7067fb3ce6d6478d079c41aac55a3fff,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f0cf64cd776ad3ba545ae44821bcabac,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,655626cf6c34a27a61314ca32d9e377b,537361ae9c870e3de8083f5a43ecc0c5,3f33ebab97ec2295f196be4b8683fecc,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,51e579c86ab51cd693181814e64ecc40,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,41ee8f09b33bc3e39aa4fd3b4e62187f,77c5dc0900f3d93a1fd137d43e4aafe0,f42246df6780c5f5ddb3fbfb67e38df5,3148e223cbb9528754295f30fbca5e5c -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>9d41074</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7910">the result for <code>0852f41</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:149-160
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:127: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:123
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Stale Image** at `src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:149-160`, ORANGE, as a line comment
2. **[low] Stale Version** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:123`
hint
The PR document says the release is version 1.43.8, but the changed `build.gradle.kts` and the top release-notes entry identify 1.43.10. The PR title also repeats 1.43.8. Update the document and title so the mandatory PR record identifies the release this change actually creates.
### Classifier
11 of 11 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65075, calls 1, tokens in 62226 / out 47187 (reasoning included in out), duration 433.0 s, cost 0.0753 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9782, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2537, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72792 / out 1131, cost 0.0024 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15864, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 19.3 s, model_review 411.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for 9d4107488f87b30fe92a750f0076203fecd6ebae.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `9d4107488f87b30fe92a750f0076203fecd6ebae`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:9d4107488f87b30fe92a750f0076203fecd6ebae -->
The branch-scoped image is reused whenever the label derived from the Dockerfile contents and configured path strings matches. If a later commit on the same branch changes only files under docker.context, such as an application archive copied by the Dockerfile, the label remains unchanged and docker build is skipped. The container then runs the previous commit's image, which can produce a false-green build. Include the committed context or build identity in the cache validity, or always invoke Docker's content-aware build cache.
**[high] Stale Image** (werkrevisor)
ORANGE; Jev: real 0.74, severity 2.9 of 3
The branch-scoped image is reused whenever the label derived from the Dockerfile contents and configured path strings matches. If a later commit on the same branch changes only files under `docker.context`, such as an application archive copied by the Dockerfile, the label remains unchanged and `docker build` is skipped. The container then runs the previous commit's image, which can produce a false-green build. Include the committed context or build identity in the cache validity, or always invoke Docker's content-aware build cache.
Fixed in 0852f41, as decided by mi: docker build now runs before every command, no longer only when the inputs label changed. Docker's layer cache sees every changed file of the context and keeps an unchanged build short; the label only records the inputs. Test: DockerBuildRunnerUnitTest, "builds the image again although its inputs label matches, so a changed context file is never stale"; it fails with the previous runner.
Fixed in `0852f41`, as decided by mi: `docker build` now runs before every command, no longer only when the inputs label changed. Docker's layer cache sees every changed file of the context and keeps an unchanged build short; the label only records the inputs. Test: `DockerBuildRunnerUnitTest`, "builds the image again although its inputs label matches, so a changed context file is never stale"; it fails with the previous runner.
This changes the security boundary between branch-controlled Docker inputs and the host, including image isolation for builds holding secrets. A mistake could expose host credentials or execute an attacker-controlled image in a privileged build, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 378551 (cached 335488) / out 1959, 71.4 s, 16 commands; reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae`.
<!-- werkrevisor-codex:9d4107488f87b30fe92a750f0076203fecd6ebae -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker inputs and the host, including image isolation for builds holding secrets. A mistake could expose host credentials or execute an attacker-controlled image in a privileged build, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 378551 (cached 335488) / out 1959, 71.4 s, 16 commands; reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae`.
</sub>
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
Review of werkrevisor: the inputs label covers the Dockerfile and its two paths, not the
files of the context, so a commit that changed only a copied file ran in the image of an
earlier commit and could turn green falsely. The legacy skip goes; Docker's layer cache keeps
an unchanged build short, and the label only records the inputs. Decided by mi.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:130: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint writing rules violated in the changed docs: .claude/skills/architecture/SKILL.md:115: more than one sentence on a line; .claude/skills/architecture/SKILL.md:115: line longer than 400 characters (1552)
hint 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
hint 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126, src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:285-287
hint model recommends merge with caution
Findings
[medium] Unused Docker path aborts the start at src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:216-235, ORANGE, as a line comment
[low] Wrong version in the PR document at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126
hint
The document states "Version 1.43.8 with a release-notes entry", but build.gradle.kts was bumped to 1.43.10 and releases.html opens with v1.43.10 (the PR title still says v1.43.8). The PR document therefore names a version that was never released and contradicts the code and the release notes; it should say 1.43.10.
[low] Stale Dockerfile comment at src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:285-287, hint, as a line comment
Classifier
11 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 67917, calls 1, tokens in 64894 / out 32664 (reasoning included in out), duration 292.7 s, cost 0.0586 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2717, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 73030 / out 737, cost 0.0060 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 16568, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 16.6 s, model_review 274.4 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `0852f41182355955b68cb0ed7062b2ae53243159` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
<!-- werkrevisor:0852f41182355955b68cb0ed7062b2ae53243159 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:b30559f9cfe8f495292696bf46ca84c1,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,763a6a699f5fbe6b4485f63e78cefef7,06988f0ec2437fff3f049f7daa2ffed0,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,6eadb27b920a7cd0bd78ac9f2b304206,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,bbef2269d8c9a06410b78d45e858ab84,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,6664af47cb1186ee14f653877c3d595e,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f1fb33fe08df610eb519f0e6b309e866,e96741a00f48a491ad62a6b5942b1ea8,718bf048f0c3f9efe10fa8c757e27b15,f0cf64cd776ad3ba545ae44821bcabac,b4796ff5692e4e2992c10e713ac79d71,68b0e857efe00e7aebdfb6e3fd11818e,c3fdedf14752e5fadf99bda287a2f688,f51910b7f7c7c6e564072e57ab952ffa,655626cf6c34a27a61314ca32d9e377b,537361ae9c870e3de8083f5a43ecc0c5,9b5517a417f54daae0ace0b5bea2c5ef,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,51e579c86ab51cd693181814e64ecc40,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,fa758c6d73f7158e5e39f94949b0b9a0,4daf47c31cc17e79a5c25f673e7f757a,a8f8c3368ad8784c250c9ba1db30a935,e2b4d5ad83ef5703ec369eccc4ecc414,f42246df6780c5f5ddb3fbfb67e38df5,3148e223cbb9528754295f30fbca5e5c -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>0852f41</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/168#issuecomment-7929">the result for <code>61c96e7</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:216-235
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:130: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** writing rules violated in the changed docs: .claude/skills/architecture/SKILL.md:115: more than one sentence on a line; .claude/skills/architecture/SKILL.md:115: line longer than 400 characters (1552)
- **hint** 8 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 2 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126, src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:285-287
- **hint** model recommends merge with caution
### Findings
1. **[medium] Unused Docker path aborts the start** at `src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:216-235`, ORANGE, as a line comment
2. **[low] Wrong version in the PR document** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126`
hint
The document states "Version 1.43.8 with a release-notes entry", but `build.gradle.kts` was bumped to 1.43.10 and `releases.html` opens with v1.43.10 (the PR title still says v1.43.8). The PR document therefore names a version that was never released and contradicts the code and the release notes; it should say 1.43.10.
3. **[low] Stale Dockerfile comment** at `src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:285-287`, hint, as a line comment
### Classifier
11 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 67917, calls 1, tokens in 64894 / out 32664 (reasoning included in out), duration 292.7 s, cost 0.0586 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2717, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 73030 / out 737, cost 0.0060 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 16568, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 16.6 s, model_review 274.4 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `0852f41182355955b68cb0ed7062b2ae53243159` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for 0852f41182355955b68cb0ed7062b2ae53243159.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `0852f41182355955b68cb0ed7062b2ae53243159`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:0852f41182355955b68cb0ed7062b2ae53243159 -->
On context close (e.g. systemd SIGTERM), a `ContextClosedEvent` listener in `BuildExecutor` terminates the process trees of all executing builds and waits (bounded) until their results are persisted as INTERRUPTED — a shutdown is never recorded as FAILED. Builds still queued stay PENDING and start no process. Both are re-enqueued by the watcher's startup recovery; INTERRUPTED therefore publishes as Gitea state `pending`, not `failure` (`GiteaStateMapping`).
The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.
The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it builds the configured image before every command (the `org.werkator.build-inputs-sha256` image label records its inputs), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.
The generated configuration template still says the image is rebuilt "when missing or stale"; the same sentence is in docs/configuration.md. Since this PR, ensureImage runs docker build before every command, so an image is never kept because it is missing or stale — the inputs label only records what the image was built from. Both texts should describe the new rule, otherwise operators read a rule the code no longer follows.
**[low] Stale Dockerfile comment** (werkrevisor)
hint
The generated configuration template still says the image is rebuilt "when missing or stale"; the same sentence is in `docs/configuration.md`. Since this PR, `ensureImage` runs `docker build` before every command, so an image is never kept because it is missing or stale — the inputs label only records what the image was built from. Both texts should describe the new rule, otherwise operators read a rule the code no longer follows.
Fixed in 61c96e7: the init template, docs/configuration.md and the KDoc of DockerConfig.dockerfile say the image is built from it before every command.
Fixed in `61c96e7`: the `init` template, `docs/configuration.md` and the KDoc of `DockerConfig.dockerfile` say the image is built from it before every command.
[medium] Unused Docker path aborts the start (werkrevisor)
ORANGE; Jev: real 0.84, severity 3.0 of 3
checkDockerBuildPaths refuses docker.dockerfile and docker.context for every branch and build definition, even for a definition whose docker.enabled is false or whose dockerfile is empty. Those values are only read by ensureImage, which returns early when no Dockerfile is configured, so no directory is ever sent to the daemon. A host that keeps an absolute or .. context beside a native build now fails to start with a config-format error although nothing about that build changed. Restrict the check to definitions that actually build an image (Docker enabled and a Dockerfile set), or keep the old value for the unused case.
**[medium] Unused Docker path aborts the start** (werkrevisor)
ORANGE; Jev: real 0.84, severity 3.0 of 3
`checkDockerBuildPaths` refuses `docker.dockerfile` and `docker.context` for every branch and build definition, even for a definition whose `docker.enabled` is false or whose `dockerfile` is empty. Those values are only read by `ensureImage`, which returns early when no Dockerfile is configured, so no directory is ever sent to the daemon. A host that keeps an absolute or `..` context beside a native build now fails to start with a config-format error although nothing about that build changed. Restrict the check to definitions that actually build an image (Docker enabled and a Dockerfile set), or keep the old value for the unused case.
Fixed in 61c96e7: checkDockerBuildPaths checks the two paths only where Docker is enabled and a Dockerfile is set, which is where ensureImage reads them. Test: ConfigLoaderUnitTest, "a Docker build path is not checked where no image is built"; it fails without the fix.
Fixed in `61c96e7`: `checkDockerBuildPaths` checks the two paths only where Docker is enabled and a Dockerfile is set, which is where `ensureImage` reads them. Test: `ConfigLoaderUnitTest`, "a Docker build path is not checked where no image is built"; it fails without the fix.
This changes security boundaries governing host filesystem access during Docker image builds and image isolation between branches and secret-bearing pull-request target builds. A mistake could expose host credentials or execute attacker-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 505512 (cached 440576) / out 1937, 62.7 s, 13 commands; reviewed head `0852f41182355955b68cb0ed7062b2ae53243159`.
<!-- werkrevisor-codex:0852f41182355955b68cb0ed7062b2ae53243159 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes security boundaries governing host filesystem access during Docker image builds and image isolation between branches and secret-bearing pull-request target builds. A mistake could expose host credentials or execute attacker-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 505512 (cached 440576) / out 1937, 62.7 s, 13 commands; reviewed head `0852f41182355955b68cb0ed7062b2ae53243159`.
</sub>
Review of werkrevisor: the check refused a docker.context or docker.dockerfile that nothing reads,
beside a native build or a used-as-is image, and could stop a start for nothing. The init template,
docs/configuration.md and the KDoc of DockerConfig.dockerfile no longer speak of a rebuild when
stale. The architecture skill keeps the line of main and says the change in lines of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:130: - [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.
hint 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/main/kotlin/de/javagil/werkator/config/WerkatorConfig.kt
hint 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
hint 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126
hint model recommends merge with caution
Findings
[low] Stale version number at docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126
hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code bumps version to 1.43.10 in build.gradle.kts and the new entry in releases.html is v1.43.10; the PR title carries the same wrong number. The version is what identifies a running instance, so the snapshot must name the version that actually ships.
Classifier
11 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 68624, calls 1, tokens in 65571 / out 39184 (reasoning included in out), duration 353.9 s, cost 0.0667 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 5, tokens in 80090 / out 1155, cost 0.0051 USD (reported), build file judge `jev-1.14.0` calls 10, tokens in 18342, cost 0.0008 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.3 s, model_review 331.6 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).
Reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
<!-- werkrevisor:61c96e787314c97768dfbf1d98322dc2d587269c -->
<!-- werkrevisor-verdict:YELLOW -->
<!-- werkrevisor-blocks:393b5401d5d32339625a7693afa35b5b,2d754b30afd28870218f506ac1ec60ed,bf6a459061cee27e7aacedf95c035b77,96cc4e0629c4ff5f0845c5adbffcb0c8,b09058f768ab4e6c3b386fc15a809582,65105675a8a26acbfc24a79611d0bed0,763a6a699f5fbe6b4485f63e78cefef7,06988f0ec2437fff3f049f7daa2ffed0,674ccf04271b61ea4d08577555e83fd3,735f5852b610f8d6bc696f20be206aff,6eadb27b920a7cd0bd78ac9f2b304206,03172db7e6ee6f112ef2f6b0be381f17,ccd2f0c74d54b00e66a9630dc5d81ff6,734509026be4ab83b5172a3406c83e79,27e6f32f531ddb9c7dc2e8f031468b16,bbef2269d8c9a06410b78d45e858ab84,f048d84201907e91f889f3ce62a7ed1b,47dff25e8b6e33cc49e865c14ef3faf7,c76a447738c7eb3f9e559af6aaafd229,785708e5cf8363ef86d5e0e68bdd65df,6664af47cb1186ee14f653877c3d595e,d1aeeede87fe9f5e0dd366e274d0d47e,fa32b4c5622ff4e0d5426b6bc082dea9,418c35164373c37efc2040dafe1644c0,b8f33ae9d929f09c799eb20de4d72dea,4221bc9874ea38af734abb5a5cca14ed,ee1c10f61de431327539a5ca7c72b620,9e1376321b12def5233ce79d94c26e4b,6b43df8b1fdbd12b3a06808d0e4fe29b,f1fb33fe08df610eb519f0e6b309e866,e96741a00f48a491ad62a6b5942b1ea8,718bf048f0c3f9efe10fa8c757e27b15,f0cf64cd776ad3ba545ae44821bcabac,8a0a9783ce184dce3953f851f97c5c84,c3fdedf14752e5fadf99bda287a2f688,2b2599e8f3da387e11fd98807528d086,655626cf6c34a27a61314ca32d9e377b,13269ea3f937a3856e233c63017f4369,537361ae9c870e3de8083f5a43ecc0c5,9b5517a417f54daae0ace0b5bea2c5ef,f157f0e84c0ba9f4182ba2d52c863995,da78edc727eb85dc3461bd715dfb226e,51e579c86ab51cd693181814e64ecc40,3498266a11c3624318e3f493aa3cee20,6c564e3e0baee6758e1338a7deead137,6d0fe8cf26293c795e305da408f87214,ce5497fdbb6c1fd6658a9a6402b3749a,6380029b4ae60f2543a7fa287618a63f,fa758c6d73f7158e5e39f94949b0b9a0,4daf47c31cc17e79a5c25f673e7f757a,a8f8c3368ad8784c250c9ba1db30a935,e2b4d5ad83ef5703ec369eccc4ecc414,569e8f0fe633f98fb0e282ba7ff94784,3148e223cbb9528754295f30fbca5e5c -->
## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind: `mixed`.
### Reasons
- **YELLOW** risky paths changed: src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (**/*PullRequestTarget*)
- **hint** deferred TODOs in the PR document: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:130: `- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.`
- **hint** 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/main/resources/templates/releases.html, src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/main/kotlin/de/javagil/werkator/config/WerkatorConfig.kt
- **hint** 2 changed files were given to the AI only as diff, not in full, so it could not check them against the rest of their file (all full files together would exceed [review] max_full_files_chars = 160000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt, src/test/kotlin/de/javagil/werkator/config/PullRequestTargetConfigUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 finding(s) of low severity or rated minor by Jev: docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126
- **hint** model recommends merge with caution
### Findings
1. **[low] Stale version number** at `docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126`
hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code bumps `version` to `1.43.10` in `build.gradle.kts` and the new entry in `releases.html` is `v1.43.10`; the PR title carries the same wrong number. The version is what identifies a running instance, so the snapshot must name the version that actually ships.
### Classifier
11 of 11 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 68624, calls 1, tokens in 65571 / out 39184 (reasoning included in out), duration 353.9 s, cost 0.0667 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 5, tokens in 80090 / out 1155, cost 0.0051 USD (reported), build file judge `jev-1.14.0` calls 10, tokens in 18342, cost 0.0008 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.3 s, model_review 331.6 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main aa0602e).<br>
Reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
</sub>
This changes the security boundary between branch-controlled Docker builds and the host, including filesystem containment and image isolation from builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 476369 (cached 424832) / out 2049, 71.4 s, 12 commands; reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c`.
<!-- werkrevisor-codex:61c96e787314c97768dfbf1d98322dc2d587269c -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including filesystem containment and image isolation from builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 476369 (cached 424832) / out 2049, 71.4 s, 12 commands; reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c`.
</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
See docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md
🤖 Generated with Claude Code
🟢 werkrevisor GREEN for
838c9e9, superseded by the result for49978c0🟢 werkrevisor: GREEN / PASS
Gate: approved by werkrevisor; merge waits for 2 open hint(s): reply to each line comment and resolve it, then restart the review or push a commit.
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 62702, duration 736.1 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 553, cost 0.0085 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 11.9 s, model_review 722.6 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
9e6dff6).Reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
werkrevisor: GREEN / PASS for
838c9e9f7497c814a6e5211faa01489bfc519188.The result comment of werkrevisor holds the details.
@@ -113,3 +113,3 @@On context close (e.g. systemd SIGTERM), a `ContextClosedEvent` listener in `BuildExecutor` terminates the process trees of all executing builds and waits (bounded) until their results are persisted as INTERRUPTED — a shutdown is never recorded as FAILED. Builds still queued stay PENDING and start no process. Both are re-enqueued by the watcher's startup recovery; INTERRUPTED therefore publishes as Gitea state `pending`, not `failure` (`GiteaStateMapping`).The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label) — in the build's workspace, from the real paths of the Dockerfile and the context, which must stay inside it, and under Werkator's own tag `werkator-build/<scope>:<inputs>` per repository and branch (one scope for the pull-request target builds), so a branch never replaces another build's image (PR#168) — maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.Writing rule: more than one sentence on a line (werkrevisor writing rule)
Fix the line, or reply why it stays, and resolve this comment.
Writing rule: line longer than 400 characters (1869) (werkrevisor writing rule)
Fix the line, or reply why it stays, and resolve this comment.
Fixed in
49978c0: the line is as on main again; the addition stands in two lines of its own, one sentence wrapped after a comma.Fixed in
49978c0: the line is as on main again; the addition stands in two lines of its own, one sentence wrapped after a comma.New commits pushed, approval review dismissed automatically according to repository settings
Codex review of
838c9e9f7497c814a6e5211faa01489bfc519188@@ -166,0 +204,4 @@repoKey: String,branch: String?,pullRequestTarget: Boolean,): String = if (pullRequestTarget) "$repoKey\npull-request target" else "$repoKey\nbranch ${branch.orEmpty()}"[high] Image scopes collide for distinct repository paths (Codex review)
The scope uses ArtifactKeys.repoKey, which replaces path separators and other characters with underscores. Thus /srv/repos/team/service and /srv/repos/team_service produce identical scopes for the same branch. With identical Dockerfile contents and configured paths, they also produce identical image tags, even when their configured docker.image names differ. Because context file contents are excluded from the inputs hash, the second repository accepts the first repository's image and runs its copied files. Hash the original repository path rather than its lossy sanitized key.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
2e2ae5b: the scope hashes the repository's path as it is,repoDir.toAbsolutePath().normalize(), no longerArtifactKeys.repoKey. Test:DockerBuildRunnerUnitTest, Scenario#168.03, withteam/serviceandteam_service; it fails without the fix.Codex review: critical
This changes the security boundary between branch-controlled Docker builds, host files, and builds holding secrets. A mistake could expose credentials or execute an attacker-controlled image, so human review is required.
Findings
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:207, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 330606 (cached 264448) / out 2339, 74.4 s, 13 commands; reviewed head `838c9e9f7497c814a6e5211faa01489bfc519188`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟠 werkrevisor ORANGE for
49978c0, superseded by the result for002f698🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
838c9e9not carried over: 1 approved block(s) are missing- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[medium] Shared Target Image at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:203-207, ORANGE, as a line comment[low] Misleading Image Comment at
src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:287, hint, as a line commentClassifier
9 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63131, calls 1, tokens in 60374 / out 34990 (reasoning included in out), duration 330.9 s, cost 0.0601 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8486, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2292, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 66187 / out 893, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14688, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 15.0 s, model_review 314.1 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
9e6dff6).Reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
werkrevisor: ORANGE / FINDINGS for
49978c00fe8e588b07ba99f28a30973496aa7a08.The result comment of werkrevisor holds the details.
@@ -166,0 +200,4 @@* has a scope of its own — it runs the base commit with the host's configuration, which no branch* writes, while its head branch's builds run whatever that branch says.*/private fun imageScope([medium] Shared Target Image (werkrevisor)
ORANGE; Jev: real 0.85, severity 3.0 of 3
The pull-request-target scope is per repository only, so every pull-request-target build of that repository shares one image, while such a build runs in a fresh workspace of the base commit the forge reports. Two pull requests into different base branches (e.g.
mainandrelease) whose Dockerfiles are identical therefore produce the same inputs hash and the same tag, and the second build runs an image that was baked from the first base branch's context files: the staleness check only covers the Dockerfile content and the two path strings, never the context contents. Include the base branch or base commit in the pull-request-target scope (or hash the context) so a build never runs another base branch's files.Fixed in
2e2ae5b: a pull-request target's scope holds its base branch (PullRequestTarget.BASE_REF_ENV), so builds intomainandreleasenever share an image. Per base branch rather than per base commit, as a branch scope keeps its image across its own commits. Test:DockerBuildRunnerUnitTest, Scenario#168.03; it fails without the fix.@@ -285,3 +285,3 @@image: "" # image for the build container; required when enableddockerfile: "" # Dockerfile to (re)build the image from when missing or stale; empty pulls the image as-iscontext: "." # Docker build context used with dockerfilecontext: "." # Docker build context used with dockerfile; both relative to the workspace, never '..'[low] Misleading Image Comment (werkrevisor)
hint
The generated template still calls
docker.imagethe "image for the build container", but with adockerfileset the build now runs the image underwerkator-build/<scope>:<inputs>anddocker.imageis only written into the labelorg.werkator.image(asdocs/configuration.mdand the release notes state). The template should say that, otherwise an operator reading the generated config expectsimageto name the image the build actually runs in.Fixed in
2e2ae5b: theinittemplate anddocs/configuration.mdsay that withdockerfileset,imageonly labels the image Werkator builds.Codex review of
49978c00fe8e588b07ba99f28a30973496aa7a08@@ -69,3 +70,3 @@val repoKey = ArtifactKeys.repoKey(repoDir)cleanupStaleContainersOnce(repoKey, repoDir)ensureImage(docker, repoDir, onAuxProcess)val image = ensureImage(docker, workingDir, imageScope(repoKey, environment["branch"], pullRequestTarget), onAuxProcess)[high] Repository image scopes collide after path sanitization (Codex review)
ArtifactKeys.repoKey replaces separators and other characters with underscores: /srv/repos/team/repo and /srv/repos/team_repo both become _srv_repos_team_repo. With the same branch, Dockerfile and configured paths, these repositories now receive the same image tag, even when their configured docker.image names differ. Since context contents are not hashed, the second repository runs the first repository’s image. Hash the unsanitized repository identity when constructing the scope.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed already in
fb9da57, after the head this review saw: the scope hashesrepoDir.toAbsolutePath().normalize(), notArtifactKeys.repoKey. Test:DockerBuildRunnerUnitTest, Scenario#168.03, withteam/serviceandteam_service.@@ -133,1 +144,4 @@val context = containedPath(root, "docker.context", docker.context)val dockerfileHash = DockerImageInputs.dockerfileSha256(dockerfile)val inputsHash = DockerImageInputs.inputsSha256(dockerfileHash, docker.dockerfile, docker.context)val image = DockerImageInputs.builtImage(scope, inputsHash)[medium] Reverting a Dockerfile can resurrect an outdated build context (Codex review)
The new tags retain an image for each historical Dockerfile hash, but the hash excludes files copied from the context. Build Dockerfile A with script version 1, change to Dockerfile B, then restore A while updating the script to version 2: inspection finds the old A tag and skips rebuilding, so the image still contains version 1. Previously B replaced the configured tag, so restoring A triggered a rebuild. Historical image reuse must account for context changes.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
fdf8f1d: one tag per scope anddocker.image,werkator-build/<scope>:current, never one per inputs hash; the inputs hash stays in the label and decides the rebuild, as before. A restored Dockerfile finds the tag replaced and is built again. Test:DockerBuildRunnerUnitTest, "a changed Dockerfile replaces the branch's image rather than keeping one per version"; it fails with the tag per inputs hash.Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including image isolation for builds holding secrets. Mistakes could expose credentials or execute another repository’s code.
Findings
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72, as a line commentsrc/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:147, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 330727 (cached 260864) / out 2412, 89.2 s, 14 commands; reviewed head `49978c00fe8e588b07ba99f28a30973496aa7a08`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.2e2ae5be6ato002f698cfe🟡 werkrevisor YELLOW for
002f698, superseded by the result forfdf8f1d🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Classifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 63460, duration 744.5 s, cost unknown, classifier `jev-1.14.0` calls 10, tokens in 8519, cost 0.0004 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67135 / out 916, cost 0.0079 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 14979, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.1 s, model_review 723.0 s, config: defaults + host + repository (.werkrevisor.toml at main4 answers ignored because of a confidence below [classifier] min_confidence.
7fabc11).Reviewed head `002f698cfe190bd472050274eaad43445088ba7c` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
Codex review of
002f698cfe190bd472050274eaad43445088ba7c@@ -69,3 +70,3 @@val repoKey = ArtifactKeys.repoKey(repoDir)cleanupStaleContainersOnce(repoKey, repoDir)ensureImage(docker, repoDir, onAuxProcess)val image = ensureImage(docker, workingDir, imageScope(repoDir, environment, pullRequestTarget), onAuxProcess)[high] Pull-request target images can execute an attacker-controlled Dockerfile (Codex review)
Passing workingDir to ensureImage also moves target builds' Dockerfiles from the primary checkout to the PR's base branch. PullRequestTargets accepts any same-repository base branch, without checking protection or a trusted allowlist; ConfigLoader even rejects branch selectors for these triggers. When a target definition uses dockerfile, a contributor can create a malicious base branch and open a PR into it. Its Dockerfile can install an ENTRYPOINT that reads the host-configured docker.env secrets when runArgs starts the container. The separate image scope prevents tag collisions but does not establish that the base branch is trusted.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
71a2385: a pull-request target build builds no image any more.ConfigLoader.checkPullRequestTargetSandboxrefusesdocker.dockerfileon such a definition, also inherited frombuilds.default, andDockerBuildRunner.startrefuses it again; the build runs in the host'sdocker.imageas it is. The image scope per base branch is gone with it.The older gap stays: a target build whose command runs files of its workspace, e.g.
./gradlew, runs those of any base branch. That needs a host list of allowed base branches and itspull_request_targetcounterpart; it is listed as a follow-up in the PR document.Tests:
PullRequestTargetConfigUnitTestandDockerBuildRunnerUnitTest, Scenario#168.04; both fail without the fix.Codex review: critical
This changes the security boundary between branch-controlled Docker inputs and host files or secrets available to trusted builds. Mistakes can expose credentials or compromise review gates, so human review is required.
Findings
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:72, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 504674 (cached 423168) / out 2497, 87.8 s, 15 commands; reviewed head `002f698cfe190bd472050274eaad43445088ba7c`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟠 werkrevisor ORANGE for
fdf8f1d, superseded by the result for71a2385🟠 werkrevisor: ORANGE / FINDINGS
Gate: no human review requested; merge conflict with main: the author rebases the branch onto main and pushes.
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[high] Stale Image at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:145-155, ORANGE, as a line comment[high] Target Collision at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:206-216, ORANGE, as a line commentClassifier
10 of 10 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 63943, calls 1, tokens in 61159 / out 44681 (reasoning included in out), duration 447.4 s, cost 0.0719 USD (reported), classifier `jev-1.14.0` calls 10, tokens in 8587, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 4881, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 67766 / out 521, cost 0.0058 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15362, cost 0.0006 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 431.0 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
7fabc11).Reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27` against base `7fabc116005b848038e10946b6e2fe43e00111a5`, werkrevisor 0.66.0.
werkrevisor: ORANGE / FINDINGS for
fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27.The result comment of werkrevisor holds the details.
@@ -133,0 +142,4 @@val root = workspace.toRealPath()val dockerfile = containedPath(root, "docker.dockerfile", docker.dockerfile)val context = containedPath(root, "docker.context", docker.context)val dockerfileHash = DockerImageInputs.dockerfileSha256(dockerfile)[high] Stale Image (werkrevisor)
ORANGE; Jev: real 0.89, severity 3.0 of 3
The new branch-scoped tag is reused whenever the inputs label matches, but
inputsHashcovers only the Dockerfile bytes and the two configured path strings. If a later commit changes files indocker.contextwhile keeping the Dockerfile unchanged,docker buildis skipped anddocker runuses the image containing the previous commit's files, which is common withCOPY . .. The freshness decision must also account for the context contents or rebuild using Docker's cache on every run.Unchanged on purpose: this is the staleness rule Werkator always had (legacy
ensure_docker_build_image). Before this PR the one tagdocker.imagewas skipped on the same inputs label in the same way, so a commit that changed only context files ran the old image too. This PR moves where the image is built and under which tag, not when it is rebuilt.Within one branch's scope the stale files are that branch's own, so no trust boundary is crossed. Rebuilding on every run, or hashing the context, changes build time for every Docker build and is a decision of its own; I leave it to a follow-up.
@@ -164,0 +203,4 @@* the host's configuration, which no branch writes, while its head branch's builds run whatever that* branch says, and the inputs hash does not cover the context's files, which differ between bases.*/private fun imageScope([high] Target Collision (werkrevisor)
ORANGE; Jev: real 0.80, severity 3.0 of 3
The scope of a pull-request target contains only the repository path, base branch, and
docker.image; it does not contain the target build identity or base SHA. Two target definitions on the same base that inherit the same image name but use different Dockerfiles or contexts therefore get the same tag. Since target builds are serialized per head branch and build name, those definitions can run concurrently, and onedocker buildcan replace the image the other starts. Include the build identity and base commit in the scope or cache key.Gone with
71a2385: a pull-request target build builds no image any more (Codex finding 7806), so there is no target scope left to collide;imageScopetakes the repository path and the branch only.Codex review: critical
This changes security boundaries governing host filesystem access during Docker builds and image isolation from privileged pull-request target builds. A mistake could expose host credentials or execute branch-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 336702 (cached 265856) / out 1606, 70.3 s, 6 commands; reviewed head `fdf8f1d87f1a3c0757a4ff935a7fff6bbd3b2e27`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.fdf8f1d87fto71a2385fc8🟡 werkrevisor YELLOW for
71a2385, superseded by the result ford2a6457🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[low] Version mismatch at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code was bumped to 1.43.10 and the new entry in
src/main/resources/templates/releases.htmlis v1.43.10. The PR document is the mandatory per-PR record and must agree with the code it describes, so please correct the version there (the PR title carries the same outdated version).[low] Shared image scope at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:151-152, hint, as a line commentClassifier
10 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 66062, calls 1, tokens in 63135 / out 45913 (reasoning included in out), duration 487.4 s, cost 0.0740 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9794, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72564 / out 831, cost 0.0065 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15724, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 17.8 s, model_review 467.9 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.66.0.
werkrevisor: YELLOW / HUMAN_REVIEW for
71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040.The result comment of werkrevisor holds the details.
@@ -133,1 +148,4 @@val context = containedPath(root, "docker.context", docker.context)val dockerfileHash = DockerImageInputs.dockerfileSha256(dockerfile)val inputsHash = DockerImageInputs.inputsSha256(dockerfileHash, docker.dockerfile, docker.context)// with the name of the image, so two definitions of one branch keep an image each[low] Shared image scope (werkrevisor)
hint
The tag of a built image is derived from the repository path, the branch and
docker.imageonly. Two build definitions of one branch that share the samedocker.image(the usual case, when both inherit it frombuilds.default) but build from different Dockerfiles therefore share the tagwerkator-build/<scope>:current: every build replaces the image of the other definition and has to rebuild it, and the claim in this comment and in the PR document that "two definitions of one branch keep an image each" does not hold. Including the build definition's name in the scope would give every definition its own image.Fixed in
d2a6457: the scope holdsdocker.dockerfileanddocker.contextbesidedocker.image, so two definitions of one branch keep an image each unless they build the same; a changed file under the same path still replaces the image. Test:DockerBuildRunnerUnitTest, Scenario#168.03, with a second Dockerfile under the same image; it fails without the fix.Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including path containment and image isolation. A mistake could expose host credentials or let a branch poison images used by privileged builds, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 264761 (cached 198016) / out 1761, 60.9 s, 11 commands; reviewed head `71a2385fc817f3e5a8cd553ed1bcd3db2f1a6040`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟡 werkrevisor YELLOW for
d2a6457, superseded by the result for5307c46🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Gate: the approval of mi came before this result; review requested again, please approve again after reading it; merge already scheduled.
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[medium] Stale Version at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:120hint; Jev: real 0.89, severity 1.5 of 3
The PR document states
Version 1.43.8, whilebuild.gradle.ktsand the new release-notes entry both use1.43.10.The mandatory PR snapshot therefore identifies a release that this PR does not produce.
Update the PR document and PR title to the version actually shipped.
Classifier
10 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65676, calls 1, tokens in 62781 / out 52926 (reasoning included in out), duration 457.6 s, cost 0.0823 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9780, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2415, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 745, cost 0.0022 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 13.3 s, model_review 442.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
Codex review: critical
This changes the security boundary for branch-controlled Docker builds: host filesystem access, image isolation between branches, and images used by builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 353816 (cached 301696) / out 2202, 68.5 s, 13 commands; reviewed head `d2a64572c52565e1615b749baad2dc4e1a717f4a`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.New commits pushed, approval review dismissed automatically according to repository settings
🟠 werkrevisor ORANGE for
5307c46, superseded by the result for9d41074🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[high] Format Break at
src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:205-232, ORANGE, as a line comment[medium] Stale Image at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:153-163, ORANGE, as a line comment[low] Stale Version at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:121hint
The Solution section says the PR is version 1.43.8, while
build.gradle.ktsand the new release-notes entry are version 1.43.10. Update the PR document so it identifies the version that will actually be shipped.Classifier
10 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65741, calls 1, tokens in 62839 / out 51731 (reasoning included in out), duration 449.1 s, cost 0.0809 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9802, cost 0.0004 USD, finding judge `jev-1.14.0` calls 2, tokens in 5289, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72820 / out 732, cost 0.0011 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15903, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 14.7 s, model_review 432.7 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
werkrevisor: ORANGE / FINDINGS for
5307c468655e09eafebaca4d250ae7d44a346f45.The result comment of werkrevisor holds the details.
@@ -133,1 +150,4 @@val inputsHash = DockerImageInputs.inputsSha256(dockerfileHash, docker.dockerfile, docker.context)// with what the definition builds from, so two definitions of one branch keep an image each// unless they build the same; a changed file under the same path still replaces the imageval image =[medium] Stale Image (werkrevisor)
ORANGE; Jev: real 0.70, severity 2.9 of 3
The tag scope includes the mutable
docker.dockerfileanddocker.context, so changing either retains the old scoped tag. If a later commit switches it back, that old tag still has a matching inputs label; because context files are not included in the label, the runner can reuse an image containing the old context even though those files changed in between. Include sufficient commit or context identity in the freshness decision, or overwrite and remove the previous scope, and add an A-to-B-to-A regression test.Fixed in
9d41074: the scope is the repository path, the branch anddocker.imageagain, never the Dockerfile or the context. Switching from A to B and back to A finds the one tag replaced by B and builds A again. Two definitions of one branch under the same image name replace each other's image; that costs a rebuild, as before this PR, and is never stale. Test:DockerBuildRunnerUnitTest, "a changed Dockerfile replaces the branch's image rather than keeping one per version", now with A, B and the first Dockerfile again; it fails with the scope of the previous commit.@@ -203,9 +203,37 @@ class ConfigLoader(yaml.convertValue(resolveBuildSections(dropNonDefinitionBuilds(raw), missingPredecessor), WerkatorConfig::class.java)}checkArtifactDirs(config)[high] Format Break (werkrevisor)
ORANGE; Jev: real 0.76, severity 3.0 of 3
This makes configurations that were valid in 1.43.9 invalid by rejecting absolute Docker paths and paths containing
.., butConfigVersions.FORMAT_BROKE_INis not advanced for this release. A file declaringwerkator.version.since: 1.43.9therefore passes version enforcement and fails later with a path error instead of being rejected through the version and migration guard. Advance the format-break marker and keep the version-dependent artifacts synchronized, as required by the configuration-version invariant.No change, on purpose.
ConfigVersions.FORMAT_BROKE_INrefuses every file whosewerkator.version.sincelies below it (ConfigVersions.verdict:since < broke && version >= broke). Setting it to 1.43.10 would refuse every existing configuration that declares asince, including the committed.werkator.ymlof every branch, so every build would fail after the deployment. The marker is for a change of the format as a whole; AGENTS.md keeps it for the removal ofbranchesandbwrap.This PR refuses only absolute paths and paths with
..indocker.dockerfileanddocker.context, which a host or a branch could only use to reach outside the workspace. The message names the key and the path. PR#167 refused such entries inartifactDirsthe same way.Codex review of
5307c468655e09eafebaca4d250ae7d44a346f45@@ -134,0 +152,4 @@// unless they build the same; a changed file under the same path still replaces the imageval image =DockerImageInputs.builtImage("$scope\nimage ${docker.image}\ndockerfile ${docker.dockerfile}\ncontext ${docker.context}",[high] Restoring a Dockerfile path can resurrect a stale image (Codex review)
Including Dockerfile and context paths in the tag preserves separate historical images. If one definition switches from Dockerfile.a to Dockerfile.b and then back to unchanged Dockerfile.a, while files copied by Dockerfile.a have changed, the original image's inputs label still matches because context contents are not hashed. The build silently runs with the old copied files. Previously, Dockerfile.b replaced the same tag, so switching back triggered a rebuild. The added test covers changing Dockerfile contents at one path, but misses this path-switch sequence.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
9d41074, together with werkrevisor's finding 7869: the scope holds neither the Dockerfile nor the context path any more, only the repository path, the branch anddocker.image. Switching fromDockerfiletoDockerfile.docsreplaces the one tag, so switching back builds again. The test "a changed Dockerfile replaces the branch's image rather than keeping one per version" now covers this path-switch sequence and fails with the previous scope.Codex review: critical
This changes the security boundary between branch-controlled Docker builds and host files, shared images, and secret-bearing pull-request target builds. A mistake could expose credentials or execute attacker-controlled code with secrets, so human review is required.
Findings
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:155, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 385951 (cached 322176) / out 2717, 90.7 s, 15 commands; reviewed head `5307c468655e09eafebaca4d250ae7d44a346f45`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟠 werkrevisor ORANGE for
9d41074, superseded by the result for0852f41🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[high] Stale Image at
src/main/kotlin/de/javagil/werkator/build/DockerBuildRunner.kt:149-160, ORANGE, as a line comment[low] Stale Version at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:123hint
The PR document says the release is version 1.43.8, but the changed
build.gradle.ktsand the top release-notes entry identify 1.43.10. The PR title also repeats 1.43.8. Update the document and title so the mandatory PR record identifies the release this change actually creates.Classifier
11 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 65075, calls 1, tokens in 62226 / out 47187 (reasoning included in out), duration 433.0 s, cost 0.0753 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9782, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2537, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 72792 / out 1131, cost 0.0024 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 15864, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 19.3 s, model_review 411.9 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
werkrevisor: ORANGE / FINDINGS for
9d4107488f87b30fe92a750f0076203fecd6ebae.The result comment of werkrevisor holds the details.
@@ -133,0 +146,4 @@val root = workspace.toRealPath()val dockerfile = containedPath(root, "docker.dockerfile", docker.dockerfile)val context = containedPath(root, "docker.context", docker.context)val dockerfileHash = DockerImageInputs.dockerfileSha256(dockerfile)[high] Stale Image (werkrevisor)
ORANGE; Jev: real 0.74, severity 2.9 of 3
The branch-scoped image is reused whenever the label derived from the Dockerfile contents and configured path strings matches. If a later commit on the same branch changes only files under
docker.context, such as an application archive copied by the Dockerfile, the label remains unchanged anddocker buildis skipped. The container then runs the previous commit's image, which can produce a false-green build. Include the committed context or build identity in the cache validity, or always invoke Docker's content-aware build cache.Fixed in
0852f41, as decided by mi:docker buildnow runs before every command, no longer only when the inputs label changed. Docker's layer cache sees every changed file of the context and keeps an unchanged build short; the label only records the inputs. Test:DockerBuildRunnerUnitTest, "builds the image again although its inputs label matches, so a changed context file is never stale"; it fails with the previous runner.Codex review: critical
This changes the security boundary between branch-controlled Docker inputs and the host, including image isolation for builds holding secrets. A mistake could expose host credentials or execute an attacker-controlled image in a privileged build, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 378551 (cached 335488) / out 1959, 71.4 s, 16 commands; reviewed head `9d4107488f87b30fe92a750f0076203fecd6ebae`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟠 werkrevisor ORANGE for
0852f41, superseded by the result for61c96e7🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[medium] Unused Docker path aborts the start at
src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:216-235, ORANGE, as a line comment[low] Wrong version in the PR document at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126hint
The document states "Version 1.43.8 with a release-notes entry", but
build.gradle.ktswas bumped to 1.43.10 andreleases.htmlopens with v1.43.10 (the PR title still says v1.43.8). The PR document therefore names a version that was never released and contradicts the code and the release notes; it should say 1.43.10.[low] Stale Dockerfile comment at
src/main/kotlin/de/javagil/werkator/commands/InitCommand.kt:285-287, hint, as a line commentClassifier
11 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 67917, calls 1, tokens in 64894 / out 32664 (reasoning included in out), duration 292.7 s, cost 0.0586 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `jev-1.14.0` calls 1, tokens in 2717, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 4, tokens in 73030 / out 737, cost 0.0060 USD (reported), build file judge `jev-1.14.0` calls 9, tokens in 16568, cost 0.0007 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 16.6 s, model_review 274.4 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `0852f41182355955b68cb0ed7062b2ae53243159` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
werkrevisor: ORANGE / FINDINGS for
0852f41182355955b68cb0ed7062b2ae53243159.The result comment of werkrevisor holds the details.
@@ -113,3 +113,3 @@On context close (e.g. systemd SIGTERM), a `ContextClosedEvent` listener in `BuildExecutor` terminates the process trees of all executing builds and waits (bounded) until their results are persisted as INTERRUPTED — a shutdown is never recorded as FAILED. Builds still queued stay PENDING and start no process. Both are re-enqueued by the watcher's startup recovery; INTERRUPTED therefore publishes as Gitea state `pending`, not `failure` (`GiteaStateMapping`).The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it (re)builds the configured image when the Dockerfile inputs changed (tracked via the `org.werkator.build-inputs-sha256` image label), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.The runtime is selected per build behind the `BuildRunner` interface: `DispatchingBuildRunner` (`@Primary`) routes to native `ProcessBuildRunner` (the default), to `DockerBuildRunner` when `docker.enabled`, or to `WerkdockBuildRunner` when `werkdock.enabled` — docker and werkdock are mutually exclusive per build and rejected in `buildSettings`, never picked silently. The Docker runner shells out to the `docker` CLI (no SDK): it builds the configured image before every command (the `org.werkator.build-inputs-sha256` image label records its inputs), maintains a per-repo Gradle cache volume, mounts the worktree and the Docker socket into a labelled (`org.hoennig.werkator`) `--rm --init` container (with `TESTCONTAINERS_HOST_OVERRIDE` from `docker.env` if it names one, else the child address a rootless daemon's rootlesskit reports for the `slirp4netns` port driver — `RootlessKitPorts`, asked over `dockerd-rootless/api.sock` on every build, PR#86 — else `localhost` under `network: host` and `host.docker.internal` otherwise), and repairs workspace ownership in-container after each command (under a rootless daemon the container runs as root, which is the host user, and the repair degenerates to `0:0`). Git works inside the container: the primary `.git` is mounted read-only with `.git/werkator/` masked by an empty tmpfs (credential isolation) and the worktree's admin dir mounted read-write (`gitMetadataMounts`). The returned `Process` is the attached `docker run` client, so log streaming and termination work exactly like native builds.Writing rule: more than one sentence on a line (werkrevisor writing rule)
Fix the line, or reply why it stays, and resolve this comment.
Writing rule: line longer than 400 characters (1552) (werkrevisor writing rule)
Fix the line, or reply why it stays, and resolve this comment.
Fixed in
61c96e7: the line is as on main again; the change stands in lines of its own after it.Fixed in
61c96e7: the line is as on main again; the change stands in lines of its own after it.@@ -283,3 +283,3 @@docker:enabled: false # run clean/build in a container instead of natively (pinned)image: "" # image for the build container; required when enabledimage: "" # image for the build container, required when enabled; with dockerfile only a label of the image Werkator builds[low] Stale Dockerfile comment (werkrevisor)
hint
The generated configuration template still says the image is rebuilt "when missing or stale"; the same sentence is in
docs/configuration.md. Since this PR,ensureImagerunsdocker buildbefore every command, so an image is never kept because it is missing or stale — the inputs label only records what the image was built from. Both texts should describe the new rule, otherwise operators read a rule the code no longer follows.Fixed in
61c96e7: theinittemplate,docs/configuration.mdand the KDoc ofDockerConfig.dockerfilesay the image is built from it before every command.@@ -209,0 +213,4 @@* branch's behalf. Checked on the bound configuration, so every layer and definition is covered:* a branch's path fails that branch's builds, a host's the start.*/private fun checkDockerBuildPaths(config: WerkatorConfig) {[medium] Unused Docker path aborts the start (werkrevisor)
ORANGE; Jev: real 0.84, severity 3.0 of 3
checkDockerBuildPathsrefusesdocker.dockerfileanddocker.contextfor every branch and build definition, even for a definition whosedocker.enabledis false or whosedockerfileis empty. Those values are only read byensureImage, which returns early when no Dockerfile is configured, so no directory is ever sent to the daemon. A host that keeps an absolute or..context beside a native build now fails to start with a config-format error although nothing about that build changed. Restrict the check to definitions that actually build an image (Docker enabled and a Dockerfile set), or keep the old value for the unused case.Fixed in
61c96e7:checkDockerBuildPathschecks the two paths only where Docker is enabled and a Dockerfile is set, which is whereensureImagereads them. Test:ConfigLoaderUnitTest, "a Docker build path is not checked where no image is built"; it fails without the fix.Codex review: critical
This changes security boundaries governing host filesystem access during Docker image builds and image isolation between branches and secret-bearing pull-request target builds. A mistake could expose host credentials or execute attacker-controlled code with secrets, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 505512 (cached 440576) / out 1937, 62.7 s, 13 commands; reviewed head `0852f41182355955b68cb0ed7062b2ae53243159`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind:
mixed.Reasons
- [ ] TODO.images: images of branches that are gone stay until 'docker image prune'; Werkator does not remove them yet.Findings
[low] Stale version number at
docs/prs/2026-10-24-PR#168-docker-image-from-workspace.md:126hint
The PR document states "Version 1.43.8 with a release-notes entry", but the code bumps
versionto1.43.10inbuild.gradle.ktsand the new entry inreleases.htmlisv1.43.10; the PR title carries the same wrong number. The version is what identifies a running instance, so the snapshot must name the version that actually ships.Classifier
11 of 11 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 68624, calls 1, tokens in 65571 / out 39184 (reasoning included in out), duration 353.9 s, cost 0.0667 USD (reported), classifier `jev-1.14.0` calls 11, tokens in 9867, cost 0.0004 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 5, tokens in 80090 / out 1155, cost 0.0051 USD (reported), build file judge `jev-1.14.0` calls 10, tokens in 18342, cost 0.0008 USD, phases: path_judge 0.5 s, classifier 0.5 s, context 20.3 s, model_review 331.6 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
aa0602e).Reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c` against base `aa0602e5c32d159f0113416e960c8a991bba949f`, werkrevisor 0.69.0.
Codex review: critical
This changes the security boundary between branch-controlled Docker builds and the host, including filesystem containment and image isolation from builds holding secrets. A mistake could expose host credentials or execute attacker-controlled code with those secrets, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 476369 (cached 424832) / out 2049, 71.4 s, 12 commands; reviewed head `61c96e787314c97768dfbf1d98322dc2d587269c`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.