# AsyncAPI specs

Each service has its own AsyncAPI 3.1 document describing the channels it
publishes (`send`) and consumes (`receive`) on the shared Valkey (Redis)
Streams broker. Messages and shared schemas are split into their own files
and referenced via `$ref`, so each message has exactly one source of truth
owned by its publisher.

## Layout

```
docs/asyncapi/
├── README.md
├── index.html         # local system-view page (Lueira ↔ Valkey ↔ Comms)
├── lueira.yaml        # Lueira service spec
├── comms.yaml         # Comms service spec
├── messages/          # one file per message, owned by its publisher
└── schemas/           # schemas reused across many messages
```

## Conventions

- **One file per message.** When a service publishes a channel, the message
payload lives in `messages/<snake_case_name>.yaml`. Both the publisher's
spec and any consumer's spec `$ref` the same file, so updating a payload
means editing one file.
- **Inline small payloads.** A message file owns its payload inline. Only
promote a payload into `schemas/` if it's large or shared by 3+ messages.
`EventHeader` is the only schema in there today and is `$ref`'d by every
message.
- **One channel, one owner.** Each channel address is owned by exactly one
publisher. Channel addresses are namespaced by owner (`lueira.events.`*,
`lueira.commands.*`, `comms.events.*`).

## Adding or changing a message

1. **New message** — create `messages/<snake_case_name>.yaml` with `name`,
  `title`, `contentType: application/json`, `headers: { $ref: '../schemas/event_header.yaml' }`,
   and the inline `payload`. Add a `channels.<name>` entry in the publisher's
   spec referencing it, and an `operations.<name>` with `action: send`. Add
   the matching `receive` entries in any consumer's spec.
2. **Change a payload** — edit `messages/<name>.yaml`. Both service specs
  pick up the change automatically.
3. **New shared schema** — drop it under `schemas/` and `$ref` it from the
  messages that share it.

After any change, validate:

```bash
npx --yes -p @asyncapi/cli@latest asyncapi validate docs/asyncapi/lueira.yaml
npx --yes -p @asyncapi/cli@latest asyncapi validate docs/asyncapi/comms.yaml
```

The CLI dereferences external `$ref`s as part of validation, so broken file
paths show up here as governance errors.

## Viewing

### Both services together (local system view)

Serve `docs/` over HTTP and open `/asyncapi/`:

```bash
python3 -m http.server --directory docs 8000
# then open http://127.0.0.1:8000/asyncapi/
```

The page parses both YAML files, follows external `$ref`s, and renders the
two services with the Valkey broker between them plus a combined channels
table. This is the page linked from the main docs landing.

### One service at a time (Studio block visualizer)

Open either spec at [https://studio.asyncapi.com](https://studio.asyncapi.com). Studio resolves the
external `$ref`s automatically when importing a remote URL.

## Cross-repo: when Comms moves out

When Comms gets its own repo:

1. Move `comms.yaml` and the Comms-owned `messages/comms_*.yaml` files to
  that repo. Keep `schemas/event_header.yaml` here (it's owned by the bus
   convention itself); Comms `$ref`s it via raw GitHub URL.
2. Update Comms's `$ref`s for messages it consumes from Lueira to the raw
  GitHub URLs:
3. In `index.html`, point the `SPECS` entry for Comms at the raw URL of
  `comms.yaml` in its new repo.

The system view stays as the central catalog; each repo owns its specs.