--- name: add-rpc description: Scaffold a new gRPC RPC end-to-end (proto, buf generate, hexagonal ports/app/adapter layers, tests, README) following this repo's established pattern across ha-gateway, ai-gateway, and discord-bot. --- # Add a gRPC RPC Use this whenever a new RPC or field is being added to `ha-gateway`, `ai-gateway`, or a new gRPC client call is being added to `discord-bot`. All three services share the same hexagonal layering, so follow this order — do not skip layers or reach across them. ## Steps 1. **Identify the target service and proto package.** - `ha-gateway` → `proto/ha/v1/*.proto` (packages: `entity`, `light`, `switch`, `event`, `common`) - `ai-gateway` → `proto/ai/v1/ai.proto` - `discord-bot` has no proto of its own; it only consumes `ha.v1` / `ai.v1` clients. 2. **Edit the `.proto` file** under `proto//v1/`. Add the message/RPC there first — it is the source of truth; `gen/` is derived from it. 3. **Regenerate generated code:** ```bash buf generate ``` This writes into `gen//v1/`. Never hand-edit files under `gen/`. 4. **Extend the port interface** in `internal/core/ports/`: - `ports/driving/*.go` — the interface the gRPC adapter calls *into* (app-facing, inbound). - `ports/driven/*.go` — the interface the app calls *out* to (HA REST, Ollama, ha-gateway client, etc.), if the new RPC needs a new external call. - `core/domain/*.go` — add/extend domain types here, not in adapter or proto types. 5. **Implement orchestration in `internal/app/`.** This is where business logic and validation live — it depends only on `core/domain` and `core/ports`, never on `adapters/*` types directly. Look at an existing file (e.g. `ha-gateway/internal/app/light.go`) for the shape: a struct holding driven-port dependencies, constructed via `NewXApp(...)`. 6. **Wire the primary adapter** in `internal/adapters/primary/grpc/.go`. Implement the generated server interface method, translate between proto types and domain types (see `mapping.go` in ha-gateway for the existing translation pattern), and call the app layer. Keep proto <-> domain conversion here, not in `app`. 7. **If the RPC needs a new outbound call**, implement it in the relevant `internal/adapters/secondary//client.go`, implementing the `driven` port interface from step 4. 8. **Add a test** using the repo's convention: plain `testing` package (no testify), with a hand-written mock struct that implements the driven port interface using function fields (see `ha-gateway/internal/app/entity_test.go` or `ai-gateway/internal/app/query_test.go` for the pattern). Table-driven `t.Run` subtests are the norm. 9. **Update the service's `README.md`:** - Move the RPC between "Implemented" and "Stubbed" in the gRPC API section if its status changed. - Add any new environment variables to the Configuration table (and to `.env.example`). - Add any new package to the Package Map section if you created one. 10. **Verify:** ```bash cd && go vet ./... && go test ./... ``` ## Layering rule to enforce Dependencies point inward only: `adapters` → `app` → `core`. Never import `internal/adapters/*` from `internal/app` or `internal/core`. Only `cmd//main.go` is allowed to import across all layers to wire dependencies together.