| name | add-api-resource |
| description | Add or change a REST API resource in agent-manager-service (the Go control plane). Use when the user asks to add/modify an endpoint, resource, route, or API operation in agent-manager-service — anything that touches the OpenAPI spec, controllers, services, repositories, or RBAC. Enforces the spec-first, codegen, and per-route-authz workflow so generated code and permissions stay consistent. |
Add/change an API resource (agent-manager-service)
Spec-first, layered, per-route-authz workflow for the Go control plane. The request/response types and mocks are generated — never hand-write them.
Read first: agent-manager-service/AGENTS.md → "Golden path", "Layering", "Permissions (RBAC)", "Code generation". This skill is the executable checklist; that guide has the why.
Steps (do in order)
- Edit the OpenAPI spec —
agent-manager-service/docs/api_v1_openapi.yaml. Add/modify the path, operation, and schemas. This is the source of truth for request/response shapes.
make spec (needs Docker) — regenerates spec/ from the YAML. The whole spec/ dir is deleted and rebuilt, so never hand-edit a struct there; your edits would be lost.
- Add permission(s) in
rbac/permissions.go — name them resource:verb (:create/:read/:update/:delete, or :manage only where peers already do).
- Implement controller → service → repository (
controllers/ → services/ → repositories/):
- Controller: HTTP only — parse/validate, map result→status, translate sentinel errors→HTTP.
- Service: business logic; depend on repo/client interfaces, never concrete types.
- Repository: persistence only (interface + impl). Add an interface if you introduce a new one.
- Register the route with authz in
api/<resource>_routes.go using HandleFuncWithValidationAndAuthz(pattern, rbac.<Perm>, ctrl.<Handler>). Every route declares its own permission.
- Grant the permission to at least one role in
rbac/predefined_roles.go (PredefinedRolePermissions). An ungranted permission is unreachable.
make codegen — only if you added/changed an interface (regenerates wire DI + mocks; needs moq on PATH). A new repo interface needs a //go:generate moq ... directive above its declaration — copy an existing one.
- Write a service unit test — use the
add-service-unit-test skill.
Audit logging
Step 5 audits the route automatically — a mutating route cannot ship unaudited, and api/audit_coverage_test.go fails the build if one does. Two cases need you:
- The build says
cannot derive an action for audited route, or the route's rbac.Permission does not describe what it does (one permission gating several operations, or naming a different resource). Add one line to actionOverrides in audit/policy.go.
- The operation touches credentials, privileges, membership, deployment or deletion. The envelope record cannot say what changed, so add a semantic event — use the
add-audit-event skill.
Never read a request or response body into a record; pass named fields via audit.Detail, which takes scalars and []string only.
Guardrails
- Multi-tenancy: the service must validate the caller's org against the target resource it loads, not just the path (
RequireOrgMatch is a first-pass filter, not the enforcement layer).
- Errors: map
gorm.ErrRecordNotFound → the specific utils.ErrXxxNotFound; wrap everything else with %w. Never flatten an unexpected error into not-found; never silently fall back to a default on a real error. Compare with errors.Is.
- Context: every I/O method takes
context.Context first and propagates it.
- Don't re-fetch a resource already loaded earlier in the request path — pass it down.
Done checklist