Skip to content

Commit 2b1e40f

Browse files
project: widen prettier scope to cover all files
The format:check and format scripts only covered src/ and demo source directories, so a bunch of files e.g. tests were never checked by CI. But, even more annoyingly, I found Claude would sometimes explicitly run `prettier --write` on excluded files that it edited, thus introducing diff noise. Instead, match ably-js's approach of formatting everything (relying on Prettier's automatic ignoral of gitignored stuff [1]). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> [1] https://prettier.io/docs/ignore
1 parent 3e04e96 commit 2b1e40f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1411
-1148
lines changed

.claude/rules/ABSTRACTIONS.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ ably-common/ # Git submodule — shared protocol resour
3939

4040
The SDK ships four entry points from a single package:
4141

42-
| Export path | Contains | Purpose | External deps |
43-
|---|---|---|---|
44-
| `@ably/ai-transport` | Generic codec interfaces, `createClientTransport`, `createServerTransport`, shared utilities | Core primitives — codec-agnostic transport and encoding | `ably` (peer) |
45-
| `@ably/ai-transport/react` | `useClientTransport`, `useView`, `useTree`, `useSend`, `useRegenerate`, `useEdit`, `useActiveTurns`, `useAblyMessages` | Generic React hooks for any codec | `ably`, `react` (peers) |
46-
| `@ably/ai-transport/vercel` | `UIMessageCodec`, `createServerTransport`, `createClientTransport`, `createChatTransport`, Vercel-specific types | Drop-in Vercel AI SDK integration | `ably`, `ai` (peers) |
47-
| `@ably/ai-transport/vercel/react` | `useChatTransport`, `useMessageSync` | React hooks for Vercel's `useChat` | `ably`, `ai`, `react` (peers) |
48-
42+
| Export path | Contains | Purpose | External deps |
43+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------------------------- |
44+
| `@ably/ai-transport` | Generic codec interfaces, `createClientTransport`, `createServerTransport`, shared utilities | Core primitives — codec-agnostic transport and encoding | `ably` (peer) |
45+
| `@ably/ai-transport/react` | `useClientTransport`, `useView`, `useTree`, `useSend`, `useRegenerate`, `useEdit`, `useActiveTurns`, `useAblyMessages` | Generic React hooks for any codec | `ably`, `react` (peers) |
46+
| `@ably/ai-transport/vercel` | `UIMessageCodec`, `createServerTransport`, `createClientTransport`, `createChatTransport`, Vercel-specific types | Drop-in Vercel AI SDK integration | `ably`, `ai` (peers) |
47+
| `@ably/ai-transport/vercel/react` | `useChatTransport`, `useMessageSync` | React hooks for Vercel's `useChat` | `ably`, `ai`, `react` (peers) |
4948

5049
## Two-Layer Architecture
5150

@@ -67,11 +66,11 @@ Header/event/message-name constants and Ably message utilities used by both laye
6766

6867
The client-side architecture separates three concerns:
6968

70-
| Component | Owns | Events |
71-
|---|---|---|
72-
| **Tree** | Complete conversation state — every node from live messages and history. Turn tracking (active turn → clientId). | `update` (any structural change), `ably-message` (every raw message), `turn` (start/end) — unfiltered, fires for all changes |
73-
| **View** | Pagination window — which history-loaded nodes are visible vs withheld. | Same event names, but **scoped to the visible window** — only fires when the visible output changes |
74-
| **Transport** | Write path (send/regenerate/edit/cancel), channel subscription, stream routing, decode loop. | `error` only — all data events moved to Tree/View |
69+
| Component | Owns | Events |
70+
| ------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
71+
| **Tree** | Complete conversation state — every node from live messages and history. Turn tracking (active turn → clientId). | `update` (any structural change), `ably-message` (every raw message), `turn` (start/end) — unfiltered, fires for all changes |
72+
| **View** | Pagination window — which history-loaded nodes are visible vs withheld. | Same event names, but **scoped to the visible window** — only fires when the visible output changes |
73+
| **Transport** | Write path (send/regenerate/edit/cancel), channel subscription, stream routing, decode loop. | `error` only — all data events moved to Tree/View |
7574

