AGENTS.md@internal/service · git:20260914.583a8ed · 2026-09-14 · sha256 d3e593b170e7ed6f

AGENTS.md@internal/service git:20260914.583a8edA

Immutable. This exact content never changes and is served at /api/v1/blob/d3e593b170e7ed6f.

# Agent Instructions — `internal/service/`

These instructions apply to `internal/service/`. Defer to
[`internal/AGENTS.md`](../AGENTS.md) (format before push) and root
[`AGENTS.md`](../../AGENTS.md) for broader rules.

## Events stay in sync with mutations

Service mutate paths (create, update, delete, state flips, factor and authz
writes) own Path B emitters: `audit.Emit` in the same transaction as the
write. Adding, changing, or removing a mutation **must** keep events current
— including **adding** a type when a new semantic mutation appears, and
**removing** a type that no longer has a producer, unless rows were ever
stored under it, in which case it moves to the catalog's Retired table.

A change is incomplete if it:

- adds a mutate without an emit,
- changes a payload so it no longer matches the catalog, or
- removes a mutate but leaves the type in the live Path B table. A type
  that has stored rows moves to Retired and keeps its `EventType` constant
  and OpenAPI member so those rows still decode, until its rows have aged
  out of retention, when the type is removed.

Authority (do not duplicate payload rules or the catalog table here):

- Catalog: [`docs/design/api/events-catalog.md`](../../docs/design/api/events-catalog.md)
  (payload rules, live Path B table, Retired table, deferred list). Context:
  [ADR 048](../../docs/adrs/048-wide-events-internal-audit-primitive.md),
  [ADR 049](../../docs/adrs/049-events-api-retention-export.md).
- Types: [`internal/domain/event.go`](../domain/event.go) `EventType`
  constants.
- Payloads: [`internal/domain/event_payload.go`](../domain/event_payload.go).
- OpenAPI: payload YAML under
  `api/openapi/endpoints/events/payloads/` plus the map in
  [`api/cmd/gen_event_schemas/main.go`](../../api/cmd/gen_event_schemas/main.go);
  regenerate with `moon run server:generate` or `go generate ./api/`.
- Producer allowlist:
  [`internal/audit/catalog_producers_test.go`](../audit/catalog_producers_test.go)
  — live catalog types must have a producer; do not list deferred types as
  live.