compose-lens 0.1.16

Loss-aware parsing, processing, validation, and rendering of Compose projects
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# Phase 2 typed Compose model

Phase 2 is complete for the first BoxFerry Compose-to-Quadlet conversion boundary. It is a
source-aware native Compose model, not a claim that every field in the Compose Specification
is already typed.

Fields outside this boundary remain available through the loss-aware syntax document and as
source-spanned unknown-field references. Adding another field must not require redesigning the
Phase 2 representation rules.

## Parse contract

`ComposeDocument::parse` extracts typed data from a `SyntaxDocument` and returns recoverable
diagnostics plus partial data. It does not:

- interpolate environment expressions;
- read the process environment;
- select profiles;
- apply Compose defaults;
- merge multiple files;
- normalize short syntax into long syntax; or
- validate runtime support in Docker Compose or Podman Compose.

Those operations belong to Phase 3 processing and compatibility profiles.

Multi-file consumers use `build_project_view` after merge and optional profile selection. That
operation types effective values directly and wraps them in `ProjectValue<T>` with complete
multi-file provenance. It never renders canonical YAML and reparses it as a single document. See
[ADR 0016](decisions/0016-native-merged-project-view.md).

The merged-project view exposes `extra_hosts` in both sequence and mapping form. Each effective
entry retains its hostname key sources, address provenance, authored collection form, raw IPv4 or
IPv6 spelling, and a distinct `host-gateway` classification. This is additive to the
single-document `ExtraHosts` model and lets adapters preserve explicit runtime mappings.

The merged-project view also exposes `healthcheck` without collapsing scalar/list command forms.
Every effective timing, retry, disable, and command field has its own merge provenance. Compose
`start_interval` remains distinct from provider-specific startup-healthcheck features.

Service `entrypoint` is native at both document and merged-project layers through a distinct
`Entrypoint` type. Explicit null, scalar, list, empty scalar, and empty list forms remain distinct.
It is not represented as `Command`: null selects the image entrypoint, empty values clear it, and
Compose replaces rather than appends the complete value during multi-file merge.

Service `init` is a source-aware boolean at both layers. Literal `true`/`false` and deferred
interpolation remain distinct, omission remains omitted, and complete-value replacement retains
every contributing source. ComposeLens does not invent a default or select or inspect the
platform-specific init binary.

Service `stdin_open` is independently source-aware at both layers. Literal `true`/`false` and
deferred interpolation remain distinct, omission remains omitted, and complete-value replacement
retains every contributing source. ComposeLens does not infer a terminal, runtime, or cross-format
policy from either boolean.

Service `tty` has the same independent source-aware boundary. Literal `true`/`false` and deferred
interpolation remain distinct, omission remains omitted, and complete-value replacement retains
every contributing source. ComposeLens does not infer terminal, runtime, or cross-format policy.

Service `privileged` is independently source-aware at both layers. Literal `true`/`false` and
deferred interpolation remain distinct, omission remains omitted, and complete-value replacement
retains every contributing source. ComposeLens does not infer security, runtime, or cross-format
policy from this boolean.

Service `blkio_config` is native at both layers as one mapping with optional integer-or-string
`weight`, ordered integer-or-string `rate` entries in `device_read_bps`, `device_read_iops`,
`device_write_bps`, and `device_write_iops`, and ordered integer-or-string `weight` entries in
`weight_device`. Mapping entries retain optional YAML-string `path`, source spans or nested merge
provenance, extensions, unknown members, duplicates, and non-mapping sequence items as explicit
unmodeled entries. Generic recursive mapping and sequence append/reset/override behavior applies.
The raw view neither resolves `extends` nor claims the specification's path-keyed rate-array
inheritance. It applies no unit, range, default, path/device, cgroup/controller, runtime,
provider, I/O, generated-document, or conversion interpretation.

Service `cgroup` is native at both layers as a strict YAML string with a source-aware raw value
and a distinct `Host`, `Private`, deferred dollar-bearing `Expression`, or diagnosed `Other`
classification. Exact lowercase `host` and `private` are the only resolved valid literals; empty,
case-variant, and other strings remain available without coercion. Scalar replacement, reset, and
override retain provenance and sensitivity. The model supplies no default or controller, cgroup
v1/v2, rootless, systemd, provider, version, runtime, `extends`, generated-document, or conversion
interpretation.

Service `cgroup_parent` is native at both layers as a strict raw YAML string independent from
`cgroup`. Empty, whitespace, deferred, and arbitrary spellings remain exact; scalar replacement,
reset, and override retain provenance and sensitivity. The model supplies no grammar, path,
controller, host, runtime, provider, version, default, `extends`, generated-document, or conversion
interpretation.