7675
The Tree is the source of truth. The View subscribes to Tree events and re-emits them filtered to what's visible. The Transport wires the channel to the Tree and exposes both as `transport.tree` and `transport.view`.
7776

@@ -146,8 +145,8 @@ Public-facing entry points (e.g. `createClientTransport()`) are factory function
146145

147146
1. **Two-layer split**: Generic transport/codec knows nothing about Vercel. Vercel layer implements the codec and provides convenience wrappers.
148147
2. **Codec-parameterized**: All generic components are parameterized by `<TEvent, TMessage>` via the `Codec` interface.
149-
4. **Constructor/option injection**: All dependencies passed explicitly — no singletons, no globals.
150-
3. **Composition, not inheritance**: Transports compose features; no class hierarchies.
148+
3. **Constructor/option injection**: All dependencies passed explicitly — no singletons, no globals.
149+
4. **Composition, not inheritance**: Transports compose features; no class hierarchies.
151150
5. **Interface-first**: Public contracts are TypeScript interfaces. Implementations are internal `Default*` classes, exposed to consumers via factory functions.
152151
6. **Header discipline**: Generic layer uses only `x-ably-*` headers. Domain-specific headers (e.g. `x-domain-*`) belong in the Vercel layer.
153152
7. **Explicit exports**: Only types and functions listed in `index.ts` files are public API.

.claude/rules/ERRORS.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Every error should either be handled internally with a clear recovery strategy,
1111
- **Isolate handler callbacks**: When the SDK invokes developer-provided callbacks (event listeners, hooks), wrap each in try/catch. One bad handler shouldn't kill internal machinery or prevent other handlers from firing.
1212
- **Define recovery semantics explicitly**: For every internal operation that can fail, decide upfront: retry, degrade, or propagate. Document the choice.
1313
- **Preserve the original error**: When wrapping errors, always attach the original as `cause`. Developers debugging production issues need the full chain.
14-
- **Best-effort operations should be labeled**: If something is fire-and-forget (e.g. cleanup publish on close), comment it as such. Swallowing an error is only acceptable when failure is unrecoverable *and* non-impactful.
14+
- **Best-effort operations should be labeled**: If something is fire-and-forget (e.g. cleanup publish on close), comment it as such. Swallowing an error is only acceptable when failure is unrecoverable _and_ non-impactful.
1515

1616
### Surfacing errors to developers
1717

