This project uses bd (beads) for issue tracking. Run bd onboard to get started.
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --status in_progress # Claim work
bd close <id> # Complete work
bd sync # Sync with gitWhen ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
All three build scenarios on branch feature/multi-arch-docker-images were tested and passed.
| Scenario | Command | Result |
|---|---|---|
| amd64 only | just build-amd64 |
PASS — image loaded locally |
| arm64 only | just build-arm64 |
PASS — image loaded locally |
| multi-arch | just build-multiarch |
PASS — cached, no --load (expected) |
| Platform | Binary layer | Total image (docker inspect) | Docker images reported |
|---|---|---|---|
| amd64 (UPX) | 8.3MB | 8.62MB | 20.3MB (virtual) |
| arm64 (no UPX) | 22.8MB | 8.33MB | 34.6MB (virtual) |
Binary UPX compression: 23.8MB -> 8.3MB (34.76% of original, 65.24% reduction).
Note: docker images virtual sizes reflect shared layer accounting on the host. The docker inspect .Size values reflect actual uncompressed layer data per image. The amd64 binary layer (8.3MB) is ~2.75x smaller than arm64 binary (22.8MB) due to UPX. The amd64 total image looks marginally larger than arm64 in inspect output because the amd64 distroless base (amd64 arch) includes slightly more data than arm64, but the binary layer dominates.
Both images run without errors:
$ docker run --rm ghcr.io/dictybase/modware-annotation:amd64-test --help
NAME:
modware-annotation - cli for modware-annotation microservice
...
COMMANDS:
start-server, load-ontologies, start-feature-server, help
$ docker run --rm --platform linux/arm64 ghcr.io/dictybase/modware-annotation:arm64-test --help
NAME:
modware-annotation - cli for modware-annotation microservice
...
Both exit with code 0. The amd64 image runs under QEMU emulation on Apple Silicon (arm64 host) with expected platform mismatch warning.
- Tag collision on
--load:build-amd64andbuild-arm64both tag as:latest. Running both sequentially means the last loaded platform overwrites the tag. Use distinct tags (:amd64-test,:arm64-test) for side-by-side comparison. build-multiarchwarning: PrintsWARNING: No output specified with docker-container driver— this is expected behavior. Without--pushor--output, results stay in the buildx cache only. Build completes successfully.- UPX version: Alpine installs UPX 5.0.2 from apk. Compression on amd64 is functional; arm64 skip is correct (arm64 UPX support can be unreliable).
- Builder: Uses
multiarchbuildx builder withdocker-containerdriver. Golang base image is cached from first build, making subsequent builds fast (~15s compile vs ~60s first run).
- Builder:
golang:1.24-alpine(sha256:8bee1901...) - Runtime:
gcr.io/distroless/static:latest(sha256:d90359c7...)
- Always use conventional commit skills (
/ai-commit-gen:commitor/git-commit-smart:commit-smart) before anygit commit - Use simple
-m "type(scope): subject"format (not heredoc) — heredoc bypasses hookify conventional commit checks - Subagents cannot invoke skills directly — main Claude handles commits after subagent implementation
- Create a separate
Dockerfile.multiarchinstead of modifying the existing single-arch Dockerfile when adding multi-arch support - Add
--chmod=755to binaryCOPYinstructions when usinggcr.io/distroless/static:nonrootruntime