Service `cpu_count` is native at both layers as a raw YAML integer/string category. Nonnegative
integers retain unbounded, base-prefixed, separator, and negative-zero spelling without numeric
conversion; quoted numeric, empty, and deferred values remain strings. Negative YAML integers are
retained with diagnostics. The model supplies no quota, host, scheduler, runtime, provider, OS,
version, default, `extends`, generated-document, or conversion interpretation.

Service `cpu_percent` is native at both layers as a raw YAML integer/string category. The schema's
integer branch is classified against its inclusive `0..=100` range without fixed-width conversion:
base-prefixed, separator, signed-zero, and arbitrarily long spelling remain exact. Out-of-range YAML
integers are retained with diagnostics, while quoted, block, empty, and deferred strings are not
coerced or range-checked. The model supplies no percentage calculation, CPU, quota, host, scheduler,
runtime, provider, OS, version, default, `extends`, generated-document, or conversion interpretation.

Service `cpu_period` is native at both layers as a raw YAML number/string category. Numeric YAML
spellings retain their exact text without conversion or validation; quoted, block, empty, and deferred
strings remain strings. The model supplies no duration, microsecond, CFS, CPU, host, runtime,
provider, OS, version, default, `extends`, generated-document, or conversion interpretation.

Service `cpu_quota` is native at both layers as a raw YAML number/string category. Numeric YAML
spellings retain their exact text without conversion or validation; quoted, block, empty, and deferred
strings remain strings. The model supplies no numeric quota, duration, microsecond, CFS, CPU, host,
runtime, provider, OS, version, default, `extends`, generated-document, or conversion interpretation.

Service `cpu_rt_period` is native at both layers as a YAML number, Compose-duration, expression,
or diagnosed other-string category. Durations retain exact `us`/`ms`/`s`/`m`/`h` segments and
fractional spelling without conversion; dollar-bearing strings are expressions. It supplies no CPU,
microsecond conversion, realtime scheduler, OS, host, default, provider, version, runtime,
generated-document, or conversion interpretation.

Execution identity and context are native at both document and merged-project layers. Effective
`user`, `userns_mode`, `group_add`, `working_dir`, and `read_only` values retain raw spelling,
field provenance, and, for supplementary groups, per-item provenance. No account, group, path, or
namespace lookup is performed.

Service `cap_add` and `cap_drop` are independent optional source-aware YAML sequences of string
scalars. Omission differs from an explicit empty sequence. Every valid item retains exact text,
order, span, and sensitivity; booleans, numbers, nulls, mappings, and nested sequences diagnose
without erasing valid sibling items or service fields. Exact duplicates violate schema
`uniqueItems` and remain available with source-spanned diagnostics. Ordinary multi-file merge
appends and then removes exact case-sensitive duplicates while combining provenance. `!reset`
produces an explicit empty value, and `!override` replaces the complete sequence without
deduplicating its replacement. A lexical future-conversion helper classifies only non-empty
whitespace-free strings as exact candidates; it does not lowercase, consult a capability
whitelist, apply target policy, or reconcile additions with drops.

Service `devices` is an optional source-aware sequence whose omission differs from an explicit
empty sequence. Items retain ordered mixed syntax: raw string short forms are conservatively
classified only as path-like, CDI-like, deferred, or opaque, while long mappings require a string
`source` and optionally retain raw string `target` and `permissions`. Exact duplicates, spans,
extensions, unknown fields, and malformed-item recovery remain visible. The effective view adds
field, item, nested-member, and contributor provenance plus sensitivity. Existing target-keyed
replacement for path forms, `!reset`, and `!override` is preserved; current Compose prose and
Compose-Go `extends` metadata disagree on whether `devices` is an ordinary append exclusion.
ComposeLens does not inspect host devices, parse colon triples, validate permissions or CDI, infer
GPU meaning, or claim runtime access.

Service `dns` and `dns_search` retain optional scalar/list syntax, explicit empty lists,
exact strings, duplicates, spans, provenance, and sensitivity. List merging appends while
cross-form updates replace. `dns_opt` retains one ordered sequence with whole-sequence replacement.
Resolver grammar and runtime meaning remain outside the model.

Service `expose` retains ordered YAML string/number identity and classifies documented decimal
port/range forms without integer conversion. Unsupported, deferred, and malformed values remain raw
and source-addressable.

Service `security_opt` retains an optional ordered raw sequence with append/reset/override
provenance. Exact AppArmor, no-new-privileges, seccomp, SELinux-label, Mask, and Unmask shapes are
independent diagnostic candidates; near misses and conflicts remain unselected. The model validates
no profile, policy, path, filesystem, provider, runtime, or cross-format semantics.

Service `annotations` retains mapping/list syntax, scalar evidence, raw list items, and keyed
effective contributors. Mapping keys do not interpolate, and key-only list items remain explicit
ambiguity rather than becoming empty label values.

Service `logging` retains an optional uninterpreted YAML string `driver`, an ordered `options`
mapping including explicit empty state, and exact string/number/null option kinds. Option keys are
non-empty and never interpolate; extensions, unknown fields, malformed entries, and valid siblings
remain source-addressable.

