Skip to content

fix: refuse a null wherever docker compose refuses one#61

Merged
lesnik512 merged 1 commit into
mainfrom
fix/nested-null-drops
Jul 14, 2026
Merged

fix: refuse a null wherever docker compose refuses one#61
lesnik512 merged 1 commit into
mainfrom
fix/nested-null-drops

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Carries #60's null policy down into every nested and top-level position. Design: planning/changes/2026-07-14.08-nested-null-content-blocks.md.

The principle, which I had wrong

compose2pod is a drop-in replacement for docker compose on rootless runners — the file it converts is the same file the developer runs locally with docker compose up. That makes the asymmetry sharp:

  • Docker refuses, we accept → the document is already broken in the developer's real workflow. Converting it anyway rescues nothing; it means CI goes green on a file docker compose would not run. A false green is worse than a hard error.
  • Docker accepts, we refuse → a working file breaks. That is the direction that must never happen.

So the test is not "does this null drop behavior?" but "would docker compose run this file at all?" If it wouldn't, there is no point emitting a podman script for it.

Eight positions failed that test

Accepted here, refused by docker compose config (v5.1.2), each silently emitting nothing:

  • healthcheck.test, .interval, .timeout, .retries, .start_period
  • deploy.resources, .limits, .reservations
  • top-level networks:, volumes:, secrets:, configs:

Two of them drop real behavior — a bare test: makes the healthcheck evaporate, a bare limits: means you get no memory limits. The rest drop nothing on their own, but they carry a document Docker won't run.

This reverses an earlier ruling — and the reason it has to

2026-07-13.10 ruled that a null healthcheck.timeout/retries/start_period means "unset". I told the maintainer that matched docker compose config. It does not — Docker refuses all three. The ruling was taken on a false premise I supplied, so the conclusion goes with it. The architecture doc contained the same false sentence ("This matches docker compose config"); it is corrected here.

Not parity for its own sake

A null inside a value is untouched and still accepted, because Docker accepts it: environment: {KEY: null} is host-passthrough, labels: {KEY: null} an empty label. compose2pod also keeps its documented divergences elsewhere (it never builds; profiles is closure-authoritative). Parity on refusal is what the drop-in role demands — nothing more.

Verification

All 12 null positions measured against docker compose config, before and after: zero false greens remain, and no over-rejection (host-passthrough, empty labels, absent keys, and command: null all still accepted).

645 tests at 100% coverage; integration green against real podman.

🤖 Generated with Claude Code

compose2pod is a drop-in replacement for docker compose on rootless runners:
the file it converts is the file the developer runs locally. That makes
accepting-what-Docker-refuses actively harmful rather than merely lax -- the
document is already broken in the real workflow, so converting it anyway means
CI goes green on a file docker compose would not run. A false green is worse
than a hard error.

So the test is not "does this null drop behavior?" but "would docker compose
run this file at all?". Eight positions failed it, accepted here and refused by
docker compose config, each emitting nothing:

  healthcheck.test / .interval / .timeout / .retries / .start_period
  deploy.resources / .limits / .reservations
  top-level networks: / volumes: / secrets: / configs:

This reverses changes/2026-07-13.10's null-scalar ruling (a null healthcheck
timeout/retries/start_period meant "unset"). That ruling was taken on the stated
premise that it matched docker compose config. It does not -- Docker refuses all
three. The premise was wrong, so the conclusion goes with it.

A null INSIDE a value is untouched and still accepted, because Docker accepts it
too: environment: {KEY: null} is host-passthrough, labels: {KEY: null} an empty
label.
@lesnik512 lesnik512 merged commit eb7c3a9 into main Jul 14, 2026
7 checks passed
@lesnik512 lesnik512 deleted the fix/nested-null-drops branch July 14, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant