| name | devcontainer-workflow |
| description | Bring the devcontainer up/down/build, run tier 1-3 smoke checks, and respect the thin host-user overlay invariant. Use when working with .devcontainer/, mise tasks, or the @devcontainers/cli. |
Devcontainer Workflow
The dotfiles devcontainer has two layers:
- Base image (
ghcr.io/ray-manaloto/dotfiles-devcontainer:dev) — built by
CI from .devcontainer/Dockerfile + .devcontainer/mise-system.toml.
Ships every tool needed inside the container at image-build time.
- Host-user overlay (
.devcontainer/Dockerfile.host-user) — adds the host
user (UID/GID/name) on top of the base image. Must stay ≤ 89 lines
(baseline 79 + 10 cap, enforced by hk step dockerfile_host_user_thin_overlay).
The user-facing workflow is mise run up → work inside → mise run down.
Tasks
mise run build
mise run up
mise run down
mise run stop
mise run test
mise run pre-commit
Smoke checks against a running devcontainer
scripts/devcontainer-smoke.sh
scripts/devcontainer-smoke.sh --include-up
Tiers:
- Tier 1 —
mise ls, which clang++ python uv hk, hk run pre-commit --all
- Tier 2 —
pytest 190/190, stat ~/.ssh ~/.claude /workspaces/dotfiles
- Tier 3 —
clang++ -fsanitize=address,undefined hello.cc && ./hello
Tier 4 (CLion remote toolchain) is manual.
mise run smoke-exec is a complementary containerized real-toolchain
exec gate (tests/test_image_smoke_exec.py, marked image_exec): it runs
the python-generated tier-1/tier-3 smoke cores against the local :dev
image and asserts both the happy path and a tampered FAIL. It's deselected
from the default mise run test (root pytest.ini) and needs Docker + a
current :dev (mise run sync).
Hard rules (DO NOT VIOLATE)
-
Never grow Dockerfile.host-user beyond 89 lines. Add capabilities via
Devcontainer Features or devcontainer.json mounts, never new RUN steps.
The hk step dockerfile_host_user_thin_overlay will block the commit.
-
Never render the chezmoi mise overlay on the Mac host.
home/dot_config/mise/config.toml.tmpl is gated by home/.chezmoiignore
on the built-in chezmoi.os fact: {{ if ne .chezmoi.os "linux" }}.
This is the canonical chezmoi pattern for multi-machine differences (see
.claude/rules/use-tool-builtins.md). Do not reintroduce a custom
is_container data variable or env-var detection — that was reverted in
the C10 refactor. The CI lint job has an "Assert chezmoiignore mise
overlay hard gate" step that machine-checks this on every PR — do not
weaken it. Belt-and-suspenders: .claude/settings.json blocks
chezmoi apply/chezmoi update on the host until Mac integration ships.
See memory feedback_devcontainer_only_mise_overlay.md.
-
Tool installs go in .devcontainer/mise-system.toml (image build) only.
home/.chezmoiscripts/ was deleted in the devcontainer-build refactor —
chezmoi scripts must never install tools. See memory
feedback_chezmoi_scripts_no_tool_install.md.
-
Chezmoi bootstrap lives at onCreateCommand.
.devcontainer/scripts/on-create.sh runs chezmoi init --apply from the
workspace bind-mount (it replaced the old install.sh postCreate
bootstrap). postCreateCommand handles the SSH agent-socket chown +
authorized keys + smoke — keep the chezmoi step at onCreate.
Telemetry
The async image-analysis.yml workflow (triggered on CI success via
workflow_run) emits artifacts/build/devcontainer-metrics.json (benchmark
fields; compressed size now read from the registry manifest, not
docker save | gzip). The PR-blocking smoke-test job runs only smoke +
Dive and no longer emits metrics.
References
mise.toml — [tasks.up], [tasks.stop], [tasks.build], alias down
hk.pkl — dockerfile_host_user_thin_overlay step
.github/workflows/ci.yml — hard-gate assertion (lint job) + tier 1-3 smoke + Dive (smoke-test job)
.github/workflows/image-analysis.yml — async benchmark + Trivy (on CI success)
home/.chezmoiignore — the gate itself
scripts/devcontainer-smoke.sh — shared tier runner