When no .zddc is on disk at the requested directory, ServeZddcFile
now renders the cascade's leaf-level ZddcFile as YAML — what
defaults.zddc.yaml's paths: tree declares for THIS exact path,
threaded through by the walker. The previous body was a comment-
only summary plus a `{}` placeholder, which forced operators to
write any override from scratch.
The .zddc file is still the single source of truth — no synthesis,
no merge: the virtual body IS the embedded subtree, marshalled in
the same shape the operator would write themselves. PUT-saving the
bytes back through the file API materialises an on-disk override
carrying exactly what the user saved. For the COMPOSED view across
the full chain, slice 2 will add ?effective=1 (returns JSON, not a
.zddc); the header comment in the virtual body points at it.
Three new test cases lock the contract:
- VirtualDefault: at /Project/.zddc with no on-disk file, the
embedded paths.* contribution surfaces (project_team: r,
observer: r, archive subtree, …).
- VirtualEmpty: at a path the embedded defaults don't declare
(e.g. /Project/random-subfolder/.zddc), the body collapses to
the header + an empty-document {} placeholder + an explanation
that rules come from ancestors only.
- VirtualPerPartyWorking: at /Project/archive/Acme/working/.zddc,
the body carries default_tool/auto_own/drop_target and the
classifier in available_tools — the per-party in-flight slot's
full declaration.
Drive-by: add `omitempty` to ZddcFile.ACL, .Admins, .Title yaml
tags. Without it, the marshaled virtual body carried `acl: {}`,
`admins: []`, and `title: ""` at every nested level, drowning the
real content in noise. ParseFile is unaffected (input parsing
ignores omitempty); WriteFile's round-trip sanity check still
passes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>