# Store App Capability Boundaries

> Status: canonical-spec
> Canonical path: `specs/store/app-registry-manifest-split.md`
> Scope: ownership boundaries between the parent `internal/store/app` package and its named capability child packages
> Non-goals: schema design, DTO shape, HTTP status mapping, capability signing, credential encryption

## Goal

Keep app-ecosystem persistence decomposed into named capability child packages
under `internal/store/app/`, while the parent `app.Store` retains only the
workflows that genuinely own cross-cutting side effects. New work in this area
goes into the matching capability package; the parent package does not accrete
new domain state machines.

## Capability Packages

The named capability child packages under `internal/store/app/` are:

| Package | Owns |
|---|---|
| `internal/store/app/registry` | App catalog reads, stored manifest parsing, developer manifest normalization/validation, latest installable metadata helpers. |
| `internal/store/app/installation` | Installable-app/version reads, installation row locks/scans, grant scope load/reconcile, JSON/write-error helpers. |
| `internal/store/app/runtime` | Runtime auth record reads, capability scope normalization/matching, machine runtime capability reads, connection view scanning, connection write-error mapping. |
| `internal/store/app/webhooks` | Webhook app definition reads, delivery-log persistence helpers, active subscription checks, provider-watch connection resolution, remote webhook primitive wrappers. |

These four package names are the complete set of allowed capability child
packages under `internal/store/app/`. Adding a new capability package requires
extending the guardrail allowlist together with the spec.

## Parent `app.Store` Retained Responsibilities

The parent `internal/store/app.Store` retains every workflow that is not a
pure capability helper. In particular it owns:

- Workspace access checks and public catalog/developer workflows.
- Install/update/uninstall transactions plus workspace, member, and channel
  side effects.
- Token signing/verification, credential encryption/decryption, and app
  callback idempotency.
- Provider config lookups, provider-specific HTTP transport (including
  GitHub calls), and webhook handler orchestration.
- Message delivery, subscription, and mention side effects.

Capability packages hold row-level reads, helpers, and validators; they do
not own transactions or side effects.

## Architectural Constraints

These constraints are enforced by
`guardrails/store/check-store-domain-split-guardrails.py`:

- Only the four named capability child packages above may exist under
  `internal/store/app/`. Existing child packages are tracked by the
  guardrail baseline; new child packages require an explicit allowlist
  update.
- No generic child package names (`helpers`, `ports`, `repository`) under
  `internal/store/`.
- `internal/store/app/ports` is explicitly forbidden.
- No direct `sqlx` imports under `internal/store/`. Database access goes
  through `coredb` abstractions.
- No upward imports from `internal/store/...` to `internal/api`,
  `internal/router`, `internal/server`, `internal/usecase`, `cmd`,
  `web`, or `desktop`.
- Capability packages must not introduce schema, DTO, or HTTP status
  changes; behavior parity is required when behavior moves between the
  parent and a capability package.

The guardrail uses a baseline-ratchet model: oversized-package metrics may
decrease, but may not increase without an explicit baseline update shipped in
the same change set.

## Verification

Repeatable verification for changes touching this area:

- `GOWORK=off go test ./internal/store/app ./internal/store/app/registry ./internal/store/app/installation ./internal/store/app/runtime ./internal/store/app/webhooks`
- `python3 -m py_compile guardrails/store/check-store-domain-split-guardrails.py`
- `guardrails/store/check-store-domain-split-guardrails.py`
- `git diff --check`

The authoritative source for the allowed child package set and baseline
metrics is `guardrails/store/check-store-domain-split-guardrails.py`. The
allowlist and baseline in that script override any descriptive list in this
spec when they disagree.

## Non-goals

- This spec does not prescribe schema migrations or DTO shape changes.
- This spec does not change HTTP/API status mapping or error semantics.
- This spec does not require moving `model` types out of the parent
  `app.Store` package.
- This spec does not govern other store domains (`message`, `taskboard`,
  `organization`, `privateagent`); each domain tracks its own split
  guardrail entries inside the same script.
