# `src/items` - item storage and retrieval

Owns the `Item` record and the two aggregate reads over it (`total_size`,
`by_team`). Nothing here talks to billing.

## What binds this module

- **[Rule 01 - item ids are opaque](../../rules/01-opaque-ids.md).** `Item.id`
  is annotated `str` and that annotation is load-bearing, not stylistic:
  `tests/test_opaque_ids.py` fails if any `id`/`*_id` field under `src/`
  becomes an int. Ids leaked sequence information once and two prospects
  raised it in security review.
- **[Rule 02 - billing boundary](../../rules/02-billing-boundary.md)** points
  at this module from the other side: `src/billing` may not import from here.
  That makes this module the stable side of the dependency, so a rename here
  cannot be fixed by reaching across from billing.

## The unit boundary - read before wiring these together

`total_size` returns **bytes**. `src/billing.line_item` takes **gigabytes**.
No code in this repository converts between them, and rule 02 forbids billing
importing this module to do it. The conversion therefore happens outside both
modules - `docs/architecture.md` attributes it to a reporting view, which is
not present in this tree.

Do not "fix" this by adding a byte-to-gigabyte conversion inside billing. That
crosses the boundary the rule exists to hold.

## What would make this document stale

- An id generator or a public API surface landing here (rule 01 becomes fully
  enforceable and `tests/test_opaque_ids.py` must be extended).
- `Item` gaining a field that billing needs, which would put the boundary
  under real pressure rather than theoretical pressure.
- A conversion helper landing anywhere in this tree - the unit boundary note
  above then describes the past.
