Problem
Adding a git-sourced dependency (pkg @ git+https://…) to a uv + hatchling + python:3.12-slim service has two non-obvious prerequisites. Both fail late, both have confusing error surfaces, and neither is mentioned in the kit's service scaffolding.
Surfaced while extracting a shared library across three services — it cost a failed build in all three repos, in the same two ways each time. That repetition is why this is a template change rather than three tickets.
1. python:3.12-slim has no git
uv shells out to the git binary to resolve git dependencies. The slim base image ships no VCS tooling, so uv sync --frozen fails inside the container while working fine on the dev machine.
Fix that worked — a multi-stage build, which also drops uv and the apt cache from the shipped image:
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:0.9.28 /uv /bin/uv
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
# ... uv sync --frozen --no-dev ...
FROM python:3.12-slim
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "yourpkg"]
2. hatchling rejects direct URL references by default
ValueError: Dependency #3 of field `project.dependencies` cannot be a direct
reference unless field `tool.hatch.metadata.allow-direct-references` is set to `true`
The confusing part: uv lock succeeds first, so it reads as a resolution problem when it's a build-backend policy. Needs:
[tool.hatch.metadata]
allow-direct-references = true
Why it matters
Neither is discoverable from the error message alone, and #1 in particular fails only in the container — so without a CI image build it surfaces on the deploy host, which is the worst possible place.
Proposed fix
- Add both to the kit's service template / scaffolding docs, gated on "if you use a git dependency".
- Ship the multi-stage Dockerfile as the default template rather than single-stage — it's better regardless (no uv, no apt cache, no git in the runtime image).
- Add a CI
docker job to the template that builds the image and imports the app. That's what turns this class of failure from a deploy-host surprise into a red PR check:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t app:ci .
- run: docker run --rm app:ci python -c "import yourpkg; print('ok')"
Acceptance
Source: docs/friction-log.md 2026-07-23 (topij/brain), entries 1 and 2 — one pattern, observed across three repos.
Problem
Adding a git-sourced dependency (
pkg @ git+https://…) to a uv + hatchling +python:3.12-slimservice has two non-obvious prerequisites. Both fail late, both have confusing error surfaces, and neither is mentioned in the kit's service scaffolding.Surfaced while extracting a shared library across three services — it cost a failed build in all three repos, in the same two ways each time. That repetition is why this is a template change rather than three tickets.
1.
python:3.12-slimhas nogituv shells out to the
gitbinary to resolve git dependencies. The slim base image ships no VCS tooling, souv sync --frozenfails inside the container while working fine on the dev machine.Fix that worked — a multi-stage build, which also drops
uvand the apt cache from the shipped image:2. hatchling rejects direct URL references by default
The confusing part:
uv locksucceeds first, so it reads as a resolution problem when it's a build-backend policy. Needs:Why it matters
Neither is discoverable from the error message alone, and #1 in particular fails only in the container — so without a CI image build it surfaces on the deploy host, which is the worst possible place.
Proposed fix
dockerjob to the template that builds the image and imports the app. That's what turns this class of failure from a deploy-host surprise into a red PR check:Acceptance
Source:
docs/friction-log.md2026-07-23 (topij/brain), entries 1 and 2 — one pattern, observed across three repos.