An explicit `container_name` is a source-aware scalar at both layers. The effective project view
retains ordinary Compose scalar replacement provenance across files. The parser does not confuse
the custom runtime name with the service key or infer one when the field is absent.

Service `hostname` is a separate source-aware YAML string scalar at both layers. Its exact value
and span are retained. Any scalar containing `$` is deferred; resolved literals are conservatively
validated as ASCII RFC-1123 hostnames with a total length of 1 through 253 and dot-separated labels
of 1 through 63 characters. Labels may contain ASCII letters in either case, digits, and interior
hyphens, and must start and end alphanumeric. Invalid literals remain inspectable with diagnostics;
null, boolean, numeric, mapping, and sequence shapes diagnose without deleting their service.
Omission stays omitted, and no value is derived from `container_name`, service keys, or runtime
state.

Service `pids_limit` is raw-preserving at both layers and remains separate from
`deploy.resources.limits.pids`. Omission stays omitted; `-1` is `Unlimited`; positive ASCII-decimal
spellings are `Finite`; and all-zero spellings are a distinct ambiguous and unportable `Zero`
state. Finite values retain their complete decimal spelling and are never parsed into a fixed-width
integer, so leading zeros and values beyond `u64` remain lossless. Interpolation-shaped strings are
deferred. Fractions, signs other than the exact `-1`, exponents, and arbitrary strings remain
`Other` with diagnostics. YAML booleans, null, mappings, and sequences are rejected as field forms
without deleting their service. ComposeLens injects no default and performs no runtime or cgroup
inspection.

Service and Build `shm_size` are raw-preserving at both layers and remain separate from IPC and
pod grouping, CPU or memory limits, and runtime `/dev/shm` inspection. YAML number and
string scalars are accepted and retain their exact value, span, and scalar category. Dollar-bearing
strings are deferred. Strings ending in the documented lowercase `b`, `k`, `kb`, `m`, `mb`, `g`,
or `gb` family expose that unit and retain the complete `amount_raw` without imposing an integer,
fraction, sign, or leading-zero grammar that Compose does not define. All-zero integral spellings
remain a distinct ambiguous state. Other schema-accepted numbers and strings remain separate
provider-dependent states with actionable diagnostics. Null, booleans, mappings, and sequences
diagnose without deleting their service. Omission stays omitted; ComposeLens does not synthesize
Podman's 64 MiB default, normalize units, or parse values into a fixed-width integer.

Service `mem_limit` is independently raw-preserving and remains distinct from `mem_reservation`,
`memswap_limit`, deploy resource memory, and `shm_size`. YAML number and string scalars retain exact
text, span, and scalar category. Dollar-bearing strings are deferred; documented lowercase `b`,
`k`, `kb`, `m`, `mb`, `g`, and `gb` suffixes retain an unconstrained raw amount; lexical zero,
schema-only numbers, and other provider-dependent strings remain distinct with recoverable
diagnostics. ComposeLens does not normalize units, parse a machine integer, reconcile deploy
values, inspect host/cgroup state, or claim non-byte values are exactly transferable.

Service-level `tmpfs` is distinct from long-syntax volume type `tmpfs` and retains omission,
scalar/list form, explicit empty lists, ordering, exact duplicates, source spans, and sensitivity.
Each exact string uses `<path>[:<options>]`: a non-empty path alone or colon-delimited non-empty
`mode`, `uid`, and `gid` assignments is `Documented`; dollar-bearing values are `Expression`;
other raw or malformed options are retained as `ProviderDependent` with an actionable diagnostic.
No path or option normalization occurs. Ordinary list-to-list multi-file merge appends without
deduplication; scalar/list mismatches replace normally, while `!reset` and `!override` remain explicit.

Service `sysctls` retains mapping versus list syntax, including explicit empty collections.
Mapping keys are non-empty literal strings and remain uninterpolated; values retain exact YAML
string, number, boolean, or null kind and spelling. List items retain exact string spelling,
ordering, spans, interpolation sensitivity, and duplicate evidence. Invalid keys, values, items,
and collection forms diagnose without erasing valid siblings. ComposeLens does not interpret
namespaces, privileges, kernel availability, or runtime coercion.

Service `ulimits` is mapping-only and accepts an explicit empty mapping. Names follow lowercase
ASCII `[a-z]+` and do not interpolate. Each ordered entry retains either one number/string scalar
or a soft/hard mapping whose members are both required; scalar spelling, value span, and malformed
siblings remain source-aware. Existing authored `Ulimits`, `Ulimit`, `UlimitValue`, `UlimitRange`,
and `LimitValue` APIs remain unchanged. The effective project types additionally retain outer-key,
field, entry, and range-member provenance, authored versus interpolated scalar spelling, YAML
number/string kind, sensitivity, omission, explicit empty/reset mappings, recursive merge,
scalar/range replacement, and override without applying runtime semantics.

