Skip to content

fix: refuse a project name Docker normalizes away to nothing - #147

Merged
lesnik512 merged 1 commit into
mainfrom
fix/project-name-normalization
Sep 27, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/project-name-normalization

Conversation

@lesnik512

Copy link
Copy Markdown
Member

No issue: found while checking whether the nested-identifier sweep I had proposed was
worth doing. It mostly is not -- seven of the eight nested identifier positions
(depends_on list entries and mapping keys, a service's secrets entry and long-form
source, build.secrets sources, profiles entries, extends.service) are clean in
both directions, because each is cross-checked against a declaration whose key #143 now
holds to the grammar. The eighth was not.

The rule

docker compose config v5.1.2 does not judge the top-level name against a pattern. It
rewrites it -- lowercase, drop every character outside [a-z0-9_-], then drop
leading _ and - -- and refuses only when nothing survives:

name: "A B"  -> ab       name: "ä"    -> project name must not be empty
name: "_z"   -> z        name: "!!!"  -> same
name: "z-"   -> z-       name: "中文"  -> same

compose2pod accepted all of them. The refused ones are documents Docker will not read,
so this is rule one. Practical impact is nil -- name is listed as supported and
otherwise never read, so the emitted script is identical either way -- but ADR-0006
states rule one as hard with no exceptions, and I had re-asserted that sentence two
commits earlier.

Why a pattern would have been the wrong fix

Because the rule is a rewrite, not a grammar, the test is "does anything survive", and
the surviving set is not what it looks like. Docker lowercases before it strips, so a
character whose lowercase is ASCII survives. Exactly two codepoints in all of Unicode do:
İ (U+0130) and K (U+212A), which Docker reads as i and k.

My first version tested the raw name and refused both -- two over-rejections on a
two-character corner of Unicode. Adversarial review caught it by exhaustive scan; I
confirmed it independently, both against real docker and by scanning U+0080-U+10FFFF in
Python, which agrees that those two are the only ones. The check runs over name.lower()
now, and the two are pinned as accepted.

str.isalnum cannot stand in for the test at all: it is Unicode-aware and would count
the ä Docker throws away.

The variable carve-out is measured, not assumed

name: "${P}" is refused by Docker with P unset and accepted with P=ok, so the
verdict belongs to the shell reading the file, not to the document -- ADR-0006's
host-state boundary. Variable-carrying names are skipped. The test uses ${_} and
-${_}- rather than ${P}: a name containing P would pass on that letter alone and
prove nothing about the carve-out.

Evidence

  • A 33-name differential sweep: no rule-one hole and no over-rejection left. The only
    divergence is ${P}, which is the carve-out above.
  • just test-ci 1586 passed, 100% coverage. just test-conformance 953 passed,
    over-rejections unchanged at 19.
  • New corpus document project_name_normalizes_to_empty.yaml; the generic corpus run is
    what holds it, since docker-rejects/we-accept is the combination assert_rule raises on.

ADR-0006

The hard-rule paragraph said "three classes" and would have contradicted itself with a
fourth appended. It now says four, drops a dangling "the sentence opening this paragraph"
back-reference, and makes the point plainly: "no exceptions left" has been written and
falsified twice, so what keeps it true is the generated probes, not the enumeration.

@lesnik512
lesnik512 merged commit 844176f into main Sep 27, 2026
15 checks passed
@lesnik512
lesnik512 deleted the fix/project-name-normalization branch September 27, 2026 20:00
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