Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2499c28
feat(llc): add the OpenAPI generated client and its generation toolin…
xsahil03x Sep 2, 2026
7f3e95a
Merge branch 'master' into v11
xsahil03x Sep 8, 2026
36126e3
Merge branch 'master' into v11
xsahil03x Sep 15, 2026
8c900b0
Merge remote-tracking branch 'origin/master' into v11
xsahil03x Sep 15, 2026
7db92d8
docs(repo): plan the migration of stream_chat onto stream_core (#2955)
xsahil03x Sep 15, 2026
afd6883
refactor(llc, ui, persistence)!: adopt stream_core's foundation (#2956)
xsahil03x Sep 15, 2026
8cabacf
refactor(llc)!: adopt stream_core's Sort for every query (#2957)
xsahil03x Sep 15, 2026
3a315c5
refactor(llc)!: adopt stream_core's Filter for every query (#2958)
xsahil03x Sep 15, 2026
3a915ac
refactor(llc)!: adopt stream_core's LocationCoordinate, sort normaliz…
xsahil03x Sep 15, 2026
752b13c
refactor(llc, ui)!: adopt stream_core's platform detector and sealed …
xsahil03x Sep 15, 2026
9ce2474
fix(llc)!: match a channel's members exactly, the way the API does (#…
xsahil03x Sep 15, 2026
b09a76f
Merge branch 'master' into v11
xsahil03x Sep 16, 2026
c42db12
fix(llc): port SyncManager to the v11 APIs
xsahil03x Sep 16, 2026
60c4f09
style(llc): log the lastSyncAt rollback as a multiline string
xsahil03x Sep 16, 2026
59b9528
refactor(llc): give SyncManager its own tagged logger
xsahil03x Sep 16, 2026
7f0f0c8
refactor(llc): move SyncManager's tag to the end of its parameters
xsahil03x Sep 16, 2026
fb94183
style(llc): indent the lastSyncAt rollback log like the repo's other …
xsahil03x Sep 16, 2026
6e7bb9a
fix(llc): stop per-channel and per-cid logging from drowning the info…
xsahil03x Sep 16, 2026
7824291
fix(llc): demote per-channel logging and drop internal names from mes…
xsahil03x Sep 16, 2026
193cb68
Merge branch 'master' into v11
xsahil03x Sep 18, 2026
9a7614a
ci(repo): check the titles of the PRs that target v11
xsahil03x Sep 18, 2026
972ac39
refactor(llc)!: move the WebSocket onto stream_core's StreamWebSocket…
xsahil03x Sep 21, 2026
5423623
Merge branch 'master' into v11
xsahil03x Sep 21, 2026
0295abb
refactor(llc)!: route `searchRoles` through the generated API client …
VelikovPetar Sep 21, 2026
1274140
refactor(llc)!: route device registration through the generated clien…
VelikovPetar Sep 21, 2026
db82e64
docs(repo): require the style and testing guides in every v11 migrati…
VelikovPetar Sep 22, 2026
da08c75
feat(llc): regenerate the OpenAPI client at protocol v239.10.0 (#2991)
xsahil03x Sep 22, 2026
64b1849
Merge branch 'master' into v11
xsahil03x Sep 24, 2026
b1ebc97
refactor(llc)!: keep the generated device and role models off the pub…
VelikovPetar Sep 25, 2026
9e67fa1
chore(repo): build the generator from chat's tools/openapi (#3014)
xsahil03x Sep 28, 2026
1b0f88d
refactor(llc, ui)!: route `enrichUrl` through the generated client (#…
VelikovPetar Sep 28, 2026
81498a0
refactor(llc)!: route user groups through the generated client (#2978)
VelikovPetar Sep 28, 2026
7e6959e
refactor(llc, core)!: route moderation through the generated API clie…
xsahil03x Sep 29, 2026
ef92b23
docs(llc): describe what a caller observes, not what the server does …
xsahil03x Sep 29, 2026
e45bc11
test(llc): cover the migrated endpoints through the public client (#3…
VelikovPetar Sep 29, 2026
879439e
refactor(llc)!: route app settings through the generated client (#2993)
VelikovPetar Sep 29, 2026
2143a2f
docs(repo): align the openapi-migration skill with the domain-model s…
VelikovPetar Sep 29, 2026
b251dd2
Merge branch 'master' into v11
xsahil03x Sep 29, 2026
9a9a705
fix(llc): keep the signed-in user authenticated when connectGuestUser…
VelikovPetar Oct 1, 2026
bb52ba5
feat(llc): regenerate the OpenAPI client at protocol v239.44.1 (#3033)
xsahil03x Oct 1, 2026
ce2ba7e
refactor(llc)!: route uploads through a v2 multipart client (#3040)
xsahil03x Oct 2, 2026
1b2b27e
refactor(llc)!: route connectGuestUser through the generated client (…
VelikovPetar Oct 3, 2026
7a14d92
Merge branch 'master' into v11
VelikovPetar Oct 5, 2026
8e75a17
Merge branch 'master' into v11
VelikovPetar Oct 6, 2026
e4533d9
Merge branch 'master' into v11
VelikovPetar Oct 9, 2026
8f4784e
refactor(llc)!: route polls through the generated client (#3037)
VelikovPetar Oct 15, 2026
30fb8e7
refactor(llc)!: make extension type values private (#3068)
VelikovPetar Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
174 changes: 174 additions & 0 deletions .claude/skills/openapi-codegen/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
---
name: openapi-codegen
description: >
Regenerate `stream_chat`'s OpenAPI client into `lib/open_api/`, and fix the generator when its output is wrong.
Use when the spec or the generator changed and you need new endpoints or fields, when `melos run gen:openapi`
fails, when generated code does not compile, or when changing the `stream_core` constraint the generated output
depends on. For migrating call sites onto the generated client, use the `openapi-migration` skill instead.
allowed-tools:
- Bash
- Read
- Edit
- Write
- Grep
- Glob
---

# openapi-codegen

The generated client is committed to the repo, so migration work never runs this. Regenerate only when the spec
or the generator changed — a new endpoint, a new field, or a generator fix you need. Land it as its own PR; never
fold a regeneration into a migration slice.

```bash
# the newest released spec, resolved for you — nothing to look up
PROTOCOL_REF=latest CHAT_BACKEND_DIR=/path/to/chat melos run gen:openapi

# a specific release
PROTOCOL_REF=openapi-v237.2.0 CHAT_BACKEND_DIR=/path/to/chat melos run gen:openapi

# same spec from a local protocol checkout — for iterating on an unreleased one
PROTOCOL_DIR=/path/to/protocol CHAT_BACKEND_DIR=/path/to/chat melos run gen:openapi

# the monolith's current API, spec and all — no protocol involvement
CHAT_BACKEND_DIR=/path/to/chat melos run gen:openapi
```

`CHAT_BACKEND_DIR` is required in every mode: the generator lives in the monolith, and protocol ships specs only.
Setting `PROTOCOL_DIR` and `PROTOCOL_REF` together is an error rather than a precedence rule — they name two
different specs. Requires `go`, `dart` and `python3`, plus `curl` for `PROTOCOL_REF`; checksum verification uses
`shasum` or `sha256sum` and warns rather than fails if neither exists.

`PROTOCOL_REF` has **no default** on purpose: a default of `main` would reintroduce the drift the stamp exists to
catch. Naming the ref — or asking for `latest`, which stamps the tag it resolved rather than the word — is what
makes a run reproducible after the fact, more so than a checkout that silently uses whatever it sits at. Both
protocol modes produce byte-identical output and the same stamp for the same release.

Backend mode is the odd one out: its spec is built from the monolith's current routes, so it can carry unreleased
endpoints and stamps `(API dev)` with a commit rather than a version. Right for "does this new endpoint generate
cleanly?", wrong for a tree you intend to commit — nobody without your exact monolith commit can reproduce it.

`scripts/generate.sh` resolves a spec, generates into `packages/stream_chat/lib/open_api`, patches it, runs
`build_runner`, then formats the package. `CHAT_BACKEND_DIR` is required either way — `generate-client` lives in
the monolith, and the protocol repo ships specs only.

After regenerating, refresh the migration plan:

```bash
python3 openapi-migration/tool/generate_plan.py
```

It rebuilds the scope tables in `openapi-migration/*.md` from the new client and reports any operation no group
claims — that is how a newly added endpoint gets noticed instead of sitting unowned.

If a regeneration renames or retypes a generated symbol that migrated code already exposes publicly, that is a
consumer-visible break: add the Symbol Map row to `migrations/v11-migration.md` in the same PR, exactly as a
feature migration would.

| Knob | Meaning |
|---|---|
| `CHAT_BACKEND_DIR` | **Always required.** Checkout of the backend monolith |
| `PROTOCOL_DIR` | Optional. Uses `openapi/v2/chat-clientside-api.json` from a checkout, skipping spec generation |
| `PROTOCOL_REF` | Optional. Downloads that spec at a tag/branch/sha. `latest` resolves protocol's newest openapi release. Mutually exclusive with `PROTOCOL_DIR`; needs `curl` |
| `scripts/renamed-models.json` | Model renames. Currently `Response → DurationResponse` |
| `tools/rename_openapi_models.dart` | Applies those renames to a spec generated without them |
| `packages/stream_chat/build.yaml` | Builder order: freezed → json_serializable → retrofit_generator |

## Things that will bite you

- **Read the provenance line, and the stamp it writes.** The run prints
`• Using spec …json (API v237.2.0, protocol @ openapi-v237.2.0)`, and `stamp_provenance` records the same facts
at the top of `lib/open_api/api.dart`:

```dart
// Spec: chat-clientside-api (API v237.2.0)
// Source: protocol @ openapi-v237.2.0
// Checksum: 0750cb0e… // protocol's sha256 sidecar, verified before generating
// Generator: GetStream/chat @ v237.3.0-135-g59094108e1
```

`stream_feeds` stamps the same idea as a single `// Source: GetStream/protocol <path> @ <tag>` line; ours is
the aligned form, and adds the checksum and generator, which feeds records neither of.

So a checked-in client says which spec and which templates produced it. Two readings to get right:
`Generator:` is a `git describe` of the backend checkout, not a release — `v237.3.0-135-g59094108e1` means 135
commits past that tag — and a `-dirty` suffix on either line means the client came from uncommitted local edits
and should not be committed. The checksum is verified against protocol's sidecar before generating, so a locally
edited spec fails the run rather than reaching 1,477 files. The stamp carries no timestamp on
purpose: regenerating from unchanged inputs must produce no diff, which is also how you tell a real template
change from noise. Watch for drift between the two versions — a generator far ahead of the spec is worth
knowing before you debug output.
- **`Response → DurationResponse` is load-bearing.** `stream_core` re-exports `package:dio/dio.dart` wholesale, so
a generated `Response` collides with dio's inside retrofit's `.g.dart`. Protocol specs ship without the rename,
which is why `tools/rename_openapi_models.dart` applies it to a temp copy of the JSON spec.
- **`--opt` does not work for Dart.** The Dart language in the monolith's generator does not implement
`Configurable`, so anything you want configurable has to be added there first.
- **The format step covers the whole package on purpose.** `build_runner` rewrites every output it touches —
including pre-existing `.g.dart` under `lib/src/` — at the dart_style default width. Narrowing that step to
`lib/open_api` makes `melos run format` fail in CI.
- **`build_runner`'s output is the real correctness signal.** `analysis_options.yaml` excludes `**/*.g.dart` and
`**/*.freezed.dart` repo-wide, so the retrofit `.g.dart` and every generated `.freezed.dart` are invisible to
`melos run analyze`. Read the build log; don't infer success from a green analyze.

## Known generator gap: the call adapter

`client.tpl` emits `_ResultCallAdapter` over `runSafely`, but core's error layer expects `runApiSafely` — the
wrapper that turns a `DioException` into a `StreamException` and catches an undecodable body instead of letting a
`TypeError` escape. Until the template is changed upstream, a regenerated client keeps the old behaviour and every
`Failure` carries a raw `DioException`.

```
monolith/openapi/generator/templates/templates/dart/client.tpl
- Future<Result<T>> adapt(Future<T> Function() call) => runSafely(call);
+ Future<Result<T>> adapt(Future<T> Function() call) => runApiSafely(call);
```

## When the generated code is wrong

Fix the templates in the monolith (`monolith/openapi/generator/templates/templates/dart/`) and link that PR from
ours. A post-generation patch in `scripts/generate.sh` is acceptable only against a reproducible failure, and only
while the upstream fix is in flight.

There is exactly one such patch today. `EventResponse.event` and `SyncResponse.events` are typed as the WSEvent
union, and the generator emits them broken twice over: a static `fromJson` with no `toJson` and no `@JsonKey`
converters, so json_serializable can't serialize the field; plus a `.freezed.dart` that writes the `WsEvent` type
argument without its `core.` prefix. `patch_ws_event_models()` fixes both in four edits — add `WsEvent` to the
barrel's `show` list, append converter functions to `ws_event.dart`, and point each field's `@JsonKey` at them.

The patch exits with an error if the `@JsonKey(name: 'event')` line it expects is gone. That is the signal upstream
landed the fix and the patch should be deleted, not repaired.

Don't propose "make `fromJson` a factory" — `WSEvent<T>` is generic, so a factory cannot return
`_CustomEvent extends WSEvent<CustomEvent>` where `WSEvent<T>` is expected. The upstream shape is converters
emitted by `model.tpl` / `discriminator.tpl` plus a `WsEvent` re-export from `models-barrel.tpl`.

## Dependencies

`stream_chat` depends on `retrofit`, `retrofit_generator`, `json_annotation ^4.12.0` and `stream_core`.

`stream_core` is currently pinned to a **git commit**, not a hosted constraint: `melos.yaml`'s
`command.bootstrap.dependencies` names a SHA for both `stream_core` and `stream_core_flutter`,
because the v11 branch uses core APIs no published version carries. `melos bootstrap` syncs that
into each package manifest, which is why you will see a `git:` block there, and **it makes
`stream_chat`, `stream_chat_flutter_core` and `stream_chat_flutter` unpublishable until a hosted
constraint is restored**. That restore is a v11 release task — `core-migration/DEFERRED.md` is the
authority on what it waits for.

The generated client itself needs nothing newer than 0.5.0, which already ships
`StreamDateTimeConverter` and the sealed error layer, so regenerating is never what the pin is
holding up. Change the constraint in `melos.yaml` only, then `melos bootstrap` — never in a package
manifest.

That is the dependency side only. The generator is still a prerequisite on its own:
`client.tpl` emits `runSafely`, and regenerating before the template change above lands
reintroduces the old error mapping no matter which core version resolves.

If core ever goes back to a git ref for cross-repo work, it has to be pinned in every package that pulls both
`stream_chat` and `stream_core_flutter`, because pub refuses git-vs-hosted for one package and honors an override's
ref over the declared one. Find them all with:

```bash
grep -rn "stream_core:" --include=pubspec.yaml packages docs sample_app melos.yaml
```

A git dep under `dependencies` blocks publishing, so it must be back on a hosted constraint before release.
Loading
Loading