Use only when placement is not forced by current code, package documentation, generated-source ownership, or Component Boundaries. Do not create a directory before its first real artifact.
| Responsibility | Owner |
|---|---|
| Business behavior, types, ports, invariants, domain errors | internal/<feature>/ |
| Concrete provider/transport adapters | internal/infra/<provider>/ |
| Process composition and lifecycle | cmd/<binary>/internal/bootstrap/ |
| Client-visible REST contracts | api/openapi/service.yaml |
| Protobuf contracts when enabled | api/proto/ |
| Generated Go | Derived directory named by the owning generator; never manual authority |
| Runtime configuration | The existing internal/config/<section>_config.go owner |
| Ordinary behavior and boundary tests | Beside their package owner |
| Real external-process/container proof | test/ with integration tag and external test package |
| Task decisions | specs/<task>/ |
| Repository agent policy and methods | The narrow docs/ or .agents/skills/ owner |
There is no public pkg/ until reusable external API ownership is explicitly
accepted. Empty api/proto/, migrations/, query, or generated directories are
not reserved. A new binary requires an independent process lifecycle; a new
network boundary, cache, queue, store, or shared package requires its accepted
architecture force.
- Feature REST implementations stay in their feature-owned package and satisfy
openapi.StrictServerInterface; bootstrap injects them throughhttpx.Handlers.API. Business handlers never move into the sharedinternal/infra/httptemplate adapter. - Directory
internal/infra/httpuses packagehttpx, andinternal/infra/grpcusesgrpcx, to avoid collisions withnet/httpandgoogle.golang.org/grpcin their callers. - Reusable runtime option mapping belongs in
cmd/internal/runtimeopts/only when more than one composition root currently consumes it. - Configuration section type, defaults, and validation stay together in
<section>_config.go; tagged types own decoding and known-key discovery. - Adapter-owned instruments stay with that adapter; a caller-provided telemetry
provider alone does not justify
metrics.goortelemetry.go. - PostgreSQL driver and generated SQL imports remain in the file families
allowed by
.golangci.yml. In particularstore_<stage>.gois a load-bearing ownership name, not a cosmetic convention. - Shared test support stays unexported in
harness_test.gofor one package,<owner-package>/<name>test/for multiple packages with one natural owner, orinternal/<name>test/only when no single package owns current consumers. examples/reference-service/is an isolated feature-slice example, never a production route or shared runtime owner.
Go filenames use lowercase snake case and one present reason to exist. Name the
owned behavior, not chronology. Reject *_additional_test.go,
*_part2_test.go, test_helpers_test.go, production *_helpers.go, util.go,
common.go, and misc.go. Use <owner>_test.go,
<owner>_contract_test.go, <feature>_integration_test.go, and goleak_test.go
only for those actual proof roles. Split by independent lifecycle, audience,
authority, or operator flow; line count alone is not an owner.
internal/infra/oauth2clientcredentials owns the fixed token endpoint,
safe token projection, and authenticated HTTP/gRPC client factories; x/oauth2
owns cache, expiry skew, and refresh synchronization. Concrete
provider adapters close the owner and give feature packages only generated
authenticated clients. It remains one removable outbound-auth pack.
cmd/jobs-worker, River, and River's Goose migration are one removable
PostgreSQL-jobs pack. Business packages define their typed arguments and workers
without a second job framework.
internal/infra/postgresoutbox and cmd/outbox-relay are the removable
River-backed outbox pack. Event meaning owns no broker address; NATS routing
remains in internal/infra/natsjs.
internal/domainevent, internal/messagingconfig, and internal/infra/natsjs
form the removable typed messaging pack. The domain package owns typed event
identity and encoding, the pure leaf owns shared client-config rules, and the
adapter owns composition routes and JetStream mechanics.
internal/infra/postgreswebhook and the enabled jobs-worker surfaces form the
removable durable-webhook pack. The always-retained internal/outboundtrust
predicate is shared with fixed-target HTTP and owns no URL, resolver, dialer,
HTTP, or config policy.
internal/inboundwebhook/ and internal/infra/postgresinboundwebhook/ form the
removable inbound receipt pack. The capability owns verification, receipt
deduplication, durable dispatch, and its pure endpoint-manifest leaf; feature
adapters own provider JSON and idempotent effects.
internal/objectstorage/ owns the provider-neutral port and
internal/infra/s3/ one fixed-authority S3-compatible adapter.
A profile-owned source, generated output, wiring, tests, config, documentation, and replacement cleanup leave together. Partial profile capability is not a supported placement.
| Source | Derived output | Drift proof |
|---|---|---|
api/openapi/service.yaml and generation config |
internal/openapi/openapi.gen.go |
make openapi-check |
| Example OpenAPI and generation config | example generated bindings | make openapi-check |
migrations/*.sql and SQL query source |
PostgreSQL generated access | make sqlc-check |
.agents/roles/ |
Codex, Claude, Qwen, Grok, Cursor, and OpenCode role carriers | make agent-roles-check |
Use the narrowest real proof owner. _contract_test.go is for an executable
boundary invariant, not a stronger-sounding unit test. test/ remains reserved
for real database, container, external process, or multi-package black-box
proof; ordinary package tests do not require Docker.
Depguard, generator drift, migration checks, and package contract tests own enforced facts. If a proposal still has no owner after this fallback, reopen Repository Architecture instead of creating a generic service, helper, or shared directory.