Repository navigation
Description
Successor to #293, which proposed replacing the placement path template with a spec.layout
discriminated union and was reversed. The template stays; the two things it genuinely cannot express
become additive fields. Design:
docs/layout/model.md, sequenced as PR 4 of
docs/layout/implementation-plan.md (both land
with #318).
The fields
serializeNamespace: Auto | Always | Never — whether a written document carries
metadata.namespace.
Auto(default): omit it when the governing kustomization already sets this resource's namespace.
Today's inferred behavior, now named.Always: always write it. The only safe choice when nothing downstream supplies it.Never: never write it, and prove something else does.
The name deliberately avoids writeNamespace: "write" is the most loaded word in this API (the
write boundary, the write jail, WriteBoundaryRefused), and writeNamespace: Never invites the
reading "never write to this namespace", which is a permission — precisely what the neighbouring
sourceNamespace fields are.
kustomizeRoot: Adopt | Create | Require — what the target does about the folder's root.
Adopt(default): use the one that is there; do nothing if there is none.Create: write akustomization.yamlthe user did not author, which is what bootstraps an empty
repository and what makesserializeNamespace: Neverprovable there — the operator owns the file
the omission depends on.Require: refuse to write when no root governs the path, instead of committing documents that
land in whatever namespace the applier defaults to.
CreatePerDirectory is deliberately deferred, with its trigger written down in model.md.
Why this is not breaking
Both defaults equal today's behavior, so this ships in any release without a coordinated consumer
bump. That is the largest consequence of the reversal: the whole placement story (PRs 1–4) is
additive, and #294's wave lost its largest member.
What has to ship with it
A post-scan validation pass, in PR 3, because these two fields make promises no CEL rule can
check — the precondition is a property of the observed folder, not of the spec:
| Rule | Precondition |
|---|---|
serializeNamespace: Never requires a namespace supplier |
a kustomization with namespace: governs the path |
kustomizeRoot: Require needs a root |
one governs the path |
| a declared single-root assertion | the folder has exactly one root |
Done when
The six scenarios in docs/layout/examples/ run
unskipped. PR 1 builds them as an executable corpus with every case that needs these fields skipped
and naming this work in its skip message, so this is finished when the last skip is gone — which
is the definition of done the plan is built to produce.
Open questions carried from the design
- Does
serializeNamespace: Neverneed to name its supplier (KustomizeRoot,
FluxTargetNamespace,Asserted) so the post-scan pass checks the guarantee rather than infers
which one was meant? - Is
Requirethe right default for a folder that already contains a kustomization when the target
is created? - Should
placement.defaultgain a CRD default now that the shipped ancestor walk (fix(placement): register a new file with its nearest ancestor kustomization #319) registers
a defaulted path with the kustomization that governs it? The remaining objection is legibility,
not correctness.
Activity
docs: point the queue at #322, and strike what 0.42.1 shipped
sunib commentedon Sep 3, 2026
Shipped in #326 and #328, with a deliberately different API contract from the one proposed here.
What shipped
spec.serializeNamespaceas a nullable boolean, notAuto | Always | Never. Unset already isthe third state and means infer, so the enum bought a spelling rather than a capability.
spec.placement.useKustomizeas a boolean, notAdopt | Create | Require. Adopt and Create arethe two states;
Requirewas dropped.Why
Requirewas droppedIt and the "
Neverrequires a namespace supplier" precondition both rest on proving a supplierexists in the repository. That check cannot be written honestly: the supplier of a namespace-free
folder is a Flux
targetNamespaceor an Argodestination.namespacein a different cluster, andthere may be more than one, so a rule keyed on the folder's own contents fires on the intended
configuration. See
model.md§ Whyfalseneeds no guard.What replaced it is checkable from the config cluster alone: an explicit
serializeNamespace: falseadmits exactly one source namespace, and a second is refused. The post-scan pass shipped with its
one checkable rule,
Ambiguous.Done-when is satisfied. The corpus runs, and no skip names this work; the single remaining skip
names patch authoring.
If the supplier proof is still wanted it needs a new issue arguing against that section, rather than
reopening this one.