Skip to content

deps(deps): Bump github.com/onsi/gomega from 1.36.1 to 1.38.0 - #6

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/go_modules/github.com/onsi/gomega-1.38.0
Closed

deps(deps): Bump github.com/onsi/gomega from 1.36.1 to 1.38.0#6
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/go_modules/github.com/onsi/gomega-1.38.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 31, 2025

Copy link
Copy Markdown
Contributor

Bumps github.com/onsi/gomega from 1.36.1 to 1.38.0.

Release notes

Sourced from github.com/onsi/gomega's releases.

v1.38.0

1.38.0

Features

  • gstruct handles extra unexported fields [4ee7ed0]

Fixes

  • support [] in IgnoringTopFunction function signatures (#851) [36bbf72]

Maintenance

  • Bump golang.org/x/net from 0.40.0 to 0.41.0 (#846) [529d408]
  • Fix typo [acd1f55]
  • Bump google.golang.org/protobuf from 1.36.5 to 1.36.6 (#835) [bae65a0]
  • Bump nokogiri from 1.18.4 to 1.18.8 in /docs (#842) [8dda91f]
  • Bump golang.org/x/net from 0.39.0 to 0.40.0 (#843) [212d812]
  • Bump github.com/onsi/ginkgo/v2 from 2.23.3 to 2.23.4 (#839) [59bd7f9]
  • Bump nokogiri from 1.18.1 to 1.18.4 in /docs (#834) [328c729]
  • Bump uri from 1.0.2 to 1.0.3 in /docs (#826) [9a798a1]
  • Bump golang.org/x/net from 0.37.0 to 0.39.0 (#841) [04a72c6]

v1.37.0

1.37.0

Features

  • add To/ToNot/NotTo aliases for AsyncAssertion [5666f98]

v1.36.3

1.36.3

Maintenance

  • bump all the things [adb8b49]
  • chore: replace interface{} with any [7613216]
  • Bump google.golang.org/protobuf from 1.36.1 to 1.36.5 (#822) [9fe5259]
  • remove spurious "toolchain" from go.mod (#819) [a0e85b9]
  • Bump golang.org/x/net from 0.33.0 to 0.35.0 (#823) [604a8b1]
  • Bump activesupport from 6.0.6.1 to 6.1.7.5 in /docs (#772) [36fbc84]
  • Bump github-pages from 231 to 232 in /docs (#778) [ced70d7]
  • Bump rexml from 3.2.6 to 3.3.9 in /docs (#788) [c8b4a07]
  • Bump github.com/onsi/ginkgo/v2 from 2.22.1 to 2.22.2 (#812) [06431b9]
  • Bump webrick from 1.8.1 to 1.9.1 in /docs (#800) [b55a92d]
  • Fix typos (#813) [a1d518b]

v1.36.2

Maintenance

Changelog

Sourced from github.com/onsi/gomega's changelog.

1.38.0

Features

  • gstruct handles extra unexported fields [4ee7ed0]

Fixes

  • support [] in IgnoringTopFunction function signatures (#851) [36bbf72]

Maintenance

  • Bump golang.org/x/net from 0.40.0 to 0.41.0 (#846) [529d408]
  • Fix typo [acd1f55]
  • Bump google.golang.org/protobuf from 1.36.5 to 1.36.6 (#835) [bae65a0]
  • Bump nokogiri from 1.18.4 to 1.18.8 in /docs (#842) [8dda91f]
  • Bump golang.org/x/net from 0.39.0 to 0.40.0 (#843) [212d812]
  • Bump github.com/onsi/ginkgo/v2 from 2.23.3 to 2.23.4 (#839) [59bd7f9]
  • Bump nokogiri from 1.18.1 to 1.18.4 in /docs (#834) [328c729]
  • Bump uri from 1.0.2 to 1.0.3 in /docs (#826) [9a798a1]
  • Bump golang.org/x/net from 0.37.0 to 0.39.0 (#841) [04a72c6]

1.37.0

Features

  • add To/ToNot/NotTo aliases for AsyncAssertion [5666f98]

1.36.3

Maintenance

  • bump all the things [adb8b49]
  • chore: replace interface{} with any [7613216]
  • Bump google.golang.org/protobuf from 1.36.1 to 1.36.5 (#822) [9fe5259]
  • remove spurious "toolchain" from go.mod (#819) [a0e85b9]
  • Bump golang.org/x/net from 0.33.0 to 0.35.0 (#823) [604a8b1]
  • Bump activesupport from 6.0.6.1 to 6.1.7.5 in /docs (#772) [36fbc84]
  • Bump github-pages from 231 to 232 in /docs (#778) [ced70d7]
  • Bump rexml from 3.2.6 to 3.3.9 in /docs (#788) [c8b4a07]
  • Bump github.com/onsi/ginkgo/v2 from 2.22.1 to 2.22.2 (#812) [06431b9]
  • Bump webrick from 1.8.1 to 1.9.1 in /docs (#800) [b55a92d]
  • Fix typos (#813) [a1d518b]

1.36.2

Maintenance

  • Bump google.golang.org/protobuf from 1.35.1 to 1.36.1 (#810) [9a7609d]
  • Bump golang.org/x/net from 0.30.0 to 0.33.0 (#807) [b6cb028]
  • Bump github.com/onsi/ginkgo/v2 from 2.20.1 to 2.22.1 (#808) [5756529]
  • Bump nokogiri from 1.16.3 to 1.16.5 in /docs (#757) [dabc12e]
Commits
  • c1237df v1.38.0
  • 36bbf72 support [] in IgnoringTopFunction function signatures (#851)
  • 4ee7ed0 gstruct handles extra unexported fields
  • 529d408 Bump golang.org/x/net from 0.40.0 to 0.41.0 (#846)
  • acd1f55 Fix typo
  • bae65a0 Bump google.golang.org/protobuf from 1.36.5 to 1.36.6 (#835)
  • 8dda91f Bump nokogiri from 1.18.4 to 1.18.8 in /docs (#842)
  • 212d812 Bump golang.org/x/net from 0.39.0 to 0.40.0 (#843)
  • 59bd7f9 Bump github.com/onsi/ginkgo/v2 from 2.23.3 to 2.23.4 (#839)
  • 328c729 Bump nokogiri from 1.18.1 to 1.18.4 in /docs (#834)
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.36.1 to 1.38.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](onsi/gomega@v1.36.1...v1.38.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-version: 1.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file go Pull requests that update go code labels Jul 31, 2025
@dependabot @github

dependabot Bot commented on behalf of github Jul 31, 2025

Copy link
Copy Markdown
Contributor Author

Reviewers

The following users could not be added as reviewers: @gitops-reverser-maintainers. Either the username does not exist or it does not have the correct permissions to be added as a reviewer.

Assignees

The following users could not be added as assignees: @gitops-reverser-maintainers. Either the username does not exist or it does not have the correct permissions to be added as an assignee.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot @github

dependabot Bot commented on behalf of github Jul 31, 2025

Copy link
Copy Markdown
Contributor Author

The reviewers field in the dependabot.yml file will be removed soon. Please use the code owners file to specify reviewers for Dependabot PRs. For more information, see this blog post.

@dependabot @github

dependabot Bot commented on behalf of github Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

Superseded by #16.

@dependabot dependabot Bot closed this Aug 25, 2025
@dependabot
dependabot Bot deleted the dependabot/go_modules/github.com/onsi/gomega-1.38.0 branch August 25, 2025 17:30
sunib added a commit that referenced this pull request Jul 30, 2026
… GitTarget work

The maintainer review's still-open block (F6, F9, F10, F12's reference nit, §3's
pushbacks) and the queue's Tier 2 items (B4, B1, #5, #6) are all `feat(api)!` on the
same object as the layout model, so they are cheaper together. That is the weaker
half of the argument.

The stronger half is that four of them are one decision seen from different angles:
**the folder is described on the GitTarget, and the connection describes only the
connection.** `spec.layout` says what the folder is, `spec.mode` whether we write
it, `spec.suspend` whether we write it now, and `commitWindow`/`commit.message` how
those writes are batched and phrased. The last pair lives on `GitProvider` today,
which is why §3 says that object is doing three jobs. Shipping the layout alone
asserts the principle with one field while another contradicts it.

Two findings change the layout design rather than accompanying it, which is the
reason to combine rather than merely batch:

- **`spec.mode: Observe` becomes how a layout is adopted.** Placement only affects
  documents that do not exist yet, so a user declaring `kind: Kustomize` on a real
  repository has nothing to preview. Observe plus `status.layout` is a dry run:
  resolve the layout, publish what it would do, write nothing, then flip to Write.
  It also gives Observe a purpose beyond being a switch nobody uses.
- **`spec.interval` is what keeps that status fresh.** The scan-derived half of
  `status.layout` has a hole: a scan happens on a write or a resync, so a stable
  target may publish a revision from last week. A periodic observation pass closes
  it, and neither piece was proposed for this reason.

Also recorded: `suspend` is a precondition rather than a rider, because a layout
that creates a `kustomization.yaml` needs a stop button; F7 already shipped the
EventRecorder the placement Event was said to be too expensive for, so that open
question is now cheap; layout is mutable like `prune`, and deciding that now keeps
#6 from reopening it; F9 stays OUTSIDE the wave because its answer constrains the
enum work; and the version stays `v1alpha3` with a loud rejection for
`spec.placement` rather than paying for a conversion path while we have one consumer.

What rides along without a synergy claim is listed as such: #5, F10, the reference
types, the `TooManyStreams` cap, and the ClusterProvider "default" message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sunib added a commit that referenced this pull request Jul 30, 2026
… refused resource must not be registered (#291)

* feat(placement)!: a human's edit to the repository must not move where the operator writes

Option C's sibling-cohort ladder is gone: `resolveInferred` through `allSameDir`,
about a third of `placement.go`, plus the tests that pinned each rung. A new
document's destination now comes from the GitTarget's declared
`placement.byType`/`default`, or from the folder having exactly one supported
kustomization root, or from the built-in canonical path — never from where the
repository happens to keep the other documents of the same type.

The argument is in docs/design/open-asks-priority.md and it is not primarily
about the bug: inference let a human's edit to a repository change the operator's
behaviour with no Kubernetes object changing and nothing in status recording the
move. The bug is the evidence. Its namespace-agnosticism guard was vacuous on the
singleton branch for a period, so a new namespace's object was appended into the
first namespace's file, which then genuinely spanned two namespaces, which
legitimized the bundle for every later object, which collapsed a whole type into
one file. A rule inferred from mutable state has failure modes that feed
themselves, and the fix for that instance did not make the class safe.

The kustomize-root fallback stays, because it is not inference. A file no
kustomization can reach is not oddly placed, it is never rendered; placing the
new document beside the folder's one root follows from there being one root.
More than one is still ambiguous and still declines.

Two things came out of doing it —

- **Namespace inheritance moved to the governing kustomization, where it belongs.**
  "Omit metadata.namespace, the context supplies it" used to be read off a
  sibling's bytes, so it only ever fired for an inferred placement; a DECLARED
  path into the same directory silently wrote a `namespace:` line the folder's
  own documents omit. It is now decided once, in `finishPlacement`, for every
  resolved path.
- **And it must match, which the old kustomize-root path never checked.** Omitting
  the namespace hands it to kustomize, so a transformer naming a DIFFERENT
  namespace would render the document as another object entirely. The explicit
  line now stays in that case, and the render oracle reports a folder that cannot
  express the object instead of the mirror quietly claiming one it does not hold.

`PlacementResult.Cohort` is deleted with the ladder, and `PlacementSource`
gains `kustomize_root` in place of `inferred`, which now names one mechanism
rather than two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(metrics): count every new placement by source, and every one we refused

Placement had two signals and neither was reachable: a log line at the skip site,
and `ResyncStats.PlacementSkipped` — a field in a resync summary, not a series.
The spec's own P8 said the "why did it land there?" trace was mandatory and it was
never built. With sibling inference deleted, a hand-authored layout needs a
`placement.byType` line, so the question "which target and which type is missing
one" has to be answerable without reading the folder.

Three counters, all labelled by `{gittarget_namespace, gittarget_name, group,
version, resource}` — the GitTarget that owns the write and the exact shape of a
`placement.byType` key, so a series reads as the line the target needs:

- **`placements_total{source, disposition}`** — one increment per new document
  actually written. `source` is declared / kustomize_root / canonical;
  `disposition` is new_file / appended. `source="canonical"` is the missing-rule
  signal, and `kustomize_root` is deliberately NOT lumped in with it: a folder
  with one render root is placing files where they build, which is the correct
  answer with no declaration at all.
- **`placement_refusals_total{reason}`** — one increment per resource the writer
  declined to place, from a closed reason set (`invalid_path`,
  `sensitive_append`, `plaintext_onto_encrypted`, `mixed_sensitivity_new_file`,
  `multi_document_target`). Every increment is a resource absent from the mirror.
- **`placement_kustomization_entries_total{outcome}`** — added / no_change /
  failed for the `resources:` entry a new file needs. `failed` is the invisible
  one: the document is committed and the entry is not, so kustomize never builds
  the file — it is in Git, it looks mirrored, and nothing applies it.

Three decisions worth stating:

- **The two counters partition the population.** A placement is recorded after the
  write lands, not at resolution, and a refusal is recorded instead — never both.
  A refusal as a `source` value would have let a dashboard count a skipped Secret
  as a successful placement.
- **The reason is typed, not a matched message.** `PlacementRefusedError` carries a
  bounded `Reason`, so the label cannot drift when an error string is reworded, and
  the two writer-side refusals share the analyzer's label domain.
- **The GitTarget labels are the point.** The design doc argued against leading
  with a bare `placement_fell_back_total` precisely because "it happened
  somewhere" is not actionable. The label keys are `gittarget_*` rather than
  `namespace`/`name` for the pod-scrape reason `TargetReconcileCompletedTotal`
  documents.

The resync path carries the same labels, taken from the resolved target metadata
rather than the synthesised events: which of the two paths created a file is not
something the operator chose, so it must not change whether the placement is visible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: three placement steps, and the metric that says when you need a rule

The spec binds the code, so the ladder cannot be deleted from one and left in the
other. `gittarget-new-file-placement-rules.md` now documents three steps —
declared, the folder's one kustomize root, canonical — and keeps Option C's
sections as history, because the argument is worth having on the page and because
its own P1–P10 risk list is the case for the removal. Each risk is annotated with
what became of it: P1, P2, P3, P4, P6 and P8 are one property stated six times and
are retired; P7, P9 and P10 are facts about the code that remains. P8 in
particular stays visible — the explainability that spec made mandatory was never
built, and the smaller ladder gets the smaller obligation it deserves.

The kustomize-root fallback keeps its section and gains the namespace-match rule,
stated as a safety property rather than a convention: omitting `metadata.namespace`
hands the namespace to kustomize, so a transformer naming a different namespace
would render a different object than the one being mirrored.

User-facing:

- **`configuration.md`** replaces the "following the existing layout" section with
  the three-step ladder, a "knowing when you need a rule" section built on
  `placements_total{source="canonical"}`, and the refusal and kustomization-entry
  counters with what each reason means for a policy.
- **`UPGRADING.md`** carries the behaviour change: who is affected (a hand-authored
  folder, not one this operator created), the one `byType` line that buys the old
  behaviour back, the query that says whether it affects you, and why there is no
  `spec.placement.mode` to switch it back on.
- **`interpreting-metrics.md`** documents the three counters with a `source` table
  saying which values need attention (`kustomize_root` does not — a folder with one
  render root is placing files where they build), and the label-cardinality reasons.
- **`architecture.md`** and **`installing-apps-as-krm.md`** stop describing a step
  that no longer runs.

`open-asks-priority.md` strikes the entry and corrects itself where building it
proved the argument wrong: it had argued *against* leading with a Prometheus
counter, on the grounds that "it happened somewhere" is not actionable. That
objection was to the labels, and it does not survive them naming the GitTarget and
the type key. "What the deletion taught" records the rest — namespace inheritance
was a second implementation of a rule that belonged to the governing kustomization,
and the write path's missing GitTarget identity is the same fact that explains why
placement had no metrics and cannot easily have an Event.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(placement): let the metric helpers take no arguments they never vary

unparam is right: every caller passed the same name and namespace, so the parameters
were documentation of an intent the tests do not have.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(e2e): a build file gaining a resources: entry is the feature, not a broken folder

The manifest-folder spec asserted the committed `kustomization.yaml` was
byte-identical to the fixture's. That assertion was pinning a side effect of
sibling inference, and it failed as soon as the inference was gone.

The namespace holds a ConfigMap nobody in the test created — the cluster's own
`kube-root-ca.crt` — and the WatchRule selects every ConfigMap, so the operator
has a watched resource with no document in Git. Placement now gives it a file
beside the folder's one kustomization and registers it in `resources:`, which is
the documented kustomize-root behaviour and what the new-file-placement spec
asserts directly. Inference used to append that resource to the existing bundle
instead, and the bundle was already listed, so the build file happened to stay
byte-identical.

The property this spec is about is that a hand-authored build file is not
reordered, reformatted, or shortened. That is now asserted directly: every line
the fixture wrote survives in order, and anything added is a `resources:` entry.
It also states the reasoning in the helper, so nobody re-tightens it to equality
and rediscovers this.

`docs/UPGRADING.md` gains the concrete shape a user of a kustomize folder will
see: a new file plus an entry, where a bundle used to grow and the build file did
not change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): decide the three placement questions, and design status.layout

A decision record for the review questions on #291, written so the answers are
arguable rather than asserted. Everything it decides lands in that same PR.

**Keep `canonical`, and split `declared`.** Renaming the built-in path to
`default` collides with `spec.placement.default`, which is the opposite thing: a
declaration. A reader of `source="default"` could not tell whether their catch-all
matched or whether nothing matched at all. The resolution the metric was actually
missing is the other one, so `declared` becomes `byType` and `default` and the
prose stops calling the built-in path "the built-in default".

**No CRD default for `placement.default`, and the reason is concrete rather than
stylistic.** The idea is clearer for a reader of one object and I gave it too
little credit at first, so the document states it at full strength and then prices
it. Two prices: the versionless template we would default to is judged NOT
identity-complete by `validateSecretSafety`, so the CRD's own default would turn
`Validated=False` on for every target without an explicit Secret route; and a
persisted default becomes the user's data, which costs us the ability to improve
the built-in path for existing targets and the ability to distinguish "the user
asked for this" from "we suggested it" ever again. The order for revisiting it is
written down rather than left as a no.

**`status.layout` instead**, with five worked examples: greenfield, a kustomize
overlay, brownfield missing one rule, two ambiguous roots, and a refusal from an
operator-configured sensitive type the static gate cannot see. It answers the same
question from a derived field, so it cannot fork from the code and improves with
it, and it says the thing a spec field structurally cannot: what the operator
understood about the folder.

Three findings changed a decision:

- `IdentityCompletePlacementTemplate` requires `{version}` for a non-narrowed
  template, which contradicts the versionless-path decision and rejects templates
  that cannot collide two identities. A bug on its own terms, and the precondition
  for any future spec default.
- The data-plane-to-status seam the queue doc said did not exist does exist:
  `MarkTargetRetention` enqueues the GitTarget on a change, which is exactly the
  missing enqueue that made an Event look expensive.
- Two supported kustomizations still decline to canonical, where no root reaches
  the file. It is committed, looks mirrored, and is applied by nothing, and the
  entries counter cannot see it because no entry is attempted.

`{kindLower}` over a `toLower` function, because a function syntax is a language
and the spec's own "keep it small" already forbids one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): the reason not to default `placement.default` is the render root, not etcd

Review pushed back on two of the three arguments in this document and was right
about both, so the record now leads with the objection that survives.

- **"Just default the Secret route too" works, and is a trap.** A defaulted
  `byType["v1/secrets"]` is narrowed to one type, so it satisfies
  identity-completeness and unblocks the bundling-default check. But Kubernetes
  defaulting applies to an ABSENT field and never merges, so a user writing any
  `byType` entry of their own replaces the whole map, silently drops the Secret
  route, and flips the object to `Validated=False` on an edit about ConfigMaps.
- **The persistence argument was overstated.** A default is persisted on every
  spec-writing apply and applied in memory on read, but NOT by our own status
  writes (GitTarget has a status subresource, verified). And freezing the built-in
  path per target is arguably desirable, since placement is already create-time and
  non-retroactive. `metadata.managedFields` even records that the server set the
  value, so "indistinguishable from a declaration" was false. What is left is spec
  bloat, which decides nothing.
- **The objection that does decide it is structural.** `resolveDeclared` returns on
  any non-empty declared template, and the kustomize-root step runs after it. A
  defaulted `default` is never empty, so the render-root step becomes unreachable
  and every new file in an overlay takes the canonical path: in Git, looking
  mirrored, rendered by nothing. That is the exact failure the render-root step
  exists to prevent. The repairs invert something load-bearing — the render root
  beating a real declaration, or placement depending on field-ownership metadata.

It also sharpens why status is the right shape rather than the cautious one: status
can show the ladder without collapsing it, and a spec default can only express the
ladder by flattening it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): a declared path in a kustomize subdirectory is an unrendered file today

Review turned the argument about a hypothetical CRD default into a live bug, which
is the most useful finding on the page.

`governingKustomization` decides whether a new file gets a `resources:` entry by
looking in exactly two places: the kustomization in the file's own directory, and
the write scope's root — the latter only when render-root scoping is in force. So a
`byType` entry pointing into a subdirectory of a self-contained kustomize folder
("configmaps/{name}.yaml") produces a file no kustomization lists. It is committed,
it looks mirrored, kustomize never builds it, and because no entry is attempted
`placement_kustomization_entries_total` cannot see it either. An overlay reading a
base is registered correctly, by accident of a branch added for another reason.

The fix replaces both cases with one rule: walk up to the nearest kustomization
inside the write jail. It cannot escape the jail by construction, the relative entry
is what `appendKustomizationResource` already computes, and the already-listed check
is path-based so it stays idempotent. It goes first in the plan, because it is a
correctness fix rather than an observability improvement.

It also undercuts F9, and the page now says so instead of keeping an argument that
has been weakened. With the ancestor walk, defaulting `placement.default` would no
longer produce unrendered files. What survives is narrower and structural: a nested
tree registered inside an overlay renders but is the worse layout, and no template
can express "beside the folder's one supported kustomization", so a defaulted
template consumes the slot in front of the one step that exists because a path
cannot say what it says. The recommendation is unchanged; its grounds are smaller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): correct the status shape, the freshness claim, and two overstated arguments

A second review found five things wrong with this page, and four of them stand.

- `defaultSource: BuiltInCanonical` contradicted the `source: KustomizeRoot` on
  its own example. It is now `effectiveFallbackSource` with
  `DeclaredDefault | KustomizeRoot | Canonical`, which answers "what happens to a
  type I have not named?" in one word instead of describing a different axis.
- The retention roll-up proves an enqueue mechanism exists, not that placement
  status will be fresh. Retention reports on every resync; placement is sparse and
  may never fire for a stable target. The field is now explicitly two halves: a
  CURRENT half derived from the last scan and stamped with `observedRevision`, and
  a HISTORICAL half accumulated since it.
- `newFiles` was wrong for an append and `fallbackTypes` was loaded language for a
  folder whose canonical layout is intentional: `placedResources` and
  `canonicalTypes`, both defined as historical.
- `metadata.managedFields` is field-management bookkeeping, not durable provenance,
  so it cannot separate a declaration from a schema default. The claim is gone
  rather than merely hedged.
- "Keep writing, make it loud" was a policy choice stated as a consequence. It is
  open again: a committed manifest nothing applies manufactures a false appearance
  of convergence, which is the failure class this project ranks first. What is not
  open is that the signal must not be a third outcome on a counter that counts
  entry ATTEMPTS.

F5b's phrasing is narrowed too: the mechanism is that a default applies to an
absent field and is never re-merged per key, not that any write replaces the map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): declare what the folder is, not the path a file takes

The placement questions kept dead-ending because the primitive is wrong. A path
template cannot say "beside this folder's one kustomization" (which is why that
rung is not a template), cannot be read at a glance, and cannot bring a folder
into existence. So the proposal is to declare the LAYOUT.

`spec.layout.kind` with `Auto`, `Kustomize`, `Tree`, `Flat` and `Template`, plus
`byType` overrides valid under every kind. Two rules carry the value:

- **whatever chose the path, the file is registered with the kustomization that
  governs it.** F10 stops being a bug that one `byType` line reproduces and becomes
  something the model cannot express;
- **a structural kind excludes a blanket `default`.** "Kustomize folder AND a nested
  canonical tree" is statable today, and broken; here it is refused by validation.

That also dissolves the defaulting argument this document set out from. Defaults
were never the problem: defaulting a PATH was, because a path is the one thing that
cannot say "look at the folder". `kind: Auto` is a safe CRD default because it NAMES
the structural rule rather than standing in front of it, and it is declared
inference, which is the difference between it and the inference we deleted.

`kind: Kustomize` with `create: true` answers the bootstrapping ask: the first write
commits a folder `kubectl apply -k` can build, rather than a file that happens to be
YAML. Its boundary is stated too, because it is the obvious place for scope creep:
the layout may create only what its own invariant requires. A repository template is
a separate object with a separate lifecycle.

Seven worked examples, a status shape carrying `declaredKind` beside the resolved
`kind` so declared inference never reads as a user's decision, metric labels, and a
mechanical migration for every current configuration (the one behavior change being
that a declared template stops silently disabling the render root).

On whether the layout should be its own CRD: no, and the decisive argument is the
one this release is about. A shared object that changes where N folders write, with
nothing on the GitTarget recording it, is structurally the same defect as sibling
inference with a different actor. It also adds a readiness chain, cross-namespace
authorization, and a third place to look, to share four lines that a generator
already repeats for free. The reuse pressure is concentrated in a large `byType`
map, so that is what we would share first, projected into status so the target still
shows what it is doing. The trigger for revisiting is written down.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): sequence the layout model with the rest of the breaking GitTarget work

The maintainer review's still-open block (F6, F9, F10, F12's reference nit, §3's
pushbacks) and the queue's Tier 2 items (B4, B1, #5, #6) are all `feat(api)!` on the
same object as the layout model, so they are cheaper together. That is the weaker
half of the argument.

The stronger half is that four of them are one decision seen from different angles:
**the folder is described on the GitTarget, and the connection describes only the
connection.** `spec.layout` says what the folder is, `spec.mode` whether we write
it, `spec.suspend` whether we write it now, and `commitWindow`/`commit.message` how
those writes are batched and phrased. The last pair lives on `GitProvider` today,
which is why §3 says that object is doing three jobs. Shipping the layout alone
asserts the principle with one field while another contradicts it.

Two findings change the layout design rather than accompanying it, which is the
reason to combine rather than merely batch:

- **`spec.mode: Observe` becomes how a layout is adopted.** Placement only affects
  documents that do not exist yet, so a user declaring `kind: Kustomize` on a real
  repository has nothing to preview. Observe plus `status.layout` is a dry run:
  resolve the layout, publish what it would do, write nothing, then flip to Write.
  It also gives Observe a purpose beyond being a switch nobody uses.
- **`spec.interval` is what keeps that status fresh.** The scan-derived half of
  `status.layout` has a hole: a scan happens on a write or a resync, so a stable
  target may publish a revision from last week. A periodic observation pass closes
  it, and neither piece was proposed for this reason.

Also recorded: `suspend` is a precondition rather than a rider, because a layout
that creates a `kustomization.yaml` needs a stop button; F7 already shipped the
EventRecorder the placement Event was said to be too expensive for, so that open
question is now cheap; layout is mutable like `prune`, and deciding that now keeps
#6 from reopening it; F9 stays OUTSIDE the wave because its answer constrains the
enum work; and the version stays `v1alpha3` with a loud rejection for
`spec.placement` rather than paying for a conversion path while we have one consumer.

What rides along without a synergy claim is listed as such: #5, F10, the reference
types, the `TooManyStreams` cap, and the ClusterProvider "default" message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(design): the namespace is part of the layout, and the layout is immutable

Review connected four things this design had left apart, and each one changes it.

**Namespace scope belongs to the layout.** A folder that omits the namespace from
its paths is a folder for one namespace, and that assumption has to be carried by
the object that owns the folder. `layout.scope: SingleNamespace|MultiNamespace` is a
STRUCTURAL claim; `spec.allowedSourceNamespaces` is an AUTHORIZATION bound; they are
different questions about the same folder and admission now checks them against each
other. It cannot be derived instead: the matcher may be absent (which
`NamespaceMatcher` defines as "no policy declared", not "one namespace"), and the
namespaces that arrive come from N WatchRule objects that do not own the folder, so a
derived assumption could be invalidated later by an edit elsewhere. Declaring it turns
that invalidation into a counted refusal naming both namespaces instead of a collision.

**Whether the namespace is written into the file is inference today, and it is the one
inference an empty folder cannot perform.** `writeNamespace: FromContext|Always|Never`
makes it declarable. `Never` needs a guarantee, because omitting the namespace hands
the object to whatever namespace the applier is pointed at. And this closes the
bootstrap loop: `create: true` plus `SingleNamespace` lets the operator write
`namespace: team-a` into the kustomization it creates and then legitimately omit it
from every file. The convention is established rather than guessed.

**The layout is immutable, with a widening exception**, which moves it from `prune`'s
company to `path`'s. The deciding fact is checkable and I had not checked it:
GitTarget has NO finalizer, so deleting one leaves the folder untouched and
re-creating it at the same path re-adopts every document by identity. Changing a
layout by recreating the object costs status and a moment of mirroring, not data,
where `prune`'s mutability argument was that a recreate would destroy what cannot be
rebuilt. A mutable layout would leave a folder permanently half one structure and half
another, with nothing recording which file came from which. `Flat` to `Tree` widening
is allowed because it cannot lose identity-completeness; narrowing is what collides.

**And `Auto` resolves once and pins.** Immutability of a field that says "look at the
folder" pins nothing: delete the `kustomization.yaml` and `Auto` would silently become
`Tree`, which is the defect this release deleted, re-entering through a default value.
Pinning also settles whether `Auto` may be the default at all. It may, and the
quickstart stays four fields.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: escape the pipes that broke the INDEX table row

A markdownlint failure I pushed past: the previous commit's chain piped lint output
through tail, so the shell saw tail's exit status and committed anyway. The row's
`FromContext|Always|Never` was read as extra table cells.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: one attribution spec, and the GitTarget wave is postponed rather than pending

Attribution was documented across seven files while it was being built, and the
one named after the deletecollection expander outlived its subject: it still
documented the `result=` label and `attribution_collection_degraded_total`, both
of which the fact-stream switchover replaced. Six design records plus that spec
are folded into `docs/spec/attribution.md`, which binds and is Vale-gated.

Every metric name, tier constant and flag in it was checked against the tree
first. What survives elsewhere is the reasoning trail that is still worth
reading (`finished/attribution-fact-stream.md`) and the one decision still open
(`design/attribution-removal-wait-options.md`). Go and test comments that cited
the deleted files now cite the spec; nothing executable changed.

The placement page claimed eight items would land in PR #291. None of them did.
It shipped the sibling-inference deletion, the three placement counters and the
namespace-transformer safety fix, so the page now says which two of its
questions were answered by shipping and which six are decided and unbuilt, filed
as #295 (correctness) and #296 (visibility).

The declared-path-in-a-kustomize-subdirectory bug moves up to Tier 1 in the
queue. One ordinary `byType` line silently produces a file that is in Git and
rendered by nothing, with nothing in status or the counters saying so, which is
this page's own definition of the product being silently wrong. It had been
written down as a finding rather than ranked because it was found while arguing
about metric names.

The layout model and the API wave are postponed to a later deployment and
tracked as #293 and #294. 0.41.0 already replaces the whole attribution model
and breaks placement; a third breaking dimension, on the shape of GitTarget
itself, is a separate conversation. Every Tier 2 entry that changes a GitTarget
field is now marked wave-bound rather than independently schedulable, and Tier 1
is explicitly kept free of the wave so it does not wait for it.

Net 2,886 lines of markdown deleted, 794 added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(placement): a refused resource must not register its file with the kustomization

`appendKustomizationResource` ran before `placeNewDocument`, so a placement the
writer then refused still gained a `resources:` entry. The reachable case is the
multi-document refusal: the file exists, holds a document we cannot account for,
and we decline to own it — then registered it into the folder's render anyway,
counted as `outcome="added"`, which is the value that is supposed to mean "the
file we just wrote will build".

The mixed-sensitivity refusal cannot reach it, because it requires a document
already written at that path in the same batch, so its entry is legitimate. That
is why the fix is pinned by a multi-document fixture and asserts the
kustomization is byte-identical after the refusal rather than only that the
counter is zero.

Moved after `wroteBytes(outcome)`, where `recordPlacement` already sits and for
the same reason.

Review follow-ups in the same pass:

- `configuration.md` had the last two live claims that omitting `spec.placement`
  follows the repository's existing layout. Sibling inference is gone; it takes
  the folder's one kustomization root or the canonical path.
- `UPGRADING.md` gives the canonical path in full, since it is the page read to
  predict where a file lands: `_cluster/`, the omitted core group, no version
  segment, `.sops.yaml`.
- The placement spec said every resolution increments `placements_total`, which
  its own test contradicts. It is every successful placement.
- The maintainer review still scheduled F9 inside the API wave while its own
  introduction says F9 is deliberately outside it.
- `redis-key-schema-v3.md` describes the deleted expander. Repointing its link at
  the new spec in the previous commit made that claim look authoritative, so it
  is marked superseded and says what replaced it.
- The layout model's `Flat` example used `writeNamespace: Never`, which its own
  table forbids without a namespace guarantor — and `Flat` has no kustomization
  to write `namespace:` into. It is `Always`. The sharper gap is recorded as an
  open question: `SingleNamespace` constrains cardinality and never names the
  namespace, which is exactly what bootstrapping needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants