Skip to content

[FEATURE] Placement visibility: status.layout, the ambiguous render root, and the metric/prose split #296

Description

@sunib

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/design/placement-visibility-and-declared-defaults.md.

Contents

  • GitTarget.status.layout (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 [FEATURE] A layout is the declared thing, not a path: GitTarget.spec.layout #293.
  • 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.layout 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 pathRefusalGitPathAccepted=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

status.layout wants #293's vocabulary (declaredKind beside the resolved kind, renderRoot), and
#294 makes it do double duty as the Observe-mode dry run. So it is worth doing with the wave rather
than inventing a status shape twice. The metric-value split, {kindLower} and the template constant do
not depend on either and can land any time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions