ca-sandbox duplicates the docker-runner, RunResult type, and MSYS_NO_PATHCONV env guard across ~8 modules with no shared primitive
Severity: medium | Confidence: 0.85 | Effort: M
Where:
plugins/ca-sandbox/tools/exec.ts:44-85
plugins/ca-sandbox/tools/cp.ts:29-40
plugins/ca-sandbox/tools/run.ts:49-91
plugins/ca-sandbox/tools/registry.ts:27-34
plugins/ca-sandbox/tools/create.ts:43-138
plugins/ca-sandbox/tools/claude-inside.ts:68-295
Evidence: The Windows path-mangling guard const DOCKER_ENV = { ...process.env, MSYS_NO_PATHCONV: "1" }; is copy-pasted verbatim (with a near-identical "Spike A/B" comment) in 8 modules: build, claude-inside, cli, cp, create, exec, registry, run. function defaultDockerRun(args) is independently re-defined in 6 of them. The result shape { code, stdout, stderr } recurs under three names: RunResult, ClaudeRunResult, DockerResult. Only cli.ts reuses one. This contrasts with sibling ca/tools/exec.ts, whose header states the opposite discipline: "Every spawn routes through here so the scrub cannot drift between call sites."
Impact: Lost-context duplication across the 13-module plugin. The MSYS_NO_PATHCONV guard is a correctness-sensitive control (on Windows + Git Bash, without it docker paths / -e values are mangled); replicated 8x it must be changed in lockstep, and a copy missed on the next Windows edge case silently reintroduces the mangling in one command path only. Six parallel defaultDockerRun copies mean maxBuffer, error-to-stderr mapping, and exit-code defaulting can drift per module. Three names for one result shape blocks a single typed seam.
Recommendation: Extract a shared ca-sandbox docker primitive module (mirroring ca/tools/exec.ts): one DOCKER_ENV, one defaultDockerRun/RunResult, exported and imported by every command module. Collapse ClaudeRunResult/DockerResult onto the single RunResult. Keep the injectable-runner seam for tests.
Acceptance criteria:
- DOCKER_ENV and the default docker runner are defined once and imported by every ca-sandbox command module
- the {code,stdout,stderr} result shape has a single exported type
- MSYS_NO_PATHCONV has exactly one definition site; ca-sandbox vitest suite stays green
ca-sandbox duplicates the docker-runner, RunResult type, and MSYS_NO_PATHCONV env guard across ~8 modules with no shared primitive
Severity: medium | Confidence: 0.85 | Effort: M
Where:
plugins/ca-sandbox/tools/exec.ts:44-85
plugins/ca-sandbox/tools/cp.ts:29-40
plugins/ca-sandbox/tools/run.ts:49-91
plugins/ca-sandbox/tools/registry.ts:27-34
plugins/ca-sandbox/tools/create.ts:43-138
plugins/ca-sandbox/tools/claude-inside.ts:68-295
Evidence: The Windows path-mangling guard
const DOCKER_ENV = { ...process.env, MSYS_NO_PATHCONV: "1" };is copy-pasted verbatim (with a near-identical "Spike A/B" comment) in 8 modules: build, claude-inside, cli, cp, create, exec, registry, run.function defaultDockerRun(args)is independently re-defined in 6 of them. The result shape{ code, stdout, stderr }recurs under three names:RunResult,ClaudeRunResult,DockerResult. Only cli.ts reuses one. This contrasts with sibling ca/tools/exec.ts, whose header states the opposite discipline: "Every spawn routes through here so the scrub cannot drift between call sites."Impact: Lost-context duplication across the 13-module plugin. The MSYS_NO_PATHCONV guard is a correctness-sensitive control (on Windows + Git Bash, without it docker paths / -e values are mangled); replicated 8x it must be changed in lockstep, and a copy missed on the next Windows edge case silently reintroduces the mangling in one command path only. Six parallel defaultDockerRun copies mean maxBuffer, error-to-stderr mapping, and exit-code defaulting can drift per module. Three names for one result shape blocks a single typed seam.
Recommendation: Extract a shared ca-sandbox docker primitive module (mirroring ca/tools/exec.ts): one DOCKER_ENV, one defaultDockerRun/RunResult, exported and imported by every command module. Collapse ClaudeRunResult/DockerResult onto the single RunResult. Keep the injectable-runner seam for tests.
Acceptance criteria: