You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The legibility residue of #291. The sibling-inference deletion shipped, and so did the counters that
say where every new file landed (placements_total{source, disposition, gittarget_namespace, gittarget_name, group, version, resource}, placement_refusals_total{reason}, placement_kustomization_entries_total{outcome}). What is still missing is the durable half:
a metric says a fall-back happened, and nothing says what the operator understood about the folder.
Shapes, with five worked examples (greenfield, kustomize overlay, brownfield missing one rule, two
ambiguous roots, a refusal from an operator-configured sensitive type): docs/layout/placement-visibility-and-declared-defaults.md
(moved there by #318; it was docs/design/...).
The field is status.placement, not status.layout.#293's spec.layout was reversed, and the
status field was renamed with it. The shape below is unchanged; read every status.layout here as status.placement. It is PR 3 of docs/layout/implementation-plan.md, and it ships additively — none of this issue is breaking any more.
Contents
GitTarget.status.placement (config surface B2). Highest value per line of code in the
config-surface doc, and more so after the deletion. An epoch-scoped per-target roll-up marked
from the write path over the existing MarkTargetRetention seam, which already enqueues on
change — that retires the "the data plane cannot notify the GitTarget" objection. It should carry
the per-target record of which types resolved by declaration and which fell back. ambiguousDocuments in particular is a correctness-relevant fallback that is currently a
debug-level store diagnostic.
renderRootReason: Ambiguous. Two supported kustomizations under one GitTarget produce a
file that is unreachable and uncounted today. Publish the signal as its own series rather than
as a third outcome on a counter that counts entry attempts. The refuse-or-write policy belongs
to the post-scan validation pass, which ships in the same PR.
Split source="declared" into byType and default, keep canonical. Today a byType hit
and a default hit are one series, so a catch-all quietly swallowing a type you meant to name
explicitly looks identical to a rule working as intended. For a metric whose job is "is a rule
missing?", that is the wrong place to lose resolution.
Unify the prose on "canonical". architecture.md still calls the built-in path "the built-in
default" while the CRD comment calls it "the built-in canonical path", and placement.default is
a user declaration. One name, one meaning.
{kindLower}, plus the single-namespace recipe and its identity caveat in configuration.md. A toLower function syntax would be a language; one variable answers the
actual need.
Express the canonical path as a template constant rendered through RenderPlacementTemplate,
pinned byte-for-byte against ResourceIdentifier.ToGitPath() across cluster-scoped, core,
grouped and sensitive identities. Removes the hand-written canonicalPath duplication.
The Event question, still undecided
Whether a fall-back to canonical also raises a corev1.Event on the GitTarget. The split argued in
the design docs is: an Event for timeliness, status.placement for durability, because an Event is
deduplicated and expires (--event-ttl defaults to 1h) while a status field is what someone reads a day
later and the only one of the two a kubectl get -o yaml in a bug report contains.
Two facts constrain it: LocateNew runs on the branch worker with no reconcile context, so the write
path has to hand the fact back rather than emit it (the pathRefusal → GitPathAccepted=False
projection is that seam); and that seam does not enqueue the GitTarget, so it surfaces on the next
requeue, up to ten minutes later — good enough for a durable condition, weak for a notification.
The metric now carries the actionable part in the meantime: one placements_total{source="canonical"}
series names the target and the exact byType key that is missing.
Relationship to the layout model
Both of this section's original premises are gone, and the conclusion survives them.
status.placement was to wait for #293's vocabulary; there is no discriminated union to borrow a
vocabulary from, and the field's own shape (renderRoot, serializeNamespace, observedRevision, conditions) is settled in implementation-plan.md. It was also to do double
duty as the Observe-mode dry run; spec.mode is dropped, and the dry run is now a suspended
target that keeps scanning — which needs this field just as much, so the reason to build it early is
unchanged.
It still ships with the placement work rather than alone, because the post-scan validation pass reads
the same scan. The metric-value split, {kindLower} and the template constant depend on nothing here
and can land any time.
Two items on this list have shipped in 0.42.1 via #319, and were tracked on the sibling issue #295: registering a declared path with the kustomization that governs it, and dropping the {version}
requirement from IdentityCompletePlacementTemplate.
The legibility residue of #291. The sibling-inference deletion shipped, and so did the counters that
say where every new file landed (
placements_total{source, disposition, gittarget_namespace, gittarget_name, group, version, resource},placement_refusals_total{reason},placement_kustomization_entries_total{outcome}). What is still missing is the durable half:a metric says a fall-back happened, and nothing says what the operator understood about the folder.
Shapes, with five worked examples (greenfield, kustomize overlay, brownfield missing one rule, two
ambiguous roots, a refusal from an operator-configured sensitive type):
docs/layout/placement-visibility-and-declared-defaults.md(moved there by #318; it was
docs/design/...).Contents
GitTarget.status.placement(config surface B2). Highest value per line of code in theconfig-surface doc, and more so after the deletion. An epoch-scoped per-target roll-up marked
from the write path over the existing
MarkTargetRetentionseam, which already enqueues onchange — that retires the "the data plane cannot notify the GitTarget" objection. It should carry
the per-target record of which types resolved by declaration and which fell back.
ambiguousDocumentsin particular is a correctness-relevant fallback that is currently adebug-level store diagnostic.
renderRootReason: Ambiguous. Two supported kustomizations under one GitTarget produce afile that is unreachable and uncounted today. Publish the signal as its own series rather than
as a third outcome on a counter that counts entry attempts. The refuse-or-write policy belongs
to the post-scan validation pass, which ships in the same PR.
source="declared"intobyTypeanddefault, keepcanonical. Today abyTypehitand a
defaulthit are one series, so a catch-all quietly swallowing a type you meant to nameexplicitly looks identical to a rule working as intended. For a metric whose job is "is a rule
missing?", that is the wrong place to lose resolution.
architecture.mdstill calls the built-in path "the built-indefault" while the CRD comment calls it "the built-in canonical path", and
placement.defaultisa user declaration. One name, one meaning.
{kindLower}, plus the single-namespace recipe and its identity caveat inconfiguration.md. AtoLowerfunction syntax would be a language; one variable answers theactual need.
RenderPlacementTemplate,pinned byte-for-byte against
ResourceIdentifier.ToGitPath()across cluster-scoped, core,grouped and sensitive identities. Removes the hand-written
canonicalPathduplication.The Event question, still undecided
Whether a fall-back to canonical also raises a
corev1.Eventon the GitTarget. The split argued inthe design docs is: an Event for timeliness,
status.placementfor durability, because an Event isdeduplicated and expires (
--event-ttldefaults to 1h) while a status field is what someone reads a daylater and the only one of the two a
kubectl get -o yamlin a bug report contains.Two facts constrain it:
LocateNewruns on the branch worker with no reconcile context, so the writepath has to hand the fact back rather than emit it (the
pathRefusal→GitPathAccepted=Falseprojection is that seam); and that seam does not enqueue the GitTarget, so it surfaces on the next
requeue, up to ten minutes later — good enough for a durable condition, weak for a notification.
The metric now carries the actionable part in the meantime: one
placements_total{source="canonical"}series names the target and the exact
byTypekey that is missing.Relationship to the layout model
Both of this section's original premises are gone, and the conclusion survives them.
status.placementwas to wait for #293's vocabulary; there is no discriminated union to borrow avocabulary from, and the field's own shape (
renderRoot,serializeNamespace,observedRevision,conditions) is settled inimplementation-plan.md. It was also to do doubleduty as the
Observe-mode dry run;spec.modeis dropped, and the dry run is now a suspendedtarget that keeps scanning — which needs this field just as much, so the reason to build it early is
unchanged.
It still ships with the placement work rather than alone, because the post-scan validation pass reads
the same scan. The metric-value split,
{kindLower}and the template constant depend on nothing hereand can land any time.
Two items on this list have shipped in 0.42.1 via #319, and were tracked on the sibling issue
#295: registering a declared path with the kustomization that governs it, and dropping the
{version}requirement from
IdentityCompletePlacementTemplate.