ci: cut changelogs as part of the release commit - #299
Conversation
The publish workflow bumped versions and committed, but nothing moved the curated [Unreleased] block, so 6.0.4 through 6.2.0 shipped to npm with no heading in any changelog and their entries piled up as "pending". Add scripts/cut-changelog.mjs and run it in create-release before the release commit. It moves the curated block under the released version, restores a bare [Unreleased], and sweeps the root changelog plus every lockstep packages/*/CHANGELOG.md. Notes: - Curated entries are authoritative; commit subjects are a fallback for the root changelog only when its block is empty, so a package with nothing pending gets no heading instead of duplicated noise. - packages/sdk-rust is excluded — it ships on its own crates.io version line. New package changelogs are swept by default. - Prereleases leave the block pending. - A pending level higher than the actual bump warns rather than fails; the step runs after npm publish, so it must never break a release. - The rebase-onto-main retry loop now resolves a changelog conflict (a PR curating [Unreleased] mid-release) by taking main's copy and re-cutting it, instead of failing a release that is already published. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TRUCraRPGnM6CP59qoxyd6
|
Warning Review limit reached
Next review available in: 38 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4b73acee9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| const version = flag('version'); | ||
| if (!version || !/^\d+\.\d+\.\d+(?:-[\w.]+)?$/.test(version)) { |
There was a problem hiding this comment.
Accept hyphens in valid prerelease versions
When workflow_dispatch supplies a custom version such as 6.3.0-rc-1, the build and npm publishing steps accept that valid SemVer prerelease, but this narrower regex rejects it in the subsequent create-release job. Because changelog cutting runs after publishing, the workflow then stops before creating the release commit, tag, and GitHub release, leaving already-published packages stranded; validate with npm's SemVer parser rather than this regex.
AGENTS.md reference: AGENTS.md:L42-L42
Useful? React with 👍 / 👎.
Main refiled the pending 6.0.4–6.2.0 entries under release headings (#298) and added scripts/cut-changelog.mjs (#299). The textual merge landed this branch's types entry inside the now-released [6.2.0] block, which would have credited an unreleased change to a shipped version — moved it back under [Unreleased], and raised that heading from Patch to Minor since widening an exported const/union is additive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CxUMkEa2foSTbw88sqEFWA
…nd API docs Addresses the three review findings on #300. All were valid. **Agent sockets lost the new dimensions (P2).** `AgentClient.connect()` copied only `agentRelayDistinctId` from its inherited `HttpClient`, so a `/v1/node/ws` session reported `ws_session_started` as unauthenticated and without user/machine/org — even though the same client's HTTP requests carried them. Same class of bug as the `RelayCast` one this PR already fixed; I'd missed the sibling. Rather than adding the four fields to a third hand-copy, the root cause was the duplication itself: three places (`withApiKey`, `RelayCast`, `AgentClient`) each spread origin fields by hand, so any new dimension had to be remembered in all three. Added `HttpClient.internalOrigin` as the single source and pointed all three at it — a new dimension now reaches every socket at once. The regression test asserts the actual agent socket URL, and I verified it goes red against the old hand-copy before going green on the fix. **Changelog claimed a shipped release (P1).** My rebase resolution put these entries inside the dated `[6.2.0]` section — main's #299 had restructured the file underneath me — falsely telling users the published package supports these options, and leaving the release tooling nothing to carry forward. Moved to `[Unreleased]`, raised Patch → Minor per the monotonic rule in AGENTS.md, and added the package-level API detail to the engine and TypeScript SDK changelogs that AGENTS.md requires. **Undocumented API inputs (P1).** New accepted headers and WS query params were in neither `README.md` nor `openapi.yaml`, so non-SDK callers had no documented contract. Both now carry the full table, the charset/length rules, and why the query-param forms exist. Both docs were also stale independently: each described a `harness` option and an `X-Relaycast-Harness` header that the engine no longer reads anywhere (it was superseded by `X-Relaycast-Origin-Actor`). Corrected to the actual contract while adding the identity fields. Validation: turbo lint+build 17/17, engine 518 passed, SDK 414 passed, openapi.yaml re-parsed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEGpCHvkLVy7AhPgjrCbcG
…ty (#300) * feat(telemetry): accept caller-declared user and org identity A relaycast workspace is an API-key row — there is no user table — so server telemetry could only ever report workspaces, never people or companies. Callers that do know who is driving a request (the Agent Relay CLI and broker, after `agent-relay cloud login`) can now declare it: `X-Agent-Relay-User-Id` / `-Org-Id` / `-Org-Slug`, or the matching `agent_relay_*` query params on a WebSocket upgrade, since browsers can't set custom headers on a handshake. `emitServerEvent` records them as `actor_user_id` / `actor_org_id` / `actor_org_slug` and prefers the user id as the distinct id, so hosted events land on the same PostHog person as that user's CLI and broker events. These are analytics dimensions only and never affect authorization — malformed values are dropped rather than truncated. The TS SDK accepts `agentRelayUserId` / `agentRelayOrgId` / `agentRelayOrgSlug`, and a supplied user id doubles as the distinct id so a host needs to set only one. Identity resolution is centralized in `resolveAgentRelayIdentity` rather than threaded field-by-field. Fixes a related gap: SDK identity reached HTTP requests but never the WebSocket, so `ws_session_started` was anonymous even for an identified caller. `RelayCast` now forwards it to the socket too. The Python, Rust, and Swift SDKs still send only `agentRelayDistinctId`; adding the user/org fields there is follow-up work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEGpCHvkLVy7AhPgjrCbcG * feat(telemetry): add actor_machine_id so machine cross-tabs survive login Keying events on the user id answered "how many people" but destroyed "how many machines": once a caller signs in, the distinct id IS the user id, so the machine dimension disappeared exactly when it got interesting. `X-Agent-Relay-Machine-Id` (and `agent_relay_machine_id` on a WS upgrade) is now read into `actor_machine_id`, reported alongside `actor_user_id` rather than instead of it. With `workspace_id` already on every server event, that makes three questions answerable: - machines per workspace — distinct actor_machine_id by workspace_id - accounts per machine — distinct actor_user_id by actor_machine_id - machines per account — distinct actor_machine_id by actor_user_id The second is the interesting one: several machines on one workspace signed into different cloud accounts is a shared team workspace; the same account everywhere is one person on several hosts. The machine id is also the distinct-id fallback for anonymous callers, so not-logged-in traffic is still attributable to a host rather than collapsing onto the workspace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEGpCHvkLVy7AhPgjrCbcG * fix(sdk,docs): forward identity to agent sockets; correct changelog and API docs Addresses the three review findings on #300. All were valid. **Agent sockets lost the new dimensions (P2).** `AgentClient.connect()` copied only `agentRelayDistinctId` from its inherited `HttpClient`, so a `/v1/node/ws` session reported `ws_session_started` as unauthenticated and without user/machine/org — even though the same client's HTTP requests carried them. Same class of bug as the `RelayCast` one this PR already fixed; I'd missed the sibling. Rather than adding the four fields to a third hand-copy, the root cause was the duplication itself: three places (`withApiKey`, `RelayCast`, `AgentClient`) each spread origin fields by hand, so any new dimension had to be remembered in all three. Added `HttpClient.internalOrigin` as the single source and pointed all three at it — a new dimension now reaches every socket at once. The regression test asserts the actual agent socket URL, and I verified it goes red against the old hand-copy before going green on the fix. **Changelog claimed a shipped release (P1).** My rebase resolution put these entries inside the dated `[6.2.0]` section — main's #299 had restructured the file underneath me — falsely telling users the published package supports these options, and leaving the release tooling nothing to carry forward. Moved to `[Unreleased]`, raised Patch → Minor per the monotonic rule in AGENTS.md, and added the package-level API detail to the engine and TypeScript SDK changelogs that AGENTS.md requires. **Undocumented API inputs (P1).** New accepted headers and WS query params were in neither `README.md` nor `openapi.yaml`, so non-SDK callers had no documented contract. Both now carry the full table, the charset/length rules, and why the query-param forms exist. Both docs were also stale independently: each described a `harness` option and an `X-Relaycast-Harness` header that the engine no longer reads anywhere (it was superseded by `X-Relaycast-Origin-Actor`). Corrected to the actual contract while adding the identity fields. Validation: turbo lint+build 17/17, engine 518 passed, SDK 414 passed, openapi.yaml re-parsed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEGpCHvkLVy7AhPgjrCbcG * fix(engine,sdk): drop oversized identity values; validate each source in turn **Oversized actor dimensions are now dropped, not truncated** (engine). A truncated user or org id is a *different* id — it can collide with a real one and attribute usage to the wrong person or company, so no attribution beats wrong attribution. Only the four new actor dimensions changed; `agent_relay_distinct_id` keeps its shipped cap-at-128 behaviour (covered by an existing test on main), so `readIdentityValue` now takes an explicit `onOversize` rather than hiding the difference. Every SDK caps well below these limits, so only a malformed caller is affected. **Identity source selection validates before it picks** (SDK). `pick` returned the first non-empty candidate and sanitized afterwards, so a malformed higher-priority value shadowed a valid lower-priority one and dropped the dimension entirely — a wrapping host with a bad internal value silently lost identity the caller had supplied correctly. Sanitizing inside the loop fixes that and, as a side effect, computes each org value once instead of twice. Both regressions are covered by tests verified red against the previous code. Docs and changelogs updated to match: the length rule and its rationale in `README.md` and `openapi.yaml`, and the entries condensed to the impact-first form AGENTS.md asks for (dropping `HttpClient.internalOrigin`, which is internal plumbing rather than user-facing API). Not adopted, with reasons: - Zod schemas for the length checks and the SDK sanitizers. `origin.ts` has no Zod dependency today and these run per request and per client construction; expressing "trim → charset → bound" as a schema is longer and slower for identical behaviour. `sanitizeAgentRelayDistinctId` is also a published export whose truncate-at-128 semantics must be preserved exactly, which `.max()` inverts. Validation: turbo lint+build 17/17, engine 520 passed, SDK 416 passed, openapi.yaml re-parsed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEGpCHvkLVy7AhPgjrCbcG --------- Co-authored-by: Claude <noreply@anthropic.com>
Follow-up to #298, which filed 6.0.4–6.2.0 by hand. This stops it recurring.
Problem
publish-npm.ymlbumps versions and commits, but nothing moves the curated[Unreleased]block —CHANGELOG.mdisn't even in thegit add. Four releases went to npm with no heading in any changelog while their entries piled up as "pending". Relay'spublish.ymlhas solved this for a while; this ports the idea with the multi-package handling relaycast needs.Changes
scripts/cut-changelog.mjs— moves the curated block under the released version, restores a bare[Unreleased], sweeps the root changelog plus every locksteppackages/*/CHANGELOG.md.packages/sdk-rustis excluded (independent crates.io version line). Package changelogs added later are swept by default.6.3.0-beta.1) leave the block pending.[Unreleased - Minor]released as a patch) emits a warning. It never fails: the step runs afternpm publish, so a hard failure would strand a published release without a tag.publish-npm.yml— runs the script increate-releasebefore the release commit, and stages the changelogs alongside the version bumps.The rebase-onto-main retry loop needed one change. Version metadata never conflicted, but a changelog does — any PR curating
[Unreleased]during the multi-minute build touches the same lines. On a conflict confined toCHANGELOG.mdfiles it now takes main's copy, re-cuts it, and continues; anything else still aborts.AGENTS.md— the "when a release is cut, move the pending entries" bullet now points at the script, so nobody hand-cuts a release.Verification
Run against the real tree (
--dry-runand for real, reverted after):Also exercised in a throwaway clone: prerelease skip; the commit-subject fallback with an emptied block (produced
### Fixed / - Remove raw NUL byte from source and correct deliver fixture— PR ref stripped,docs:commit skipped); the level-mismatch warning; and a forced mid-release conflict where a concurrent PR raised the pending heading to- Minor— the resolution path kept that PR's entries, cut them into the release, and completed the rebase.Not verifiable until a real release: the workflow wiring itself (job ordering,
needs.build.outputs.new_version). The script and the conflict block were tested directly.🤖 Generated with Claude Code
https://claude.ai/code/session_01TRUCraRPGnM6CP59qoxyd6
Generated by Claude Code