Service-level `restart` is raw-preserving at both layers. Known policies are classified as `no`,
`always`, `on-failure[:max-retries]`, and `unless-stopped`; a deferred interpolation remains
distinct, and invalid/provider-specific values remain available with diagnostics. The optional
decimal retry spelling is not normalized, so an authored `on-failure:003` remains exact.
This field is separate from `depends_on.<service>.restart`, which describes an explicit
Compose-controlled dependency update, and from `deploy.restart_policy`.

Service `pull_policy` is raw-preserving at both layers. `always`, `never`, `missing`, `build`,
`daily`, `weekly`, the `if_not_present` alias, and valid `every_<duration>` values receive distinct
classifications without changing caller spelling. Custom intervals match the schema grammar
`every_([0-9]+[wdhms])+`: integer week, day, hour, minute, and second components can be combined;
fractions, `us`, and `ms` are retained as `Other`. `every_0s` is schema-valid and therefore remains
`Every`, while its prose semantics stay explicitly ambiguous. Interpolation-shaped values remain
deferred. The schema-only `refresh` spelling is classified separately from both documented forms
and invalid/provider-specific values; that classification is not a provider support claim.
`pull_refresh_after` is independently retained as a strict raw YAML string, including empty and
deferred values. It keeps scalar replacement/reset/override provenance, source spans, sensitivity,
and malformed-field evidence, but does not parse an interval, supply a default, couple to
`pull_policy: refresh`, calculate refresh state, infer provider/version support, generate, or convert.

Service `stop_signal` and `stop_grace_period` are independent optional values at both layers.
Signals retain the complete scalar without imposing a token grammar not defined by Compose. A
quoted empty signal remains distinct from a missing or null field. `StopGracePeriod` retains its
authored scalar as a policy-accepted value, interpolation-shaped value, or explicit
invalid/provider-specific value. ComposeLens's raw-preserving policy uses the documented `us`,
`ms`, `s`, `m`, and `h` units and accepts composite, zero-with-unit, and fractional segments;
health-check-only `ns`, `µs`, and `μs` acceptance is not inherited. Following existing typed-field
conventions, any retained scalar containing `$` is classified as interpolation-shaped. This is a
lexical classification, not proof that the scalar is eligible for or contains a valid Compose
interpolation expression. No target lifecycle normalization is performed.

Service `env_file` is native at both layers without performing file I/O. A lone scalar and each
ordered sequence item retain short syntax; mapping items retain long syntax with source-aware
`path`, `required`, and `format`. `required` distinguishes a literal boolean from deferred
interpolation. `format` retains its complete scalar while classifying `raw`, a deferred
expression, or an invalid/provider-specific value. The effective project view preserves ordinary
sequence append order plus collection, item, and nested-field provenance. Relative-path
resolution, file existence, and parsing file contents remain caller-owned operations.

Service config and secret grants are native at both layers. The merged-project view retains short
versus long syntax, collection/item provenance, and separate provenance for long-form `source`,
`target`, `uid`, `gid`, and `mode`. This preserves Compose's unique-by-target merge behavior,
including nested values inherited from an earlier file.

Compose processing tags `!reset` and `!override` remain intact in the syntax document. The typed
parser reads their inner value when it matches a typed field, allowing valid override documents to
participate in loading without incorrectly reporting the wrapper as a field-type error. The merge
stage, not the typed parser, applies the tag's semantics.

## Typed boundary

| Location | Phase 2 fields |
| --- | --- |
| Document | `name`, `services`, `networks`, `volumes`, `configs`, `secrets` |
| Service | `hostname`, `container_name`, `image`, `platform`, `build`, `entrypoint`, `command`, `credential_spec`, `extends`, `provider`, `post_start`, `pre_stop`, `pre_start`, `blkio_config`, `attach`, `init`, `stdin_open`, `tty`, `privileged`, `environment`, `env_file`, `labels`, `annotations`, `logging`, `extra_hosts`, `user`, `userns_mode`, `group_add`, `cap_add`, `cap_drop`, `devices`, `dns`, `dns_opt`, `dns_search`, `expose`, `security_opt`, `working_dir`, `read_only`, `pids_limit`, `shm_size`, `mem_limit`, `tmpfs`, `sysctls`, `pull_policy`, `pull_refresh_after`, `restart`, `runtime`, `stop_signal`, `stop_grace_period`, `ulimits`, `depends_on`, `healthcheck`, `deploy`, `ports`, `volumes`, `networks`, `profiles`, `configs`, `secrets` |
| Network definition | `driver`, `driver_opts`, `attachable`, `enable_ipv4`, `enable_ipv6`, `external`, `internal`, `ipam`, `labels`, `name` |
| Volume definition | `driver`, `driver_opts`, `external`, `labels`, `name` |
| Config definition | `file`, `environment`, `content`, `external`, `name` |
| Secret definition | `file`, `environment`, `external`, `name` |

Every implemented mapping retains `x-` extensions and unrecognized fields with source spans.
Collections retain authored order.

## Representation fidelity

Field-specific variants retain forms whose behavior or meaning can differ:

- entrypoint and command: independent explicit null, scalar, or list values, including empty
  scalar and empty list;
- attach, init, stdin_open, tty, and privileged: independent literal booleans or deferred interpolation expressions;
- capability additions and drops: independent omitted or explicit ordered string sequences with
  exact duplicates retained for diagnostics and case variants kept distinct;
- service devices: an omitted or explicit ordered sequence of mixed raw short strings and long
  mappings with required `source`, optional raw `target`/`permissions`, exact duplicates, and
  retained CDI/deferred/opaque evidence;
- service DNS servers: an omitted raw scalar or ordered list, including explicit empty lists and
  exact duplicate strings;
- environment: list or mapping, including `NAME`, `NAME=`, empty strings, and null values;
- environment files: a scalar path, ordered path list, or ordered long entries with `path`,
  `required`, and `format` retained independently;
- ports: scalar short syntax or mapping long syntax;
- service volumes: scalar short syntax or mapping long syntax;
- service networks: name sequence or options mapping;
- service config and secret grants: name short syntax or mapping long syntax;
- extra hosts: hostname/address sequence or mapping, retaining delimiters and IPv6 brackets;
- service hostname: resolved RFC-1123 literal, deferred dollar-bearing expression, or retained
  invalid string literal;
- dependencies: service-name sequence or condition/options mapping;
- health-check tests: shell-command scalar or tokenized list;
- PID limit: unlimited, arbitrary-precision positive decimal, ambiguous zero, deferred expression,
  or retained unsupported scalar;

- shared-memory size: exact YAML number/string provenance, documented lowercase unit plus raw
  amount, ambiguous zero, deferred expression, or distinct provider-dependent number/string state;
- service temporary filesystems: omitted, scalar, or ordered list form with explicit empty list,
  exact duplicates, colon-delimited documented assignments, and retained raw target options;
- service sysctls: omitted, ordered mapping or ordered list form with explicit empty collections,
  exact scalar kind/spelling, uninterpolated map keys, and retained duplicate list evidence;
- service logging: omitted or mapping form, optional uninterpreted string driver, ordered
  string/number/null options including explicit empty state, extensions, unknowns, and malformed
  sibling recovery;
- restart policy: a known literal, optional raw-preserving `on-failure` retry count, deferred
  expression, or retained invalid/provider-specific scalar;
- pull policy: documented literals, a retained alias, exact custom interval spelling, deferred
  expression, schema-only refresh classification, or retained invalid/provider-specific scalar;
- stop lifecycle: an unconstrained raw signal scalar and an independent raw-preserving Compose
  duration value/expression/other state;
- ulimits: an ordered lowercase-name mapping with one number/string scalar or required separate
  soft/hard values, including an explicit empty mapping;
- build: raw list/scalar-map `additional_contexts`, short scalar or long mapping context, raw ordered `cache_from`/`cache_to`, non-empty `dockerfile`, opaque `target`/`network`/`isolation`, raw ordered `platforms`/`tags`, map/list `args`/`labels`, boolean/string `no_cache` and `sbom`, scalar boolean/expression `pull`, raw-preserving `shm_size`, short/long `secrets`, and sensitive list/scalar-map `ssh` retain source evidence and recovery.
  Cache descriptors receive no type, reference, source, destination, path, image, credential, or builder interpretation. Platforms receive no OCI parsing; string `no_cache` and `sbom` values retain type and spelling without boolean coercion, while `pull` receives no interpolation, default, or execution inference. `sbom` does not parse generators, expose generated SBOM data, or trigger builds.
  No build resolves top-level secrets, materializes contents, parses SSH grants/paths/sockets/agents, or generates builds;
- service and resource labels: list or mapping. Service-label list entries retain the complete
  scalar so values containing additional `=` characters are not truncated.

`attach` preserves literal booleans or deferred dollar expressions with source and scalar merge
provenance; invalid forms remain source-addressable evidence. It has no default or generated API,
and it makes no logging, runtime, provider, CLI, compatibility, BoxFerry, or other cross-format
inference.

The raw short volume and port strings remain authoritative. Conservative helper parsing must not
turn platform-dependent path or address grammars into false certainty. ADR 0003 defines the full
[syntax-form policy](decisions/0003-preserve-compose-syntax-forms.md).

Container-side mount targets additionally expose a lexical `ContainerPath` classification for Unix
absolute, Windows drive-letter, Windows UNC, relative, and deferred paths. This classification is
independent of the machine running ComposeLens. Host bind sources continue through the separate
host-path resolver with explicit origins and home context.

