Deep Review: 20260430-134246-pr-37
| Date | 2026-04-30 13:42 |
| Repo | rancher-sandbox/rancher-desktop-opensuse |
| Round | 2 (of target) |
| Author | @Nino-K |
| PR | #37 — Add rdd-guest socket bridge for Windows Docker socket forwarding |
| Commits | ed5f52e6a Add rdd-guest socket bridge for Windows Docker socket forwarding |
| Reviewers | Claude Opus 4.7 (effort: xhigh), Codex GPT 5.5 (effort: xhigh), Gemini 3.1 Pro (effort: default) |
| Verdict | Merge with fixes — address the tight Accept loop and the unaddressed .gitignore/godoc/module-drift suggestions before merging. |
| Wall-clock time | 31 min 6 s |
Executive Summary ¶
Round 2 of PR #37. Since round 1 (commit 88abad6e4) the author has addressed every must-fix from that review: the vsock listener now rejects connections whose peer CID is not vsock.Host, the unguarded halfCloser type assertions are gated with comma-ok, the committed ELF binary is gone from git ls-files, the TODO is separated from the halfCloser godoc with a blank line, and handleConn now takes the program's signal-aware ctx (used by the Docker dial). A sync.WaitGroup was also introduced inside pipe().
The current code is materially safer. What remains is one new important finding the round-1 review missed (a tight Accept loop on persistent errors such as EMFILE), one prior-round suggestion that was started-but-not-finished (the WaitGroup is internal to pipe() so the program still abandons in-flight handlers at SIGTERM), one prior-round important whose binary half is fixed but whose .gitignore half is not, and a handful of smaller suggestions: the godoc convention nit @mook-as raised inline, a Moby-engine ExecCondition for service-file consistency with docker.service, dead-code cleanup in the EOF check, a port-contract comment, and the unaddressed Go-toolchain drift between rdd-guest and rd-init.
Structure: 0 critical, 1 important, 7 suggestions, plus design observations on the round-1→round-2 resolution path.
Critical Issues ¶
None.
Important Issues ¶
<-ctx.Done()
l.Close()
}()
for {
conn, err := l.Accept()
if err != nil {
if ctx.Err() != nil {
return
}
log.Printf("rdd-guest: accept: %v", err)
continue
}
go handleConn(ctx, conn)
}
}
// TODO: once rancher-desktop-daemon is public, replace the inlined halfCloser,
When l.Accept() returns a persistent non-shutdown error — EMFILE on file-descriptor exhaustion, ENFILE on system-wide exhaustion, ENOMEM under memory pressure — the loop logs and immediately retries with no backoff. The condition does not self-clear instantly: the kernel still refuses to allocate, so Accept returns the same error microseconds later. The result is a hot CPU loop that pegs a core and floods journalctl with thousands of identical lines per second until something else frees the fd or the process is restarted by an operator.
Restart=on-failure does not help because the process never exits. The bridge runs as root, and LimitNOFILE for systemd services defaults relatively low, so this is a realistic production failure mode for any vsock service that holds a long-lived connection per Docker API call.
Fix: backoff briefly on accept errors so the process yields the CPU and the journal stays readable.
conn, err := l.Accept()
if err != nil {
if ctx.Err() != nil {
return
}
log.Printf("rdd-guest: accept: %v", err)
+ time.Sleep(100 * time.Millisecond)
continue
}
(important, gap)
Suggestions ¶
go func() {
<-ctx.Done()
l.Close()
}()
for {
conn, err := l.Accept()
if err != nil {
if ctx.Err() != nil {
return
}
log.Printf("rdd-guest: accept: %v", err)
continue
}
go handleConn(ctx, conn)
}
}
// TODO: once rancher-desktop-daemon is public, replace the inlined halfCloser,
// pipe(), and handleConn() here with a direct import of pkg/socketbridge, which
// contains the implementations (HalfCloser interface, Pipe function).
The author added a sync.WaitGroup inside pipe() (main.go:99-114), which serializes the two forward goroutines — but the handleConn goroutines launched at line 51 are still untracked at the program level. When SIGTERM arrives, <-ctx.Done() closes the listener, Accept errors, the loop returns, and main exits with active connections mid-io.Copy. The OS reaps their fds, peers see an abortive close instead of the bidirectional half-close drain pipe was carefully written to deliver. Long-running Docker calls (exec, attach, events, logs -f, image pulls) get truncated mid-stream.
}
if *enableContainerd &&
*enableDocker {
log.Fatal("requires either -docker or -containerd but not both.")
}
if err := runAgent(
*enableContainerd, *enableDocker, *enableKubernetes,
*containerdSock, *configPath, *k8sServiceListenerAddr,
*adminInstall, *k8sAPIPort, *tapIfaceIP,
); err != nil {
log.Fatal(err)
}
log.Info("Rancher Desktop Agent Shutting Down")
}
func runAgent(
enableContainerd, enableDocker, enableKubernetes bool,
containerdSock, configPath, k8sServiceListenerAddr string,
adminInstall bool,
k8sAPIPort, tapIfaceIP string,
) error {
groupCtx, cancel := context.WithCancel(context.Background())
defer cancel()
The internal WaitGroup is the right primitive — it just needs to live one scope up.
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
...
+ var wg sync.WaitGroup
for {
conn, err := l.Accept()
if err != nil {
if ctx.Err() != nil {
+ wg.Wait()
return
}
log.Printf("rdd-guest: accept: %v", err)
continue
}
- go handleConn(ctx, conn)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ handleConn(ctx, conn)
+ }()
}
}
A short systemd TimeoutStopSec= in the unit caps the drain so a stuck connection cannot wedge systemctl restart.
(suggestion, gap) — round 1 S5 unaddressed. Re-raised at the same severity per the Step 5 prior-round-suggestions gate. The round-1 framing (Gemini) named the same shape; the author's partial fix (WG inside pipe) suggests the issue was acknowledged.
.gitignore still missing the rdd-guest build artifact — round 1 I1 partially unaddressed Claude Gemini¶
/root/build/
*.qcow2.xz
*.raw.xz
*.tar.xz
The committed binary from round 1 is gone (git ls-files src/rdd-guest/ returns only go.mod, go.sum, main.go), so the runtime concern is resolved. The companion fix — adding the path to .gitignore so a developer's go build inside src/rdd-guest/ does not stage the artifact again — is still missing. Jan's PR comment explicitly called this out as the one item to address.
/root/build/
*.qcow2.xz
*.raw.xz
*.tar.xz
+/src/rd-init/rd-init
+/src/rdd-guest/rdd-guest
(suggestion, gap) — round 1 I1 demoted to suggestion: the binary itself is gone, only the ignore-rule remains.
// Package main is the rdd-guest agent that runs inside the Lima/WSL2 VM.
// It listens on a vsock port and forwards connections to the Docker socket,
// enabling the Windows host to reach /var/run/docker.sock via Hyper-V vsock.
//
package main
import (
"context"
"errors"
"io"
Two issues in the same block. The godoc convention for main packages is // Command <name> is …; src/rd-init/main.go:1 already uses // Command rd-init is a minimal implementation of cloud-init to start lima., so this PR diverges from local style. @mook-as raised this inline (discussion_r3163651500) and it remains unresolved. Separately, rdd-guest.service:4 is gated by ConditionVirtualization=wsl and config.sh:124 enables the service only inside the wsl profile, so "Lima/WSL2" oversells the supported runtime path (round 1 S1).
// Command rd-init is a minimal implementation of cloud-init to start lima.
// Note that this only implements `cloud-init-local`.
package main
import (
"context"
[Unit]
Description=Rancher Desktop Guest Socket Bridge
Documentation=https://github.com/rancher-sandbox/rancher-desktop-daemon
ConditionVirtualization=wsl
[Service]
ExecStart=/usr/local/bin/rdd-guest
Restart=on-failure
RestartSec=5s
# Enable network namespace
systemctl enable network-setup
systemctl enable rancher-desktop-guest-agent.service
systemctl enable wsl-proxy.service
systemctl enable rdd-guest.service
# Do not manage /tmp; that is managed by WSL.
mkdir -p /usr/local/lib/tmpfiles.d
touch /usr/local/lib/tmpfiles.d/fs-tmp.conf
fi
-// Package main is the rdd-guest agent that runs inside the Lima/WSL2 VM.
-// It listens on a vsock port and forwards connections to the Docker socket,
-// enabling the Windows host to reach /var/run/docker.sock via Hyper-V vsock.
-//
+// Command rdd-guest is the agent that runs inside the WSL2 VM. It listens on
+// a vsock port and forwards connections to the Docker socket, enabling the
+// Windows host to reach /var/run/docker.sock via Hyper-V vsock.
package main
(suggestion, gap) — supersedes round 1 S1.
func pipe(a, b halfCloser) {
var wg sync.WaitGroup
forward := func(dst, src halfCloser) {
defer wg.Done()
_, err := io.Copy(dst, src)
if err != nil && !errors.Is(err, io.EOF) {
log.Printf("rdd-guest: copy: %v", err)
}
_ = dst.CloseWrite()
}
wg.Add(2)
go forward(a, b)
io.Copy's contract guarantees that EOF on src is not surfaced as an error: "A successful Copy returns err == nil, not err == EOF, because Copy is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported." So errors.Is(err, io.EOF) is unreachable under the documented contract; any non-nil err here represents a genuine fault (ECONNRESET, EPIPE, write error to a closed dst) worth logging. The check also drags in errors purely for this unreachable branch.
_, err := io.Copy(dst, src)
- if err != nil && !errors.Is(err, io.EOF) {
+ if err != nil {
log.Printf("rdd-guest: copy: %v", err)
}
After the change, the errors import becomes unused — drop it from the import block.
(suggestion, enhancement)
Description=Rancher Desktop Guest Socket Bridge
Documentation=https://github.com/rancher-sandbox/rancher-desktop-daemon
ConditionVirtualization=wsl
[Service]
ExecStart=/usr/local/bin/rdd-guest
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=rancher-desktop.target
config.sh:124 enables rdd-guest.service for every WSL image, but the bridge proxies /var/run/docker.sock — which only exists when CONTAINER_ENGINE=moby. The repo's convention for engine-conditional units is an ExecCondition against ${CONTAINER_ENGINE}, e.g. docker.service.d/conditional.conf:3:
# Enable network namespace
systemctl enable network-setup
systemctl enable rancher-desktop-guest-agent.service
systemctl enable wsl-proxy.service
systemctl enable rdd-guest.service
# Do not manage /tmp; that is managed by WSL.
mkdir -p /usr/local/lib/tmpfiles.d
touch /usr/local/lib/tmpfiles.d/fs-tmp.conf
fi
# Only enable docker if desired
[Service]
ExecCondition=test ${CONTAINER_ENGINE} = moby
ExecCondition=test ${CONTAINER_ENGINE} = moby
buildkitd.service:10-11 and containerd.service.d/conditional.conf:3 follow the same pattern with containerd. In containerd mode today, rdd-guest accepts a host-side connection, dials a non-existent Docker socket, logs a per-connection dial error, and closes — net effect is journal noise, not a functional bug. Adopting the project pattern is the cleaner fix.
Requires=buildkitd.socket
StopPropagatedFrom=rancher-desktop.target
[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/rancher-desktop
ExecCondition=test ${CONTAINER_ENGINE} = containerd
ExecStart=/usr/local/bin/buildkitd \
--oci-worker=false --containerd-worker=true \
--containerd-worker-addr=/run/k3s/containerd/containerd.sock \
--addr fd://
# Only enable containerd if it is the selected container engine.
[Service]
ExecCondition=test ${CONTAINER_ENGINE} = containerd
[Service]
+EnvironmentFile=-/etc/sysconfig/rancher-desktop
+ExecCondition=test ${CONTAINER_ENGINE} = moby
ExecStart=/usr/local/bin/rdd-guest
Restart=on-failure
RestartSec=5s
(suggestion, enhancement)
"syscall"
"github.com/mdlayher/vsock"
)
const (
vsockPort = 6660
dockerSockPath = "/var/run/docker.sock"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
6660 must stay in lockstep with the dial port in rancher-desktop-daemon. Drift between the two ends produces a silent connect-refused on the Windows side with no in-tree breadcrumb pointing at the matching constant. Other vsock ports in this repo (src/go/networking/cmd/host/switch_windows.go:46-47, src/go/networking/cmd/network/setup_linux.go:60-61) at least appear in pairs in the same repo; this one does not. A one-line comment naming the daemon-side counterpart shortens future debugging and survives the eventual pkg/socketbridge import.
virtualSubnet string
staticPortForward arrayFlags
)
const (
vsockListenPort = 6656
vsockHandshakePort = 6669
timeoutSeconds = 5 * 60
)
func main() {
flag.BoolVar(&debug, "debug", false, "enable additional debugging")
}
const (
nsenter = "/usr/bin/nsenter"
unshare = "/usr/bin/unshare"
vsockHandshakePort = 6669
vsockDialPort = 6656
defaultTapDevice = "eth0"
WSLVeth = "veth-rd-wsl"
WSLVethIP = "192.168.143.2"
namespaceVeth = "veth-rd-ns"
namespaceVethIP = "192.168.143.1"
const (
+ // vsockPort must match the dial port used by rancher-desktop-daemon's
+ // socket bridge (cmd/rdd-host).
vsockPort = 6660
dockerSockPath = "/var/run/docker.sock"
)
(suggestion, enhancement)
module github.com/rancher-sandbox/rancher-desktop-opensuse/src/rdd-guest
go 1.23.0
require github.com/mdlayher/vsock v1.2.1
require (
github.com/mdlayher/socket v0.5.1 // indirect
src/rd-init/go.mod:3 declares go 1.25.0 and golang.org/x/sys v0.43.0 (recently bumped in 69c42c26d and 7bc2b6722). Same builder image, same repo, two different toolchain floors. Dependabot raises bumps per module, so the next x/sys advisory will need two PRs instead of one.
module github.com/rancher-sandbox/rancher-desktop-opensuse/src/rd-init
go 1.25.0
require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/coreos/go-systemd/v22 v22.7.0
github.com/goccy/go-yaml v1.19.2
-go 1.23.0
+go 1.25.0
...
- golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
After bumping, run go mod tidy to refresh go.sum.
(suggestion, gap) — round 1 S4 unaddressed.
Design Observations ¶
Concerns ¶
- (future) The inlined
halfCloser/pipe/handleConnare explicitly temporary per the TODO atsrc/rdd-guest/main.go:55-57—pkg/socketbridgefromrancher-desktop-daemonreplaces them once that repo opens. Three of the round-2 findings (S1 graceful shutdown, S4 deadio.EOFcheck, S6 port contract) live entirely inside this temporary code; fixing them is cheap and buys correctness for whatever interim window exists, but a reader might reasonably defer them until the upstream import lands. Worth a tracking issue so the cleanup actually happens. Claude - (in-scope) The systemd unit still has no
After=ordering. Sibling units (wsl-proxy.service,rancher-desktop-guest-agent.service) lack ordering hints too, so this matches local convention. Failure mode is benign — an early connection logs a dial error and the Windows side retries — butAfter=docker.socketwould let the first boot connection succeed cleanly when Moby is selected. Skip if the Windows-side retry already absorbs this. (Carried from round 1; unchanged.)
Strengths ¶
- (in-scope) The peer-CID check at
src/rdd-guest/main.go:72-76correctly enforces "only the Windows host may proxy through this bridge"; the inline comment atmain.go:65-68documents why the check is there (WSL2 vsock-namespace sharing across distros) — a model of "explain the trust assumption, not just the mechanic." Claude Gemini - (in-scope) Comma-ok on both
halfCloserassertions plus a per-side log includingvsockConn.RemoteAddr()makes a futuremdlayher/vsockwrapping change a graceful per-connection drop rather than a process-wide panic. Claude - (in-scope)
pipe()propagates EOF withCloseWrite()per direction rather than tearing both sides down on first EOF — the correct behavior for HTTP-over-Unix Docker traffic with hijacked streams (docker exec -it,docker attach). The internal WaitGroup is now a clean replacement for the chan-of-error pattern. Claude Gemini - (in-scope) Dialing Docker per accepted connection rather than holding a long-lived shared connection means a Docker restart does not require restarting the bridge. Codex
- (in-scope) Defense in depth:
ConditionVirtualization=wslin the unit prevents the bridge from starting on macOS/Lima images even though the binary is copied into/usr/local/binfor all profiles. Codex
Testing Assessment ¶
No tests added. src/rd-init/ continues without a test suite, so this matches existing convention rather than regressing it. Codex ran go test ./... in src/rdd-guest (passes, no test files) and smoke-built for linux/amd64 and linux/arm64. Verification today is by booting a WSL image. Highest-value scenarios if a regression slipped in:
- Vsock peer-CID rejection (highest risk if the round-1 fix regresses): dial from CID 1 (loopback inside the VM) and verify the connection is rejected before
rdd-guestdials docker.sock. - Half-close on hijacked streams:
docker exec -itfrom the host, exercising bothCloseWritepaths inpipe(). - Graceful shutdown under load (covers S1):
systemctl restart rdd-guestwhile adocker logs -fis in flight; the client should see clean EOF, not a connection reset. - Accept under fd pressure (covers I1): exhaust fds in the bridge process and verify it does not hot-loop.
- Engine-aware activation (covers S5): switch to containerd and confirm the bridge does not produce repeated dial-failure entries in the journal.
- Listener restart:
systemctl restart rdd-guestshould rebind port 6660 cleanly with no stale-socket error.
Documentation Assessment ¶
- The package doc at
src/rdd-guest/main.go:1-5still says "Lima/WSL2" and starts withPackage main is the rdd-guest agentrather than the godoc-conventionalCommand rdd-guest is …(S3). - The
Documentation=URL inrdd-guest.service:3still points atrancher-desktop-daemon. Per the author's reply ondiscussion_r3162930815, this is intentional (the daemon side is the more important piece of the bridge); see Acknowledged Limitations. - The
handleConndoc comment atmain.go:65-68is genuinely useful "why" documentation — it captures the WSL2 trust-model reasoning behind the CID check. - No README under
src/rdd-guest/explains why a separate vsock bridge is needed alongsidewsl-proxy.service. The TODO atmain.go:55is a useful pointer to the eventual replacement, but a one-paragraph note for future maintainers would be cheap to add.
Commit Structure ¶
Single commit, message accurate, scope coherent. Nothing to flag.
Acknowledged Limitations ¶
- TODO comment (
src/rdd-guest/main.go:55-57): the inlinedhalfCloser/pipe/handleConnare intentionally temporary; cleanup is tied torancher-desktop-daemonbecoming public sopkg/socketbridgecan be imported. The author is aware that this code will be deleted; depth-of-test investment scales accordingly. Documentation=URL (rdd-guest.service:3, prior-round S3): @mook-as suggested pointing at this repo now that the source lives here. The author replied ondiscussion_r3162930815that the daemon side is the more important piece of the bridge and elected to leave the URL pointing atrancher-desktop-daemon. Treat as resolved by author response; do not re-raise.defer l.Close()thread (discussion_r3157388765→_r3162971660→_r3163247910): the author removed the redundant defer so only the<-ctx.Done()goroutine path closes the listener; the duplicate close path mook-as flagged is gone.- PR-author note (round-1 review response): Jan flagged the binary-in-source-tree as the one real concern from round 1's external review. The binary is removed; the
.gitignorehalf is still open (S2).
Unresolved Feedback ¶
discussion_r3163651500— @mook-as: "That should beCommand rdd-guest is…". Captured as S3.- PR-author note about
.gitignore: not addressed in any subsequent commit. Captured as S2. discussion_r3163274099— @mook-as: "Loggingconn.RemoteAddrhere might be good too. Same with thedockerConnone." The author addedRemoteAddrlogging on theCloseWritecapability-mismatch paths (lines 87, 92), but the Docker-dial-failure log atmain.go:80still does not include the vsock client's address. Minor; not separately flagged. Gemini
Agent Performance Retro ¶
Claude ¶
Caught the round-1 S5 (graceful shutdown) being only half-fixed — the WaitGroup-inside-pipe change is the kind of half-step easy to mistake for a complete fix, and reading both rounds side-by-side was the only way to spot it. Also surfaced the godoc-convention nit @mook-as raised inline (round 1 missed this because the inline review hadn't happened yet) and noticed the .gitignore follow-up was left undone after the binary was removed. Calibration on the graceful-shutdown finding was a half-step hot — Important rather than Suggestion in the original output; consolidated to Suggestion to honor the round-1 prior-round-suggestions gate (it was a Suggestion in round 1 and the surrounding code did not materially change at the program level). Did not catch the tight Accept loop or the dead errors.Is(err, io.EOF) check.
Codex ¶
One finding (S5 — Moby ExecCondition) but a strong one: it required reading three sibling unit files (docker.service.d/conditional.conf, buildkitd.service, containerd.service.d/conditional.conf) to recognise the project pattern and apply it to the new unit. This is the kind of cross-file consistency check git blame-aware agents do well. Verified locally with go test ./... and linux/amd64/linux/arm64 smoke builds, more verification than the other two. Underweight overall — missed the tight Accept loop and the round-1 prior-round suggestions still open in the diff.
Gemini ¶
Surfaced the new Important finding of this round (tight Accept loop on EMFILE/ENFILE) — a real production-stability concern none of the three agents caught in round 1. Also caught the dead io.EOF check, citing the relevant io.Copy contract sentence. The .gitignore finding was raised at Important rather than Suggestion, an over-call — the binary is already removed, so this is a hygiene rule, not a runtime concern. Skipped git blame per its known quota behavior, which did not affect this review since the changed lines are mostly new. Strongest agent on the program-correctness axis again, lighter on documentation, hygiene, and project conventions.
Summary ¶
| Claude Opus 4.7 | Codex GPT 5.5 | Gemini 3.1 Pro | |
|---|---|---|---|
| Duration | 23m 17s | 5m 03s | 2m 56s |
| Findings | 4S | 1S | 1I 2S |
| Tool calls | 44 (Bash 24, Read 20) | 66 (shell 66) | 10 (runshellcommand 7, grepsearch 1, listdirectory 1) |
| Design observations | 3 | 2 | 2 |
| False positives | 0 | 0 | 0 |
| Unique insights | 3 | 1 | 2 |
| Files reviewed | 6 | 6 | 6 |
| Coverage misses | 0 | 0 | 0 |
| Totals | 4S | 1S | 1I 2S |
| Downgraded | 1 (I→S) | 0 | 1 (I→S) |
| Dropped | 0 | 0 | 0 |
Reconciliation:
- Claude raised graceful-shutdown at Important; consolidated to Suggestion (S1) to match round-1 severity per the prior-round-suggestions gate.
- Gemini raised
.gitignoreat Important; consolidated to Suggestion (S2) since the binary itself is removed and only the ignore-rule remains.
Gemini was the most valuable agent for this round — the tight Accept loop is the only round-2 finding that genuinely changes whether the PR is merge-safe under load. Claude provided the broadest cross-round coverage (catching what the round-1→round-2 partial fixes left behind) and surfaced the godoc-convention nit. Codex's pattern-matching across sibling units produced the cleanest engine-gating suggestion, but its single-finding output left several round-1 prior suggestions unflagged.
Review Process Notes ¶
Skill improvements ¶
- Add a review dimension on tight retry loops. When a PR introduces a
for { … }loop that calls a syscall-backed accept/read/recv withcontinueon error, the prompt should ask: does the error class self-clear instantly, and if not, will this hot-loop the CPU?EMFILE,ENFILE,ENOMEM, and persistentEAGAINon a non-blocking listener are the textbook cases. Naming the pattern indimensions.mdraises the chance future agents flag it without having to reason from first principles each time. Why: Two of three agents missed this on PR #37 round 2 even though the loop is six lines and the failure mode (CPU peg + journal flood) is well-known. A named dimension turns a "you have to know to look for it" issue into a "the prompt asks about it" issue.
Repo context updates ¶
- Document the engine-conditional unit pattern. When a systemd unit serves a backend that is engine-gated (
docker/mobyvscontainerd), the convention in this repo isEnvironmentFile=-/etc/sysconfig/rancher-desktopplusExecCondition=test ${CONTAINER_ENGINE} = <engine>. Examples live atdocker.service.d/conditional.conf:3,containerd.service.d/conditional.conf:3, andbuildkitd.service:10-11. Reviewers of new WSL-side units should check whether the new unit serves an engine-specific resource and apply the same pattern; running an engine-specific bridge under the wrong engine produces journal noise and wasted listeners. Why: This pattern is consistent enough across the repo to count as project convention, but it is invisible from a single-file diff. Codifying it lets future reviews flag the gap at Suggestion without re-deriving the pattern from scratch.