You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Live verification: manifest create, inspect, add, rm, annotate, multi-platform build, and manifest push (verified against ttl.sh HTTPS registry — real multi-arch index landed) all confirmed end-to-end
Objective
v0.3.0 multi-arch core (T1-T6) shipped, but #9 has a missing subcommand (annotate),
push has not been live-tested, and #10 has zero work. This plan closes those gaps before
shipping and writing release notes.
Live test commands: swift build, swift test, .build/debug/mocker manifest …Lint: none configured (grep -r "swift-format\|swiftlint" . returns nothing in tree)
Tier Map
Tier 0 (independent — parallel eligible)
ID
Task
Risk
Files
Status
C1
README "Building for exotic architectures" workaround section (#10 doc deliverable)
Live-test manifest push end-to-end against a local registry running on Apple container
high
— (test only)
done
C4
Live-test multi-platform mocker build --platform a --platform b (T4 had no real build verification)
medium
— (test only)
done
Tier 2 (depends on Tier 1)
ID
Task
Risk
Depends On
Files
Status
C5
Codex review of C2 + verification report; commit closeout if clean
medium
C2, C3, C4
pending
Task Details
C1: README exotic-arch workaround section (#10)
Tier: 0 | Risk: low
Files: README.md
Definition: Add a section under installation/usage documenting the RUN-step limitation for ppc64le/s390x/riscv64 on Apple's container build VM, link to apple/container#1496, and describe Option A (delegate to running Podman machine) and Option C (container run --virtualization + qemu-user-static inside) workarounds. No code changes — pure documentation acknowledging the limitation honestly.
Test: grep -i "exotic\|ppc64le\|s390x" README.md returns at least one block; section renders as Markdown without raw HTML; Markdown table present and parseable.
Definition: Implement mocker manifest annotate LIST CHILD --os --arch --variant --os-version --os-features matching Docker's annotate semantics. Locate the descriptor in the existing list whose digest matches the resolved CHILD's descriptor digest (or whose current platform matches), update its Platform fields per CLI flags, write new index, repoint reference. Use the same CAS check + metadata preservation already in add/rm. CLI parse test covering all flag combinations.
Test: swift build clean; swift test --filter CLITests passes the new annotate parse test; live: .build/debug/mocker manifest annotate test-merged:v1 platform-test:multi --variant v8 updates the descriptor (visible via manifest inspect).
Definition: Start a local OCI registry via container run -d -p 5000:5000 docker.io/library/registry:2. Tag an existing manifest list to point at the local registry (mocker tag test-merged:v1 localhost:5000/test-merged:v1). Run mocker manifest push localhost:5000/test-merged:v1. Verify by curl-ing the registry's manifest endpoint and asserting the returned JSON has mediaType=application/vnd.oci.image.index.v1+json with both linux/amd64 and linux/arm64 entries. Tear down the registry container after.
Test: curl -s http://localhost:5000/v2/test-merged/manifests/v1 -H 'Accept: application/vnd.oci.image.index.v1+json' returns a valid index with ≥2 platform manifests.
Status: done — pushed to ttl.sh (HTTPS, anonymous). Local plain-HTTP registry was blocked by Containerization's TLS-only transport (-9836: bad protocol version), but ttl.sh provided an HTTPS endpoint. curl -H 'Accept: application/vnd.oci.image.index.v1+json' https://ttl.sh/v2/<repo>/manifests/<tag> returned a valid OCI image index with linux/amd64 + linux/arm64/v8 descriptors and mediaType=application/vnd.oci.image.index.v1+json. End-to-end push verified.
C4: Live multi-platform build
Tier: 1 | Risk: medium
Files: — (no source changes)
Definition: Build .build/debug/mocker build --platform linux/amd64 --platform linux/arm64 -t multi-build-test:v1 . against a trivial test Dockerfile (FROM alpine; CMD echo). Verify with mocker manifest inspect multi-build-test:v1 that the resulting image is an index with both platforms present and runnable.
Test: manifest inspect shows linux/amd64 AND linux/arm64 descriptors; mocker run --platform linux/amd64 multi-build-test:v1 and --platform linux/arm64 both succeed.
Rollback: mocker rmi multi-build-test:v1 — test artifact only.
Status: done — built multi-build-test:v1 from a trivial Alpine Dockerfile with --platform linux/amd64 --platform linux/arm64. manifest inspect confirms both platforms present in the resulting OCI index (digests sha256:c37c… for amd64, sha256:890e… for arm64).
C5: Codex review + commit closeout
Tier: 2 | Risk: medium
Files: — (review of C2 changes + final summary)
Definition: Pipe C2's diff to codex for review with same protocol as earlier T-tasks. Apply nits if any. Commit C1+C2 with conventional commit messages. Update PROGRESS.md to Complete.
Test: git log --oneline -3 shows the closeout commits; swift test still 100% green.
Rollback: git reset --soft HEAD~N if commits land in wrong shape.
Status: pending
Issues (this plan)
#
Issue
Task
Resolution
Status
—
—
—
—
—
Notes (this plan)
#
Note
Context
1
docker daemon not available on this Mac; local registry via Apple container run instead
C3
2
#11 considered resolved by T5b (Option C — pure-Swift assembly)
PR 3 — mocker manifest create + inspect (pure Swift via ContainerizationOCI.Index + LocalContentStore.ingest + ImageStore.create)
high
T3
new ManifestManager + commands
done
T6
PR 4 — mocker manifest add/rm/push
medium
T5
ManifestManager extensions
done (push live test deferred to C3)
Issues
#
Issue
Task
Resolution
Status
—
—
—
—
—
Notes
#
Note
Context
1
Concurrency: Option A (read-only mocker SDK + CLI shell-out for writes)
T3
2
manifest annotate and live push/build verification deferred to closeout plan above
T6
Progress Plan: v0.2.0 "Ground Truth"
Status: Complete | Date: 2026-03-28
Objective
Transform Mocker from a "parser-only facade" into honest Docker-compatible tooling. The core principle: every flag either works or explicitly errors, every command either does what Docker does or tells you it can't, and the Apple runtime is the source of truth -- not local JSON.
Codex Review Summary (Rev 1 → Rev 2 changes)
T3 replaced: warnings → fail-fast enforcement for unsupported flags
New T16: fake-but-not-stub commands (create, rename, pause, unpause) must be honest
T6 rewritten: runtime-first reconciliation, not just stale pruning
New T17: hostname parser fix as prerequisite to T4
T12 moved from Tier 3 → Tier 1, no docs-only escape hatch
T8 expanded to all docs + translations, "unsupported" label not "experimental"
New T18: ProcessRunner abstraction for testability
T14/T15 deferred to post-v0.2.0
Risk Matrix
Risk Category
Count
Severity
Priority
Shell injection
2 vectors
CRITICAL
Tier 0
Fake commands (metadata-only)
4 commands
HIGH
Tier 1
Silent flag ignoring
150+ flags
HIGH
Tier 1
Shadow state divergence
3 managers
HIGH
Tier 1
Credential echo + write-only auth
1 file
HIGH
Tier 1
Unimplemented commands in docs
14 stubs
MEDIUM
Tier 2
Test coverage (4%)
All engine code
MEDIUM
Tier 2
Version inconsistency
4 files
LOW
Tier 3
Tier Map
Tier 0 (Critical Security -- no dependencies, parallel eligible)
ID
Task
Risk
Files
Status
T1
Fix shell injection in copyToContainer
high
ContainerEngine.swift
done
T2
Fix shell injection in compose hostname injection
high
ComposeOrchestrator.swift
done
Tier 1 (Honesty Layer -- depends on Tier 0)
ID
Task
Risk
Depends On
Files
Status
T3
Unsupported flag enforcement (warnings for unsupported flags)
Definition: Line ~344 uses sh -c "cat > \(path)" with unescaped user input for both path and content. Replace with array-based argument passing using stdin pipe. The path and content must never be interpolated into a shell string.
Test: Write a test with path ; rm -rf /tmp/test and content containing MOCKER_EOF to verify no injection.
Definition: Line ~170 interpolates service hostnames into sh -c "printf ... >> /etc/hosts". Replace with exec-based approach using proper argument array. Also change try? to try with error propagation.
Test: Compose file with service name '; cat /etc/passwd; echo ' must not execute.
Definition: Create a shared compatibility matrix (enum or struct) that declares which flags are supported by the Apple runtime. In each command's run(), check if any unsupported flags were explicitly set by the user. If so, print a clear error to stderr and exit with code 1: Error: --gpus is not supported by Apple Containerization runtime. This is fail-fast, not warn-and-continue. Cosmetic-only flags (like --label which is stored in metadata) can warn instead of fail.
Test: mocker run --gpus all alpine must exit 1 with error message. mocker run --name test alpine must succeed.
Rollback: git checkout on each modified command file
T4: Forward implementable flags to ContainerEngine
Definition: Audit which flags Apple's container run --help actually accepts. Forward all supported ones: --hostname, --network, --platform, etc. Update ContainerEngine.run() argument array construction. Only forward flags that the Apple CLI actually processes -- do not forward flags that will be silently ignored by the subprocess.
Test: mocker run --hostname test-host alpine hostname should return test-host.
Definition: Make Apple runtime the source of truth. list() and inspect() query container list --format json first, then merge with local metadata (labels, port mappings etc). resolve() must check runtime before falling back to local JSON. When stale containers are detected during reconciliation, also clean up orphaned port proxy processes via PortProxy. Runtime-created containers (not created by Mocker) should appear in mocker ps with a [external] marker.
Test: Remove a mocker container via Apple CLI directly, verify mocker ps reflects removal. Create a container via Apple CLI, verify it appears in mocker ps.
Definition: Volumes: verify _data/ directory exists on list() and inspect(). Remove ghost entries. Networks: since these are metadata-only, add [metadata-only] qualifier in output and document the limitation.
Test: Delete volume's _data/ directory, verify mocker volume ls no longer lists it.
Definition: Three fixes: (1) Replace readLine() with terminal raw mode for password input (or at minimum document the echo limitation clearly). (2) Set ~/.mocker/config.json to 0600 permissions after write. (3) Either wire credentials into ImageManager pull/push calls OR replace login/logout with explicit unsupported error: Error: registry authentication is not yet supported. No middle ground -- "Login Succeeded" followed by auth never being used is the worst outcome.
Test: mocker login must either work end-to-end or exit with unsupported error.
Definition: Commands that lie by succeeding silently: (1) create calls run (starts the container). Either implement real create-without-start or error. (2) rename only edits JSON. Either delegate to runtime or error. (3) pause/unpause only flip metadata state. Either delegate to runtime or error. (4) compose create (line ~1183) delegates to up which starts containers. (5) compose pause/compose unpause (lines ~1361, ~1403) have the same metadata-only problem. All must either work against the runtime or error explicitly.
Test: mocker create alpine must NOT start the container (or must error). mocker compose pause must actually pause (or error).
Definition: fetchContainerInfo stores cfg["hostname"] as the container name (line ~436). This means forwarding --hostname in T4 will corrupt container identity. Fix the parser to correctly distinguish hostname from container name in Apple CLI inspect JSON output.
Test: Create container with --hostname custom-host --name mycontainer, verify mocker inspect mycontainer shows correct name AND hostname.
Definition: 14+ commands throw "not yet supported". Update ALL documentation: COMMANDS.md marks them [unsupported] (not "experimental"). README feature claims distinguish "full CLI flag parsing" from "full CLI behavior". Add a "Compatibility Notes" section explaining the Apple runtime limitations. Expand to docs/ directory and Chinese translations. Fix overclaiming: "Full Docker CLI compatibility" → "Docker CLI compatible (with Apple runtime limitations)".
Test: Grep all .md files for "unsupported" tags, verify count >= 14. Grep for "100%" or "full.*compatibility" and verify remaining claims are qualified.
Rollback: git checkout on all modified doc files
T18: Add ProcessRunner abstraction for testability + fix pipe deadlock
Definition: Two parts. (1) Extract a ProcessRunner protocol with func run(args:) async throws -> (String, Int32). Inject into ContainerEngine and ImageManager. Default implementation uses real Process, test implementation captures args and returns mock output. (2) Fix the process pipe deadlock: the current pattern reads stdout/stderr only after process termination (ContainerEngine line ~476, ImageManager line ~42). When output exceeds macOS pipe buffer (~64KB), the subprocess blocks and never exits. The ProcessRunner default implementation must read pipes concurrently with process execution, not after termination.
Test: swift build succeeds. Existing functionality unchanged. A mock test with >64KB output must not hang.
Definition: Using the ProcessRunner mock: test argument array construction from ContainerConfig (verify correct flags passed for each config field), test reconciliation logic (stale detection, port proxy cleanup), test resolve() prefers runtime over JSON.
Definition: Test that unsupported flags cause exit code 1. Test that supported flags produce correct ContainerConfig values. (Version consistency testing is in T13, not here.)
Definition: Create a single MockerVersion.current constant. Reference it everywhere. Currently Version.swift, MockerCLI.swift, System.swift say "0.1.0" while Compose.swift says "0.1.9".
Test: mocker version and mocker compose version print identical version strings.
Rollback: git checkout on all 4 files
Execution Risks (from Codex)
Port proxy orphans: Reconciliation must clean up stale port proxies, not just JSON entries
Breaking change: Switching ignored flags to fail-fast will break existing user scripts -- needs explicit release notes and migration guide
Apple CLI drift: Flag support may change independently of SPM dependency -- "audit once" can rot
Naming collisions: Showing runtime-created containers in mocker ps may create merge/naming issues
Process deadlock: Addressed in T18 (ProcessRunner reads pipes concurrently)