## Issue-derived model expansion

Raw `user` values are authoritative. User/group helpers recognize names, numeric IDs, empty
components, and deferred interpolation without resolving accounts. The split ignores colons inside
`${VAR:-default}`, avoiding a common false decomposition. `ulimits` recognizes authored `-1` as an
explicit unlimited value in both scalar and soft/hard forms. Its generated boundary deliberately
narrows output to quoted `-1` or non-negative ASCII decimals and never emits `host`, arbitrary
schema strings, or provider defaults.

`service_healthy`, `service_started`, and `service_completed_successfully` dependency conditions
are native types. `ComposeDocument::validate_dependencies` checks one document; post-merge
`validate_references` checks the selected project. A missing Compose health check produces a
warning because the image may contain health metadata. An explicitly disabled health check is an
error for a required `service_healthy` edge. `required: false` downgrades unavailable dependency
diagnostics to warnings without hiding the reference.

Build and deploy definitions expose every current top-level subfield as its own stable kind and source reference; the project view promotes map/list `build.additional_contexts`, `build.context`, map/list `build.args`/`build.labels`, Build-specific list/map `build.extra_hosts` with scalar or nested-list raw string addresses, raw ordered `build.cache_from`/`build.cache_to`/`build.entitlements`, `dockerfile`, exact-string `dockerfile_inline`, `target`, `network`, opaque string `isolation`, raw ordered `platforms`/`tags`, boolean/string `no_cache`/`sbom`, boolean/expression `privileged`/`pull`, raw-preserving `shm_size`, service-equivalent ordered `build.ulimits`, and short/long `build.secrets`.
Platforms retain per-item source, sensitivity, append/reset/override provenance, duplicates, explicit empties, and malformed evidence without OCI or runtime inference.
`dockerfile_inline` retains exact empty or multiline string content, source spans, interpolation sensitivity, scalar merge provenance, and mutual-exclusion evidence with `dockerfile`; it does not parse Containerfiles, access paths or contexts, scan secrets, build, or infer Docker, BuildKit, or runtime behavior. Docker Compose v2.17.0 is an implementation badge only, with earlier and removal boundaries unknown. Other fields remain source-addressable, and no build is resolved, materialized, or generated.
`build.provenance` has a distinct boolean-or-opaque-string model with spans, interpolation sensitivity, scalar merge provenance, and malformed evidence; it performs no attestation parsing, generation, publication, validation, builder execution, or runtime inference. Docker Compose v2.39.0 is an implementation badge only, with earlier and removal boundaries unknown.
`build.privileged` accepts YAML literal booleans or deferred dollar expressions. Ordinary quoted
non-expression strings are invalid schema ambiguity rather than coerced booleans; they retain
source-addressable unmodeled evidence with diagnostics. Docker Compose v2.15.0 is an
implementation badge only, with earlier and removal boundaries unknown. No privilege, platform,
runtime, or build behavior is inferred.

`deploy.endpoint_mode` is independently typed as `vip`, `dnsrr`, or a raw retained `Other` value.
The latter remains a portability diagnostic rather than a rejected value; non-string forms retain
their source reference with diagnostics. The prose's `vip` default and the schema's lack of an
effective default are recorded as an evidence conflict, so ComposeLens injects no default. It does
not infer platform, service discovery, VIP, DNS, replica, deployment, runtime, or conversion behavior.

`deploy.mode` likewise retains `global`, `replicated`, or raw `Other` strings. Empty, deferred,
and provider-specific strings are portability diagnostics rather than coerced defaults; omission
remains absent rather than becoming `replicated`. No replica, scale, placement, job, deployment,
runtime, or conversion behavior is inferred.

`deploy.replicas` retains only its exact YAML number spelling or distinct YAML string spelling in
`DeployReplicas`; strings include empty and deferred values. It applies no integer grammar,
positive/zero/default rule, mode coupling, scale, allocation, scheduling, runtime, or conversion
interpretation, and has no version-boundary badge.

