Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions .agents/skills/add-dependency/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Use a dependency when your controller needs to:

## Key Principles

See also "Dependency Timing" in @.agents/skills/new-controller/patterns.md
See also "Dependency Timing" in [patterns.md](../new-controller/patterns.md)

### 1. Resolve Dependencies Late

Expand Down Expand Up @@ -156,6 +156,8 @@ func (c myReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.

In `actuator.go`, resolve the dependency before using it:

Use `orcv1alpha1.IsAvailable` as the readiness predicate. This is sufficient because `Status.ID` is always set before a resource becomes Available:

```go
func (actuator myActuator) CreateResource(ctx context.Context, obj *orcv1alpha1.MyResource) (*osResourceT, progress.ReconcileStatus) {
resource := obj.Spec.Resource
Expand All @@ -164,9 +166,7 @@ func (actuator myActuator) CreateResource(ctx context.Context, obj *orcv1alpha1.
if resource.ProjectRef != nil {
project, reconcileStatus := projectDependency.GetDependency(
ctx, actuator.k8sClient, obj,
func(dep *orcv1alpha1.Project) bool {
return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil
},
orcv1alpha1.IsAvailable,
)
if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule {
return nil, reconcileStatus
Expand All @@ -189,9 +189,7 @@ func (actuator myActuator) ListOSResourcesForImport(ctx context.Context, obj orc

project, rs := dependency.FetchDependency(
ctx, actuator.k8sClient, obj.Namespace, filter.ProjectRef, "Project",
func(dep *orcv1alpha1.Project) bool {
return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil
},
orcv1alpha1.IsAvailable,
)
reconcileStatus = reconcileStatus.WithReconcileStatus(rs)

Expand Down Expand Up @@ -235,7 +233,7 @@ Create dependency tests in `internal/controllers/<kind>/tests/<kind>-dependency/
- Test that resource waits for dependency
- Test that dependency deletion is blocked (if using DeletionGuard)

Follow @.agents/skills/testing/SKILL.md for running unit tests, linting, and E2E tests.
Follow [testing](../testing/SKILL.md) for running unit tests, linting, and E2E tests.

## Checklist

Expand Down
23 changes: 21 additions & 2 deletions .agents/skills/new-controller/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,28 @@ Implement:
- `ListOSResourcesForAdoption()` - Match by spec fields
- `GetResourceReconcilers()` - (if resource supports updates)

**ReconcileResourceActuator is optional**: The generic reconciler detects it via type assertion at runtime — there is no factory method to implement. To opt in, add the `reconcileResourceActuator` type alias and interface assertion in `actuator.go`, then implement `GetResourceReconcilers` on the actuator struct:

```go
type (
reconcileResourceActuator = interfaces.ReconcileResourceActuator[orcObjectPT, osResourceT]
resourceReconciler = interfaces.ResourceReconciler[orcObjectPT, osResourceT]
)

var _ reconcileResourceActuator = myActuator{}

func (actuator myActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) {
return []resourceReconciler{
actuator.updateResource,
}, nil
}
```

If the resource is fully immutable (no mutable fields, no tags, no sub-resources), skip this entirely — the generic reconciler will not call it.

### Implementation Patterns

Follow the patterns in @.agents/skills/new-controller/patterns.md when implementing the actuator and API types.
Follow the patterns in [patterns.md](patterns.md) when implementing the actuator and API types.

### Status Writer (internal/controllers/<kind>/status.go)

Expand All @@ -217,7 +236,7 @@ Implement:

Complete the scaffolded API validation test in `test/apivalidations/<kind>_test.go` by adding tests for any resource-specific validations (enums, numeric ranges, tag uniqueness, format validation, cross-field rules). Look for `TODO(scaffolding)` markers in the generated file.

Complete the E2E test stubs in `internal/controllers/<kind>/tests/` and run tests following @.agents/skills/testing/SKILL.md
Complete the E2E test stubs in `internal/controllers/<kind>/tests/` and run tests following [testing](../testing/SKILL.md)

## Checklist

Expand Down
40 changes: 4 additions & 36 deletions .agents/skills/new-controller/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,49 +85,17 @@ Distinguish between errors that can be retried vs those requiring user action.
| **Retryable** (default) | Transient issues (network, API unavailable) | Automatic retry with backoff |
| **Terminal** | Invalid configuration, bad input, permission denied | No retry until spec changes |

```go
// Terminal on create: User must fix the spec
if err != nil {
if !orcerrors.IsRetryable(err) {
err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration,
"invalid configuration creating resource: "+err.Error(), err)
}
return nil, progress.WrapError(err)
}

// Terminal on update: Treat as terminal (spec likely conflicts with existing state)
if err != nil {
if !orcerrors.IsRetryable(err) {
err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration,
"invalid configuration updating resource: "+err.Error(), err)
}
return progress.WrapError(err)
}
```
Use `orcerrors.IsRetryable(err)` to check; wrap non-retryable errors with `orcerrors.Terminal()`. See AGENTS.md "Error Classification" for the code pattern.

## 5. Dependency Timing

Resolve dependencies as late as possible, as close to the point of use as possible.

**Rationale**: Avoid injecting dependency requirements where not strictly required. This reduces coupling and gives users greater flexibility when fixing failed deployments.
Resolve dependencies as late as possible, as close to the point of use as possible. Only fetch a dependency when you actually need its ID for the current operation.

**Examples:**
- A Subnet depends on Network for creation, but not for import by ID or deletion
- Don't require recreating a deleted Network just to delete a Subnet whose `status.ID` is already set
- Add finalizers to dependencies only immediately before the OpenStack create/update call that references them

```go
// Good: Only fetch dependency when needed for creation
if resource.VipSubnetRef != nil {
subnet, depRS := subnetDependency.GetDependency(ctx, ...)
reconcileStatus = reconcileStatus.WithReconcileStatus(depRS)
}

// Bad: Fetching dependency unconditionally even when not needed
subnet, depRS := subnetDependency.GetDependency(ctx, ...) // Wrong if subnet is optional
```
- Only fetch optional dependencies conditionally (`if resource.SubnetRef != nil`)

For detailed dependency implementation: @.agents/skills/add-dependency/SKILL.md
For detailed implementation: [add-dependency](../add-dependency/SKILL.md)

## 6. Code Clarity

Expand Down
71 changes: 7 additions & 64 deletions .agents/skills/proposal/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,45 +91,12 @@ Before writing a proposal, ask the user about:

## Research Phase

Before writing the proposal, research relevant areas based on the enhancement type:
Before writing the proposal, research the relevant area:

### For Controller Enhancements

1. **OpenStack API**
- Read the OpenStack API documentation for the resource
- Identify required vs optional fields
- Understand resource lifecycle (creation, updates, deletion)
- Check for async operations (polling requirements)

2. **Gophercloud Support**
- Check if gophercloud has client support for this resource
- Identify the module path and types
- Note any missing functionality that needs upstream work

3. **Existing Patterns**
- Look at similar controllers in ORC for patterns to follow
- Identify if existing utilities can be reused
- Check if new generic functionality is needed

4. **Dependencies**
- Map out all ORC resource dependencies
- Determine which are required vs optional
- Identify deletion guard requirements

### For Infrastructure Enhancements (metrics, webhooks, etc.)

1. **Current Implementation**
- Check existing code for related functionality (e.g., `cmd/manager/`, `internal/`)
- Identify current ports, endpoints, and configurations
- Verify technical details by reading the actual code

2. **Framework Capabilities**
- Check controller-runtime documentation for built-in features
- Identify what's provided vs what needs custom implementation

3. **Integration Points**
- How does this integrate with existing infrastructure?
- What configuration already exists that this should use?
- **OpenStack API & gophercloud**: Read the API docs, check gophercloud support, note async operations
- **Existing ORC patterns**: Look at similar controllers or infrastructure code for patterns to follow
- **Dependencies**: Map ORC resource dependencies (required vs optional, deletion guards)
- **Current implementation**: For infrastructure enhancements, read the actual code to verify technical details (ports, endpoints, framework capabilities)

## Filling Out the Template

Expand Down Expand Up @@ -194,33 +161,9 @@ Address each of these in the **Risks and Edge Cases** section:
| **OpenStack compatibility** | Does this work across different OpenStack versions? (N/A for K8s-only) |
| **Interaction with existing features** | Could this conflict with existing behavior? |

### Verification Before Submission

Before finalizing the proposal:

1. **Internal consistency**: Verify anything referenced in one section is defined elsewhere
- If you mention a metric/API/config in mitigations, ensure it's defined in Proposal
- If you reference a flag, show its usage

2. **Technical accuracy**: Verify details against actual code
- Check ports, endpoints, paths in the codebase
- Verify framework capabilities match what you describe

3. **Completeness**: Ensure examples are complete and correct
- Code examples should compile conceptually
- Config examples should be valid YAML/JSON

## Tips for Writing Good Enhancements

1. **Be concise but complete** - Include enough detail for reviewers to understand the proposal without unnecessary verbosity.

2. **Focus on the "why"** - Motivation is often more important than implementation details.

3. **Think about edge cases** - The Risks and Edge Cases section is where you demonstrate you've thought through the implications.

4. **Consider alternatives** - Showing that you've evaluated other approaches strengthens your proposal.
### Before Submitting

5. **Keep it updated** - As implementation progresses, update the Implementation History section.
Verify internal consistency (anything referenced in one section is defined elsewhere), technical accuracy (check against actual code), and that examples are complete.

## Submission Process

Expand Down
Loading
Loading