@@ -86,9 +86,9 @@ throw new Ably.ErrorInfo('unable to send message; room is not attached', ErrorCo
8686
throw new Ably.ErrorInfo('unable to detach room; room is in failed state', ErrorCode.RoomInInvalidState, 400);
8787

8888
// Wrong — do not use these prefixes
89-
"cannot send message"
90-
"failed to send message"
91-
"Could not send message"
89+
// "cannot send message"
90+
// "failed to send message"
91+
// "Could not send message"
9292
```
9393

9494
Dynamic context is allowed in the message:
@@ -145,12 +145,12 @@ reject(
145145

146146
Use the custom Vitest matchers in a test helper (`test/helper/expectations.ts`):
147147

148-
| Matcher | Usage |
149-
|---|---|
150-
| `toBeErrorInfo({ code?, statusCode?, message?, cause? })` | Assert a value is an `Ably.ErrorInfo` matching the given fields |
151-
| `toThrowErrorInfo({ code?, statusCode?, message? })` | Assert a sync function throws a matching `Ably.ErrorInfo` |
152-
| `toBeErrorInfoWithCode(code)` | Shorthand — assert value is `Ably.ErrorInfo` with a specific code |
153-
| `toThrowErrorInfoWithCode(code)` | Shorthand — assert sync function throws with a specific code |
154-
| `toBeErrorInfoWithCauseCode(code)` | Assert value is `Ably.ErrorInfo` whose `.cause.code` matches |
148+
| Matcher | Usage |
149+
| --------------------------------------------------------- | ----------------------------------------------------------------- |
150+
| `toBeErrorInfo({ code?, statusCode?, message?, cause? })` | Assert a value is an `Ably.ErrorInfo` matching the given fields |
151+
| `toThrowErrorInfo({ code?, statusCode?, message? })` | Assert a sync function throws a matching `Ably.ErrorInfo` |
152+
| `toBeErrorInfoWithCode(code)` | Shorthand — assert value is `Ably.ErrorInfo` with a specific code |
153+
| `toThrowErrorInfoWithCode(code)` | Shorthand — assert sync function throws with a specific code |
154+
| `toBeErrorInfoWithCauseCode(code)` | Assert value is `Ably.ErrorInfo` whose `.cause.code` matches |
155155

156156
The matchers only check the fields you provide — omitted fields are not compared. The `cause` field is checked recursively.

.claude/rules/LOGGING.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ type LogContext = Record<string, any>;
1919

2020
## Log Levels
2121

22-
| Level | When to use |
23-
|---|---|
24-
| `Trace` | Routine operations — entry point of every key method. The most verbose level. |
25-
| `Debug` | Useful for debugging but superfluous in normal operation — successful completions, state transitions, decision points. |
26-
| `Info` | Operationally significant but expected — transport open/close, lifecycle events. |
27-
| `Warn` | Not an error yet, but could cause problems — unexpected but recoverable states. |
28-
| `Error` | An operation has failed and cannot be automatically recovered. |
29-
| `Silent` | No logging. |
22+
| Level | When to use |
23+
| -------- | ---------------------------------------------------------------------------------------------------------------------- |
24+
| `Trace` | Routine operations — entry point of every key method. The most verbose level. |
25+
| `Debug` | Useful for debugging but superfluous in normal operation — successful completions, state transitions, decision points. |
26+
| `Info` | Operationally significant but expected — transport open/close, lifecycle events. |
27+
| `Warn` | Not an error yet, but could cause problems — unexpected but recoverable states. |
28+
| `Error` | An operation has failed and cannot be automatically recovered. |
29+
| `Silent` | No logging. |
3030

3131
Levels are hierarchical. Setting the level to `Debug` suppresses `Trace` but shows everything else.
3232

@@ -85,7 +85,8 @@ this._logger.debug('Tree.upsert(); inserting new node', { msgId, parentId, forkO
8585

8686
// Warning
8787
this._logger.warn('DefaultDecoderCore.decode(); unexpected message action', {
88-
action, serial: message.serial,
88+
action,
89+
serial: message.serial,
8990
});
9091

9192
// Error
@@ -130,7 +131,8 @@ Not yet an error, but something that could cascade:
130131

131132
```ts
132133
this._logger.warn('DefaultDecoderCore.decode(); unrecognized message name', {
133-
name: message.name, serial: message.serial,
134+
name: message.name,
135+
serial: message.serial,
134136
});
135137
```
136138

.claude/rules/TESTS.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
## Two tiers
44

5-
| Tier | Command | Runs against | What it proves |
6-
|---|---|---|---|
7-
| **Unit** | `npm test` | Mocks only | Every code path works correctly in isolation |
8-
| **Integration** | `npm run test:integration` | Real Ably channels | Happy path works end-to-end over real Ably |
5+
| Tier | Command | Runs against | What it proves |
6+
| --------------- | -------------------------- | ------------------ | -------------------------------------------- |
7+
| **Unit** | `npm test` | Mocks only | Every code path works correctly in isolation |
8+
| **Integration** | `npm run test:integration` | Real Ably channels | Happy path works end-to-end over real Ably |
99

1010
Config: `vitest.config.ts` (unit, excludes `*.integration.test.ts`) and `vitest.config.integration.ts` (integration only).
1111

@@ -49,11 +49,11 @@ By default, integration tests run against the **Ably sandbox**. The globalSetup
4949

5050
To run against a different environment, set `VITE_ABLY_ENV`:
5151

52-
| `VITE_ABLY_ENV` | Behaviour | API key required? |
53-
|---|---|---|
54-
| *(unset)* / `sandbox` | Provisions a sandbox app automatically | No |
55-
| `local` | Connects to `local-rest.ably.io:8081` (no TLS) | Yes — set `VITE_ABLY_API_KEY` |
56-
| `production` | Connects to production Ably | Yes — set `VITE_ABLY_API_KEY` |
52+
| `VITE_ABLY_ENV` | Behaviour | API key required? |
53+
| --------------------- | ---------------------------------------------- | ----------------------------- |
54+
| _(unset)_ / `sandbox` | Provisions a sandbox app automatically | No |
55+
| `local` | Connects to `local-rest.ably.io:8081` (no TLS) | Yes — set `VITE_ABLY_API_KEY` |
56+
| `production` | Connects to production Ably | Yes — set `VITE_ABLY_API_KEY` |
5757

5858
### Conventions
5959

.claude/skills/commit/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ project-specific guidance takes precedence.
5757
### Project-specific component prefixes
5858

5959
The component prefix is derived from the file paths in the diff. Examples:
60+
6061
- `codec:` `codec/vercel:` `transport:` `transport/vercel:`
6162
- `react:` `react/vercel:`
6263
- `claude/skills:` `claude/rules:`

.claude/skills/docs/SKILL.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,23 @@ direct, declarative statement. No preamble, no "In this guide, we'll..."
8383

8484
```markdown
8585
<!-- Good -->
86+
8687
Cancellation in `@ably/ai` is a channel-level operation - the client publishes
8788
a cancel signal on the Ably channel, the server receives it and aborts the
8889
matching turns.
8990

9091
<!-- Good -->
92+
9193
Interruption is when a user sends a new message while the AI is still streaming
9294
a response.
9395

9496
<!-- Bad -->
97+
9598
In this guide, we'll explore how to cancel streaming responses in your
9699
AI application.
97100

98101
<!-- Bad -->
102+
99103
This page covers the cancellation feature of the AI Transport SDK.
100104
```
101105

@@ -109,7 +113,7 @@ This page covers the cancellation feature of the AI Transport SDK.
109113
"robust." The technical explanation IS the value proposition.
110114
- **No hedging.** Don't say "you may want to" or "consider using." Say
111115
"use X when Y."
112-
- **Use hyphens, not em-dashes.** Use ` - ` (space-hyphen-space) for
116+
- **Use hyphens, not em-dashes.** Use <code> - </code> (space-hyphen-space) for
113117
parenthetical asides, not ``. This applies to prose, code comments,
114118
and table cells.
115119

@@ -195,8 +199,8 @@ Internals pages follow this flow:
195199
glossary, and the corresponding feature/concept pages
196200

197201
Internals pages differ from concept pages: concept pages explain what
198-
developers need to know to *use* the SDK. Internals pages explain how the
199-
SDK works *internally* for contributors and curious engineers. Internals
202+
developers need to know to _use_ the SDK. Internals pages explain how the
203+
SDK works _internally_ for contributors and curious engineers. Internals
200204
pages can reference source file paths and internal class names.
201205

202206
### Tables
@@ -206,10 +210,10 @@ codes, phase behaviors. Tables are scannable and dense. Prefer a table over
206210
a bullet list when comparing properties across items.
207211

208212
```markdown
209-
| Filter | Effect | Use case |
210-
|---|---|---|
211-
| `{ own: true }` (default) | Cancel all turns started by this client | Stop button |
212-
| `{ turnId: "abc" }` | Cancel one specific turn | Cancel a specific generation |
213+
| Filter | Effect | Use case |
214+
| ------------------------- | --------------------------------------- | ---------------------------- |
215+
| `{ own: true }` (default) | Cancel all turns started by this client | Stop button |
216+
| `{ turnId: "abc" }` | Cancel one specific turn | Cancel a specific generation |
213217
```
214218

215219
### Diagrams
@@ -218,7 +222,7 @@ Use **Mermaid diagrams** (` ```mermaid `) for sequence diagrams, flowcharts,
218222
and any multi-column or multi-participant interactions. Mermaid is rendered
219223
by GitHub and most doc tooling, so alignment is never an issue.
220224