`deploy.labels` is distinct from service container labels. It retains mapping scalar categories and
nulls or ordered list bare/`KEY=VALUE` spellings. Mapping keys merge and replace by key; lists
append with duplicate evidence retained despite schema `uniqueItems`. No container-label, service,
runtime, platform, deployment, or conversion behavior is claimed, and there is no version boundary.
Update_config and the distinct rollback_config are independent typed mappings: parallelism preserves
YAML-integer or strict-string spelling; delay, monitor, and failure action are strict strings;
maximum failure ratio preserves YAML-number or strict-string spelling; and order retains documented
or provider-specific values. The current [Deploy update section](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#update_config)
and [rollback section](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#rollback_config)
name `continue` and `pause` for rollback `failure_action`, while the current
[Compose JSON schema](https://github.com/compose-spec/compose-spec/blob/master/schema/compose-spec.json)
accepts any string; this API follows the schema and retains it without rejection. The schema
enumerates `order`, so other strings are retained with a mapping-specific portability diagnostic.
Neither those sources nor the [Compose merge rules](https://github.com/compose-spec/compose-spec/blob/main/13-merge.md)
provide an introduction/removal/provider boundary or defaults; generic mapping merge applies and
ComposeLens injects none.

`credential_spec` is a mapping-only service value with optional strict YAML-string `config`, `file`,
and `registry` members, including explicit empties and malformed/unknown evidence. The current
[Compose service reference](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#credential_spec)
describes URI-like `file`/`registry` forms, while the current
[Compose JSON schema](https://github.com/compose-spec/compose-spec/blob/master/schema/compose-spec.json)
models all three as strings in one mapping; ComposeLens retains raw spelling without resolving that
prose/mapping ambiguity. The [Compose merge rules](https://github.com/compose-spec/compose-spec/blob/main/13-merge.md)
therefore apply as generic recursive mapping merge. No file, registry, account, top-level config,
Windows/gMSA, platform, provider, runtime, or conversion behavior is inferred.

`extends` accepts either a schema-supported YAML-string short reference or a long mapping. The
long form retains optional strict YAML-string `service` and `file` members, explicit empties,
mapping span, extensions, and malformed or unknown members; a missing `service` remains retained
with a diagnostic. The [Compose service reference](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#extends)
focuses on the mapping while the [schema](https://github.com/compose-spec/compose-spec/blob/master/schema/compose-spec.json)
also permits the short string. The [merge rules](https://github.com/compose-spec/compose-spec/blob/main/13-merge.md)
apply only generic scalar replacement or recursive mapping merge. These raw types never expand or
merge a referenced service, look up files, normalize paths, traverse cycles, or import resources.
The separate `validate_references` stage may validate a same-file long-form `service` edge when
`file` is absent; it performs none of those operations or provider, platform, runtime, or conversion inference.

`provider` is a closed mapping with a required strict YAML-string `type` and optional `options`
mapping, as defined by the [Compose JSON schema](https://github.com/compose-spec/compose-spec/blob/master/schema/compose-spec.json).
Option keys are nonempty; each value is a YAML string, number, or boolean scalar or an ordered
sequence of those categories. Empty types remain valid, while malformed or duplicate
members and sequence items remain source-aware evidence and a missing type is diagnosed. Generic
mapping/scalar/sequence merge applies with no provider execution, discovery, setup/teardown,
environment injection, secret or credential resolution, provider grammar/compatibility validation,
or conversion behavior.

`post_start` and `pre_stop` are ordered sequences of closed hook mappings. Each hook requires a `command` in the
existing null/scalar/list form and may carry a local map/list `environment`, boolean `privileged`,
and strict YAML-string `user` or `working_dir`; extensions and malformed entries remain evidence.
Generic sequence merge applies without command execution, environment inheritance, lifecycle scheduling,
privilege decisions, provider compatibility claims, or conversion.

`pre_start` is a distinct ordered hook sequence. Its `command` is optional and preserves the
existing null/scalar/list forms; strict raw `image`, local map/list `environment`, boolean
`privileged`/`per_replica`, and strict YAML-string `user`/`working_dir` members remain optional
and source-aware. It applies no image, environment, identity, replica, lifecycle, or runtime default.

Service `runtime` retains only a strict YAML-string scalar, including empty and deferred values.
Ordinary scalar replacement/reset/override provenance remains visible without a runtime grammar,
allowlist, default, host inspection, provider compatibility, execution, generation, or conversion claim.

Service `pull_refresh_after` retains only a strict YAML-string scalar, including empty and deferred
values. Ordinary scalar replacement/reset/override provenance remains visible without an interval
grammar, minimum, default, refresh-state calculation, `pull_policy: refresh` coupling, provider
compatibility, execution, generation, or conversion claim.

Service `platform` retains only a strict YAML-string scalar, including empty and deferred values.
Ordinary scalar replacement/reset/override provenance remains visible without OCI component parsing,
normalization, alias/case handling, host or image-manifest inspection, build feasibility, provider
support, `build.platforms` coupling, defaults, generation, or conversion claim.

`deploy.restart_policy` is distinct from service `restart`. Its condition retains `none`,
`on-failure`, `any`, deferred, and unknown values; delay/window and max-attempts retain raw
spellings and YAML integer/string category. No fallback, default, precedence, attempt simulation,
runtime, or conversion behavior is inferred.

`deploy.placement` retains ordered YAML-string `constraints`, preference mappings with an optional
YAML-string `spread`, and `max_replicas_per_node` as a YAML-integer or YAML-string scalar category.
Extensions, unknown members, and malformed siblings remain source-aware evidence. Its effective
view keeps collection, item, and nested-member provenance across generic append, replacement,
reset, and override behavior. ComposeLens applies no constraint/spread grammar, node selection,
count/range/default, mode coupling, scheduling, runtime, or conversion interpretation.

`deploy.resources.limits.cpus` retains YAML-number or YAML-string scalar category and exact
spelling, `deploy.resources.limits.memory` accepts only YAML strings and retains raw text with
documented lowercase-unit, lexical-zero, deferred, and provider-dependent classification, while
`limits.pids` retains YAML-integer or YAML-string categories; `reservations.cpus` independently
retains YAML-number or YAML-string scalar categories, while `reservations.memory` reuses the same
YAML-string-only raw classification as deploy resource-limit memory. Extensions, unknown members, and malformed
values remain source-aware evidence, while the effective view retains recursive mapping,
replacement, reset, override, and sensitivity provenance. These values infer no service CPU,
`mem_limit`, unlimited, positivity, range/default, host, cgroup, runtime, consistency, or conversion behavior.

`deploy.resources.reservations.generic_resources` is retained as schema-only evidence, not a
resource-matching model. Ordinary sequences append; reset and override retain collection and item
provenance. Mapping items expose optional raw `discrete_resource_spec.kind` and number-or-string
`value`, while non-mapping items and malformed members remain source-addressable evidence. No
prose, version, provider, matching, scheduling, device, runtime, or conversion behavior is
inferred; reservation-device semantics remain outside the generic-resource boundary.

`deploy.resources.reservations.devices` retains schema-only ordered mapping/unmodeled item evidence.
Each valid mapping requires a capabilities sequence of exact raw strings and may retain an optional
strict YAML-string `driver`, raw YAML-integer-or-string `count`, and ordered strict-YAML-string
`device_ids`; duplicates remain in order and malformed members remain unmodeled. Count and IDs
conflict diagnostically without a winner. Ordinary append/reset/override and nested provenance
remain observable without device selection/loading, capability or driver grammar, count range,
sign, default, `all`, allocation/GPU/device matching, CDI, host, scheduling, provider/version,
runtime, cgroup, or conversion behavior. Options retain map/list syntax, scalar fidelity, malformed
evidence, exact duplicate list strings, and generic provenance without provider interpretation.

Podman `keep-id`, `auto`, and `nomap` user-namespace values and the `host-gateway` token have native
classifications. Their compatibility findings cite official implementation documentation and do
not promote untested provider pass-through to supported. See
[ADR 0014](decisions/0014-issue-derived-native-model-expansion.md).

## Deferred values

Interpolation is not parsing. Boolean-capable fields therefore distinguish a YAML boolean literal
from a deferred scalar expression such as `${EXTERNAL:-false}`. Environment and option mappings
retain null, boolean, numeric, and string scalars without applying interpolation or coercion.

Image references are intentionally tolerant. A combined tag and digest such as
`registry.example/app:1.2@sha256:abcdef` is accepted and retains its complete raw value. ComposeLens
does not reject a real implementation-supported reference merely because an unrelated stricter
grammar would reject it.

## Empty YAML values

An omitted mapping value, for example `INHERITED:` in an environment mapping or `cache:` in a
top-level volume collection, is an explicit null-like authored value. The typed extractor uses
source columns to recover same-level sibling entries when the private YAML dependency nests them
under that empty value. Recovery crosses the private tree boundary when an empty value is the last
entry of a child mapping, so later parent fields are not silently reparented. Regression fixtures
require the empty entry and all following siblings to remain independently visible.

## Diagnostics and evidence

Invalid collection shapes, invalid short/long alternatives, incomplete long forms, duplicate
fields, and invalid typed scalars produce stable diagnostic codes and source labels. Extraction
continues wherever partial data remains useful.

The authored `typed-model` fixtures cover valid Phase 2 forms, issue-derived post-0.1 forms,
raw and malformed PID limits, shared-memory sizes, and service temporary filesystems, invalid recoverable forms, combined
image tags and digests, deferred expressions, empty values, extension fields, unknown fields, and
the short/long service-volume SELinux asymmetry.

Primary format references:

- [Compose Specification](https://github.com/compose-spec/compose-spec/blob/main/spec.md)
- [Docker Compose services reference](https://docs.docker.com/reference/compose-file/services/)
- [Docker Compose networks reference](https://docs.docker.com/reference/compose-file/networks/)
- [Docker Compose volumes reference](https://docs.docker.com/reference/compose-file/volumes/)
- [Docker Compose configs reference](https://docs.docker.com/reference/compose-file/configs/)
- [Docker Compose secrets reference](https://docs.docker.com/reference/compose-file/secrets/)
- [Docker Compose build reference](https://docs.docker.com/reference/compose-file/build/)
- [Docker Compose deploy reference](https://docs.docker.com/reference/compose-file/deploy/)
- [Podman 5.4 run reference](https://docs.podman.io/en/v5.4.0/markdown/podman-run.1.html)