--- name: nic-planning description: 'Task planning and approach strategy for NIC. Use when starting any non-trivial task, reading issues or specs, planning before implementing, or when asked to create a plan for a change.' --- # Planning and Task Approach ## Before Writing Code 1. **Read the requirement** — Understand what's being asked. Check linked issues, specs, or PRs for full context. 2. **Write acceptance criteria** — Before implementing, state what "done" looks like in testable terms. What should work? What should be rejected? What must NOT break? 3. **Identify security impact** — Does this change: - Accept new external/untrusted input? → Requires input validation at the trust boundary - Touch credential or secret paths? → Requires human review - Generate config from user data? → Must pass through `containsDangerousChars()` - Change RBAC or access control? → Requires security review 4. **Identify affected layers** — Determine which architectural layers are touched: - Data Model (`pkg/apis/configuration/v1/types.go`) - Validation (`pkg/apis/configuration/validation/`) - Controller (`internal/k8s/`) - Config Generation (`internal/configs/`) - Templates (`internal/configs/version1/` or `version2/`) - Process Management (`internal/nginx/`) - Helm Chart (`charts/nginx-ingress/`) 5. **Check invariants** — Review the Key Invariants section in AGENTS.md: - Security: `containsDangerousChars()` on user strings reaching NGINX config - Codegen: Never edit `zz_generated.deepcopy.go` manually - Templates: Update BOTH OSS and Plus variants for shared directives; Plus-only directives go in the Plus template only - CRD fields: Every new field needs kubebuilder markers + validation + template + tests 6. **Identify test surface** — What tests need adding or updating? - Unit tests for validation logic - Negative tests for input rejection (security) - Snapshot tests for template output - Helm tests if chart changes - Integration tests if behaviour changes 7. **Produce a plan** — State your approach before coding. List files to change in order. Include what this change must NOT break. ## Layer Impact Checklist For any change, ask: - [ ] Does it accept new external input? → Add validation with `containsDangerousChars()` or appropriate sanitizer - [ ] Does it touch `types.go`? → Run `make update-codegen` then `make update-crds` - [ ] Does it add a template directive? → Update BOTH `nginx.ingress.tmpl` AND `nginx-plus.ingress.tmpl` (or v2 equivalents) if the directive is shared; Plus-only directives go in the Plus template alone - [ ] Does it add a CRD field? → Add kubebuilder markers, validation, template struct, rendering, tests - [ ] Does it touch Helm values? → Update `values.yaml`, `values.schema.json`, and helmunit tests - [ ] Does it affect config generation? → Add a snapshot fixture that exercises the change, then run `make test-update-snaps` - [ ] Does it change telemetry data types? → Run `make telemetry-schema` - [ ] Does user-controlled data reach NGINX config? → Verify sanitization path exists and is tested ## Definition of Done Do not report a task as complete until every applicable box is ticked. These are the steps most often skipped. | Condition | Required action | Verification | | --- | --- | --- | | Edited any `.tmpl` | Snapshot fixture added **and** regenerated | `git diff -- '**/__snapshots__/**'` is non-empty and shows the new directive | | Edited a template struct (`version1/config.go`, `version2/http.go`, `version2/stream.go`) | Fixture populates the field, snapshots regenerated | Same as above | | Edited `pkg/apis/**/types.go` | `make update-codegen && make update-crds` | `git status` shows regenerated `pkg/**`, `config/crd/bases`, `deploy/crds*.yaml`, `docs/crd/` | | Edited telemetry `Data` / `NICResourceCounts` | `make telemetry-schema` | No diff on re-run | | Added/changed imports | `go mod tidy` | `go.mod` / `go.sum` clean | | Edited chart templates or values | testdata + helmunit case | `charts/tests/__snapshots__/` diff is non-empty | | Added a pytest marker | Registered in `pyproject.toml` | Suite runs under `--strict-markers` | | Any of the above | `make test` then `make lint` | Both pass | **Snapshot rule:** regenerating without adding a fixture produces an empty diff, which is a *silent failure*, not a success. If `make test-update-snaps` changes nothing after a template edit, you have not tested the feature. ## Scope Assessment | Scope | Indicators | Action | | --- | --- | --- | | Trivial | Typo, docs, comment fix | Fix directly, no plan needed | | Small | Single layer, <50 lines, no API change | Brief plan → implement → test | | Medium | 2-3 layers, new field or annotation | Detailed plan → implement layer by layer → test each | | Large | New subsystem, new policy type, cross-cutting | Write plan document → get approval → implement in stages | ## Common Planning Mistakes - Starting implementation before understanding the full scope of affected files - Forgetting to update BOTH OSS and Plus templates for a shared directive -- or the inverse, leaking a Plus-only directive into the OSS template - Changing `types.go` without running codegen - Adding a VirtualServer feature without checking if Ingress (v1) also needs it - Adding Helm values without updating the JSON schema - Not checking if the feature already exists as an annotation when adding a CRD field - Skipping snapshot regeneration after template changes -- or regenerating without adding a fixture, which silently produces no diff - Forgetting `make telemetry-schema` after touching telemetry data types - Assuming a directive exists or behaves a certain way without checking -- verify NGINX semantics before wiring a template - Not identifying where untrusted input enters — a new CRD field IS user input that reaches NGINX config - Skipping negative tests — only testing the happy path leaves injection vectors undiscovered ## Ordering Rules for Multi-Layer Changes When a change spans multiple layers, implement in this order: 1. **Data model** — Define types/fields in `types.go` 2. **Codegen** — `make update-codegen && make update-crds` 3. **Validation** — Add validation rules in `pkg/apis/configuration/validation/` 4. **Config structs** — Add fields to template structs in `version1/` or `version2/` 5. **Config generation** — Wire the new field into config builders in `internal/configs/` 6. **Templates** — Add NGINX directives to `.tmpl` files (OSS + Plus) 7. **Controller** — Wire into sync handlers if needed 8. **Helm** — Update chart values, schema, templates 9. **Tests** — Unit, negative, snapshot (fixture + regenerate), helm, integration 10. **Regenerate** — `make update-codegen`, `make update-crds`, `make telemetry-schema`, `make test-update-snaps`, then `make test` and `make lint`