221-
```markdown
225+
````markdown
222226
```mermaid
223227
sequenceDiagram
224228
participant C as Client
@@ -228,8 +232,8 @@ sequenceDiagram
228232
C->>Ch: publish(x-ably-cancel)
229233
Note left of C: close local stream(s)
230234
Ch->>S: deliver to cancel listener
231-
``` (close the code fence)
232235
```
236+
````
233237

234238
**When to use Mermaid vs plain text:**
235239

@@ -254,8 +258,8 @@ eliminates this class of bugs entirely.
254258
- **Real types, real methods.** Use the actual API - `transport.send()`,
255259
`turn.cancel()`, not pseudocode.
256260
- **Comments explain the non-obvious.** Don't comment `// cancel the turn`
257-
above `turn.cancel()`. Do comment `// fire-and-forget - POST doesn't block
258-
the stream return`.
261+
above `turn.cancel()`. Do comment
262+
`// fire-and-forget - POST doesn't block the stream return`.
259263
- **TypeScript only** for now (the SDK is TypeScript).
260264
- **Both sides.** Feature pages show client and server code. Use section
261265
headers to separate them.
@@ -273,11 +277,13 @@ readers skim, and the link needs to be where the concept appears.
273277

274278
```markdown
275279
<!-- Good: inline link where the concept is mentioned -->
280+
276281
The [decoder](decoder.md) accumulates deltas via string concatenation and
277282
uses [prefix-matching](decoder.md#known-serial-prefix-match) to detect
278283
whether an update is incremental or a replacement.
279284

280285
<!-- Bad: concept mentioned without link, with "See also" at the bottom -->
286+
281287
The decoder accumulates deltas via string concatenation and uses
282288
prefix-matching to detect whether an update is incremental or a replacement.
283289
...
@@ -351,14 +357,14 @@ docs/
351357

352358
**Page type by directory:**
353359

354-
| Directory | Page type | Pattern |
355-
|---|---|---|
356-
| `concepts/` | Concept page | Definition → architecture → data flow → key details |
357-
| `get-started/` | Quickstart | Prerequisites → step-by-step → "what's happening" → next steps |
358-
| `frameworks/` | Framework guide | What the framework provides → what's missing → how AIT fills gaps → integration paths |
359-
| `features/` | Feature page | Definition → problem framing → mechanism → client code → server code → edge cases |
360-
| `reference/` | Reference page | One section per API item: signature, params table, return type, example |
361-
| `internals/` | Internals page | Definition → concepts → operations → algorithms → edge cases → cross-refs |
360+
| Directory | Page type | Pattern |
361+
| -------------- | --------------- | ------------------------------------------------------------------------------------- |
362+
| `concepts/` | Concept page | Definition → architecture → data flow → key details |
363+
| `get-started/` | Quickstart | Prerequisites → step-by-step → "what's happening" → next steps |
364+
| `frameworks/` | Framework guide | What the framework provides → what's missing → how AIT fills gaps → integration paths |
365+
| `features/` | Feature page | Definition → problem framing → mechanism → client code → server code → edge cases |
366+
| `reference/` | Reference page | One section per API item: signature, params table, return type, example |
367+
| `internals/` | Internals page | Definition → concepts → operations → algorithms → edge cases → cross-refs |
362368

363369
If a page doesn't fit these categories, discuss placement before writing.
364370

0 commit comments

Comments
 (0)