docs(sp42): skills cyclone-store/tail — refresh module list, event kinds, streaming endpoint count
This commit is contained in:
@@ -5,14 +5,14 @@ description: "Cyclone live-tail streaming wire format and the useTailStream / us
|
||||
|
||||
# cyclone-tail
|
||||
|
||||
Cyclone keeps the Claims, Remittances, and Activity pages live without
|
||||
Cyclone keeps the Claims, Remittances, Activity, Acks, and Ta1Acks pages live without
|
||||
polling: every store write publishes an internal EventBus event, the
|
||||
page opens a `GET /api/<resource>/stream` HTTP/1.1 chunked-NDJSON
|
||||
connection, and new rows land in the table the moment they hit the
|
||||
database. This skill codifies the wire format, the hook triplet, and
|
||||
the backoff/stall machinery so additions stay consistent with the
|
||||
three streaming pages already shipped (`Claims`, `Remittances`,
|
||||
`ActivityLog`).
|
||||
five streaming pages already shipped (`Claims`, `Remittances`,
|
||||
`ActivityLog`, `Acks`, `Ta1Acks`).
|
||||
|
||||
## When to use
|
||||
|
||||
@@ -42,8 +42,8 @@ three streaming pages already shipped (`Claims`, `Remittances`,
|
||||
`item_dropped` (`{"id": "..."}` queue-overflow notice), `error`
|
||||
(`{"message": "..."}` promoted to a thrown error by the hook).
|
||||
Defined at `src/lib/tail-stream.ts:22-44`; emitted by
|
||||
`backend/src/cyclone/api.py:1357-2006`. See
|
||||
`references/wire-format.md`.
|
||||
the streaming routers in `backend/src/cyclone/api_routers/{claims,remittances,activity,acks,ta1_acks}.py`
|
||||
(post-SP36 split). See `references/wire-format.md`.
|
||||
2. **Hook triplet.** Streaming pages compose three pieces:
|
||||
`useTailStream(resource)` (`src/hooks/useTailStream.ts:80` — opens
|
||||
the stream, drives the backoff/stall state machine, dispatches
|
||||
@@ -52,8 +52,9 @@ three streaming pages already shipped (`Claims`, `Remittances`,
|
||||
(`src/hooks/useMergedTail.ts:25` — returns
|
||||
`baseItems + tailSlice` dedup'd against `baseItems`), and a
|
||||
per-resource initial-fetch hook (`useClaims`, `useRemittances`,
|
||||
`useActivity`). The page wires all three — see
|
||||
`src/pages/Claims.tsx:87-89`.
|
||||
`useActivity`, `useAcks`, `useTa1Acks`). The page wires all three —
|
||||
see `src/pages/Claims.tsx:87-89`. The five resources share the
|
||||
same triplet; only the resource string differs.
|
||||
3. **Stall threshold.** 30 seconds of total silence — heartbeats
|
||||
included — flips status to `stalled` and surfaces the
|
||||
`↻ Reconnect` button on `<TailStatusPill>`. Constant:
|
||||
@@ -67,8 +68,8 @@ three streaming pages already shipped (`Claims`, `Remittances`,
|
||||
`<TailStatusPill>` from `connecting` to `live` and to reset the
|
||||
reconnect backoff counter (`useTailStream.ts:174-180`).
|
||||
5. **Content-Type.** Stream endpoints respond with
|
||||
`media_type="application/x-ndjson"` — see
|
||||
`backend/src/cyclone/api.py:1401,1894,2005`. Frontend sets
|
||||
`media_type="application/x-ndjson"` — set in each
|
||||
`api_routers/<resource>.py` stream handler. Frontend sets
|
||||
`Accept: application/x-ndjson` at `src/lib/tail-stream.ts:67-70`.
|
||||
Never `application/json` for a stream endpoint.
|
||||
6. **Backoff.** Transient errors retry with `1s → 2s → 4s → 8s → 16s
|
||||
@@ -108,7 +109,7 @@ export function ClaimsPage() {
|
||||
|
||||
### Backend `/api/foo/stream` endpoint
|
||||
|
||||
Pattern from `backend/src/cyclone/api.py:1357-1401`. Register BEFORE
|
||||
Pattern from `backend/src/cyclone/api_routers/claims.py`. Register BEFORE
|
||||
`/api/foo/{foo_id}` so the literal `stream` segment doesn't match as
|
||||
an id. Two phases — eager snapshot, then live subscription — wrapped
|
||||
in `StreamingResponse` with `media_type="application/x-ndjson"`.
|
||||
@@ -160,7 +161,7 @@ takes `status` + `lastEventAt` from `useTailStream` plus
|
||||
- **Don't change the wire format on one endpoint without updating
|
||||
the others.** The parser (`src/lib/tail-stream.ts`) and the hook
|
||||
dispatch switch (`useTailStream.ts:173-195`) are shared across all
|
||||
three live streams. Adding a new event type means updating
|
||||
five live streams. Adding a new event type means updating
|
||||
`TailEvent`, `KNOWN_TYPES`, the dispatch `case`, the `armStall`
|
||||
re-arm list, and the backend emitter — in that order.
|
||||
- **Don't emit `item` events before `snapshot_end`.** The hook uses
|
||||
@@ -168,7 +169,7 @@ takes `status` + `lastEventAt` from `useTailStream` plus
|
||||
`connecting` to `live` and to reset the reconnect backoff counter.
|
||||
Emitting `item`s first lands rows in `useTailStore` but the UI
|
||||
still reads "Connecting" — operators see a flash of stale state.
|
||||
Emit snapshot, then `snapshot_end`, then live (`api.py:1386-1401`).
|
||||
Emit snapshot, then `snapshot_end`, then live.
|
||||
- **Don't change the 30s stall threshold without updating the README
|
||||
"Status pill" table.** The constant
|
||||
(`STALL_TIMEOUT_MS = 30_000` at `useTailStream.ts:53`) is the
|
||||
@@ -176,7 +177,7 @@ takes `status` + `lastEventAt` from `useTailStream` plus
|
||||
edit at `README.md:118`.
|
||||
- **Don't add `useTailStream` calls from `useFoo.ts` hooks.**
|
||||
Streaming connections belong on the page (`src/pages/Claims.tsx`,
|
||||
`Remittances.tsx`, `ActivityLog.tsx`). Hoisting into `useFoo`
|
||||
`Remittances.tsx`, `ActivityLog.tsx`, `Acks.tsx`, `Ta1Acks.tsx`). Hoisting into `useFoo`
|
||||
couples the lifecycle to whoever mounts it and breaks
|
||||
one-resource-one-page ownership.
|
||||
|
||||
@@ -185,16 +186,16 @@ takes `status` + `lastEventAt` from `useTailStream` plus
|
||||
- **`cyclone-frontend-page`** — page components live in `src/pages/`;
|
||||
load when adding or refactoring a streaming page to confirm the
|
||||
route + `PageHeader` + table conventions.
|
||||
- **`cyclone-api-router`** — endpoint conventions (`api_routers/` for
|
||||
resource-group routers, `api.py` for the live-tail endpoints); load
|
||||
when adding or changing an HTTP endpoint that surfaces streamed or
|
||||
paginated data.
|
||||
- **`cyclone-api-router`** — endpoint conventions (`api_routers/`
|
||||
hosts the streaming endpoints post-SP36, alongside the other
|
||||
resource-group routers); load when adding or changing an HTTP
|
||||
endpoint that surfaces streamed or paginated data.
|
||||
- **`cyclone-store`** — write-path conventions in `store.py` and the
|
||||
pubsub event contract (`claim_written`, `remittance_written`,
|
||||
`activity_recorded`); load when adding a new entity whose writes
|
||||
should fan out to a stream endpoint.
|
||||
- **`cyclone-tests`** — frontend `*.test.tsx` siblings cover
|
||||
`useTailStream`, `useMergedTail`, `TailStatusPill`; backend
|
||||
`test_api_stream_live.py` covers the three live-tail endpoints;
|
||||
load when the increment changes wire-format behavior or adds a
|
||||
streaming hook.
|
||||
`test_api_stream_live.py` covers the five live-tail endpoints
|
||||
(claims, remittances, activity, acks, ta1-acks); load when the
|
||||
increment changes wire-format behavior or adds a streaming hook.
|
||||
Reference in New Issue
Block a user