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
Copy file name to clipboardExpand all lines: .cursor/rules/controller-reconcile-helper-apply.mdc
+9-7Lines changed: 9 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -167,11 +167,12 @@ func applyFoo(
167
167
168
168
---
169
169
170
-
## Flow phases and **Outcome**
170
+
## Flow phase scopes and outcomes
171
171
172
-
- **ApplyReconcileHelpers** MUST NOT create a `reconcile/flow` **phase** (they do not accept `ctx context.Context`; see `controller-reconcile-helper.mdc`).
173
-
- **ApplyReconcileHelpers** MUST NOT return **Outcome** (in code: `flow.Outcome`) (they are “in-memory write” steps).
174
-
- If a failure is possible, return `error` and let the calling function convert it into `flow.Fail(err)` (or equivalent **flow** handling).
172
+
- **ApplyReconcileHelpers** MUST NOT create a `reconcile/flow` **phase scope** (they do not accept `ctx context.Context`; see `controller-reconcile-helper.mdc`).
173
+
- **ApplyReconcileHelpers** MUST NOT return **ReconcileOutcome** (`flow.ReconcileOutcome`) or **EnsureOutcome** (`flow.EnsureOutcome`) (they are “in-memory write” steps).
174
+
- If a failure is possible, return `error` and let the caller convert it into a flow result in its own scope
175
+
(for example, `rf.Fail(err)` in a reconcile scope or `ef.Err(err)` in an ensure scope).
- They MAY use **ConstructionReconcileHelpers** (`new*`, `build*`, `make*`, `compose*`) for internal in-memory construction, as long as the compute helper’s purity/determinism/non-I/O contract remains satisfied.
24
24
- They treat `obj` and all caller-provided inputs as **read-only inputs** and MUST NOT mutate them (including via **Aliasing** of maps/slices; **Clone** before modifying derived maps/slices).
25
25
- They MUST NOT perform **Kubernetes API I/O**, call **DeepCopy**, execute patches, or make any **patch ordering** / **patch type decision** decisions.
26
-
- If a **ComputeReconcileHelper** returns **Outcome** (in code: `flow.Outcome`), it MUST use it only for **flow control** (continue/done/requeue) and/or **errors**.
27
-
- A **ComputeReconcileHelper** MUST NOT use **Outcome** change tracking (`ReportChanged`, `ReportChangedIf`) or **Optimistic-lock signaling** (`RequireOptimisticLock`).
26
+
- A **ComputeReconcileHelper** MUST return computed values (and optionally `error`) and MUST NOT report object mutations or optimistic-lock intent.
27
+
In particular, a **ComputeReconcileHelper** MUST NOT return `flow.EnsureOutcome` and MUST NOT call `ReportChanged*` / `RequireOptimisticLock`.
28
28
- If `computeTarget*` derives **target** values for **both** **patch domains** (**main patch domain** + **status patch domain**) that will later be used by **IsInSyncReconcileHelper** and/or **ApplyReconcileHelper**, it MUST return **two separate** values (**target main** + **target status**), not a mixed struct.
29
29
- New code MUST NOT introduce `computeDesired*` helpers. Replace legacy “desired” helpers with **intended**/**target**/**report** helpers.
30
30
- If a **ComputeReconcileHelper** depends on previous compute output, the dependency MUST be explicit in the signature as args **after `obj`**.
@@ -159,44 +159,36 @@ Or, for **report** computations when the helper needs data from `Reconciler`:
> This keeps the call site clean and avoids `(flow.Outcome, DesiredFoo, error)` tuples.
199
-
200
192
### Dependent compute
201
193
If a compute helper depends on previous compute output, the dependency MUST be explicit and come **after `obj`**:
202
194
```go
@@ -257,7 +249,7 @@ See the common determinism contract in `controller-reconcile-helper.mdc`.
257
249
In particular, avoid producing “equivalent but different” outputs across runs (e.g., unstable ordering).
258
250
- **ComputeReconcileHelpers** MAY use extracted computation/caching components owned by the reconciler (e.g. “world view” / “planner” / “topology scorer”, caches), as described in `controller-file-structure.mdc` (“Additional components”), as long as they do not violate the I/O boundaries above.
259
251
- Note: cache population is a side effect and an additional source of state; therefore, the helper is deterministic only relative to that state. For the same explicit inputs and the same state of these components, the result MUST be the same.
260
-
- If a **ComputeReconcileHelper** returns **Outcome** (in code: `flow.Outcome`), its **flow decision** and **error** MUST be stable for the same inputs and object state.
252
+
- Errors (when returned) MUST be stable for the same inputs and object state (no nondeterministic branching / hidden I/O).
261
253
262
254
> Practical reason: nondeterminism creates patch churn and flaky tests.
263
255
@@ -277,44 +269,42 @@ See the common read-only contract in `controller-reconcile-helper.mdc` (especial
277
269
278
270
---
279
271
280
-
## Flow phases and **Outcome**
272
+
## Phase scopes (optional)
281
273
282
-
- A **ComputeReconcileHelper** MUST NOT create a `reconcile/flow` **phase** by default.
283
-
- A **large** **ComputeReconcileHelper** MAY create a `reconcile/flow` **phase** (`flow.BeginPhase` / `flow.EndPhase`) **only when it improves structure or diagnostics**.
284
-
- Otherwise (small/straightforward compute), it MUST NOT create a **phase**.
285
-
- If it creates a **phase** (or writes logs), it MUST accept `ctx context.Context` (see `controller-reconcile-helper.mdc`).
286
-
- If a **ComputeReconcileHelper** returns **Outcome** (in code: `flow.Outcome`), it MUST use helpers from `internal/reconciliation/flow`:
### Change reporting and optimistic-lock signaling
290
282
291
-
**ComputeReconcileHelpers** MUST NOT report object changes or optimistic-lock requirements via **Outcome** (in code: `flow.Outcome`):
283
+
**ComputeReconcileHelpers** MUST NOT report object changes or optimistic-lock requirements:
284
+
- MUST NOT return `flow.EnsureOutcome`
292
285
- MUST NOT call `ReportChanged` / `ReportChangedIf`
293
286
- MUST NOT call `RequireOptimisticLock`
294
287
295
-
Rationale: `Outcome.DidChange()` / `Outcome.OptimisticLockRequired()` semantically mean
288
+
Rationale: change reporting / optimistic-lock intent semantically mean
296
289
“this helper already mutated the target object and the subsequent save of that mutation must use **Optimistic locking** semantics”.
297
290
**ComputeReconcileHelpers** do not mutate `obj` by contract.
298
291
299
-
---
300
-
301
-
### Returning results when using **Outcome**
302
-
303
-
If a **ComputeReconcileHelper** returns **Outcome** (in code: `flow.Outcome`), it MAY write its computed result into an explicit output argument passed by pointer (e.g. `*DesiredState` / `*ActualState`) instead of returning that result as an additional return value.
func computeIntendedFoo(ctx context.Context, obj *v1alpha1.Foo, out *IntendedFoo) (err error) {
296
+
sf := flow.BeginStep(ctx, "compute-intended-foo")
297
+
defer sf.OnEnd(&err)
298
+
ctx = sf.Ctx()
299
+
310
300
if out == nil {
311
-
return flow.Fail(fmt.Errorf("out is nil"))
301
+
return sf.Errf("out is nil")
312
302
}
313
303
314
304
// compute into *out (pure)
315
-
*out = IntendedX{ /* ... */ }
305
+
*out = IntendedFoo{ /* ... */ }
316
306
317
-
return flow.Continue()
307
+
return nil
318
308
}
319
309
```
320
310
@@ -368,7 +358,6 @@ Notes (SHOULD):
368
358
369
359
- See the common error handling rules in `controller-reconcile-helper.mdc`.
370
360
- **ComputeReconcileHelpers** SHOULD generally return errors as-is.
371
-
- If a **ComputeReconcileHelper** returns **Outcome** (in code: `flow.Outcome`), use `flow.Fail(err)` for errors.
372
361
373
362
**Allowed (rare)**: when propagating a **non-local** error (e.g., from parsing/validation libs or injected pure components) and additional context is necessary to **disambiguate multiple different error sources** within the same calling **Reconcile method**, a **ComputeReconcileHelper** MAY wrap with small, local context:
- return `flow.ReconcileOutcome` / `flow.EnsureOutcome` or make flow/patch orchestration decisions (patch ordering/strategy/execution).
31
31
- MUST be plain functions (no `Reconciler` receiver) and may only call other **construction** helpers.
32
32
- If the primary goal is a reconciliation pipeline artifact (**intended/actual/target/report**) or domain decision-making, prefer **ComputeReconcileHelper** (`compute*`) and use construction helpers only as sub-steps.
33
33
@@ -244,10 +244,10 @@ Important distinctions:
244
244
245
245
---
246
246
247
-
## Flow phases and **Outcome**
247
+
## Flow phase scopes and outcomes
248
248
249
-
- **ConstructionReconcileHelpers** MUST NOT create a `reconcile/flow` **phase**.
250
-
- **ConstructionReconcileHelpers** MUST NOT return **Outcome** (in code: `flow.Outcome`).
249
+
- **ConstructionReconcileHelpers** MUST NOT create a `reconcile/flow` **phase scope** (`flow.BeginReconcile` / `flow.BeginEnsure` / `flow.BeginStep`).
250
+
- **ConstructionReconcileHelpers** MUST NOT return **ReconcileOutcome** (`flow.ReconcileOutcome`) or **EnsureOutcome** (`flow.EnsureOutcome`).
251
251
- **ConstructionReconcileHelpers** MUST NOT log (they do not accept `ctx context.Context`).
252
252
253
253
---
@@ -284,16 +284,18 @@ func newFoo(ctx context.Context, c client.Client, obj *v1alpha1.Foo) (FooOut, er
- Prefer encoding retry/requeue policy explicitly in the returned **Outcome**.
145
+
- **CreateReconcileHelpers** MUST NOT create a `reconcile/flow` **phase scope** — they should stay mechanical and short.
146
+
- **CreateReconcileHelpers** MUST return `error` and MUST NOT return **ReconcileOutcome** (`flow.ReconcileOutcome`) or **EnsureOutcome** (`flow.EnsureOutcome`).
147
+
- Any retry/requeue policy belongs to the calling **Reconcile method** (use `ReconcileFlow` there).
157
148
158
149
---
159
150
160
151
## Error handling
161
152
162
153
- See the common error handling rules in `controller-reconcile-helper.mdc`.
163
154
- A **CreateReconcileHelper** SHOULD be mechanically thin: if the single `Create(...)` call fails, return the error **without wrapping**.
164
-
- If returning **Outcome** (in code: `flow.Outcome`), use `flow.Fail(err)` (or equivalent) with the original `err`.
165
155
- A **CreateReconcileHelper** MUST NOT enrich errors with additional context (including **object identity** such as `namespace/name`, UID, object key).
166
156
- Error enrichment (action + **object identity** + **phase**) is the calling **Reconcile method**’s responsibility.
167
157
@@ -171,18 +161,18 @@ See the common read-only contract in `controller-reconcile-helper.mdc` (especial
171
161
172
162
❌ Doing existence checks (`Get/List`) or any extra Kubernetes API calls:
0 commit comments