docs(catalogs): v2 measurements contract — artist_account_id filter + echo + no-cap semantics#267
Conversation
… filter, echo field, explicit no-cap semantics (chat#1850) Amends the GET /api/catalogs/measurements contract from docs#265: - optional query param artist_account_id (uuid): scope measurements + valuation to the catalog's songs linked to that artist via song_artists; absent = whole catalog. Malformed values are a 400. - response field artist_account_id (uuid, nullable): echoes the applied filter so clients can verify the response scope before trusting it. - measurements/total_streams explicitly cover ALL matching songs — no row cap (the current impl silently truncates at 1,000; fixed api-side). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
📝 WalkthroughWalkthroughUpdated the OpenAPI specification for GET /api/catalogs/measurements to document optional artist_account_id scoping, clarify exhaustive/no-row-cap read behavior, expand 400 error documentation, and add a nullable artist_account_id field to the CatalogMeasurementsResponse schema. ChangesArtist scoping documentation
Estimated code review effort: 1 (Trivial) | ~5 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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.
🧹 Nitpick comments (1)
api-reference/openapi/releases.json (1)
2469-2482: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winConsider marking
artist_account_id(and other core fields) as required in the response schema.The description explicitly tells clients to verify this echo before treating numbers as artist-scoped, but the schema has no
requiredlist, so the field is technically optional to omit. Addingartist_account_idto arequiredarray (withnullable: truealready in place) would make the contract clients are told to depend on enforceable in the spec itself.♻️ Proposed schema tweak
"CatalogMeasurementsResponse": { "type": "object", "description": "Latest per-song play counts for a catalog plus the valuation band derived from them", + "required": [ + "status", + "measurements", + "valuation", + "total_streams", + "artist_account_id" + ], "properties": {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api-reference/openapi/releases.json` around lines 2469 - 2482, The response schema for the valuation payload should explicitly require the echoed scope fields instead of leaving them optional. Update the OpenAPI schema around artist_account_id so it is listed in the relevant required array (with nullable: true preserved), and apply the same treatment to any other core response fields that clients must always receive; use the existing schema object definitions in the valuation response to keep the contract enforceable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@api-reference/openapi/releases.json`:
- Around line 2469-2482: The response schema for the valuation payload should
explicitly require the echoed scope fields instead of leaving them optional.
Update the OpenAPI schema around artist_account_id so it is listed in the
relevant required array (with nullable: true preserved), and apply the same
treatment to any other core response fields that clients must always receive;
use the existing schema object definitions in the valuation response to keep the
contract enforceable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 109682cb-2c91-4476-9722-e887ca7f66a5
📒 Files selected for processing (1)
api-reference/openapi/releases.json
… caller-paginated rows (chat#1850) Amendment per the 2026-07-07 decision on chat#1850: server-side loop-to-exhaustion is rejected. The contract becomes: - aggregates (measured_song_count, total_streams, valuation) computed in a single SQL aggregate over the latest-per-ISRC subquery — whole scope, no row cap, regardless of pagination - the measurements array is caller-paginated: page/limit params (default limit 50, max 100), same pagination envelope as GET /api/catalogs/songs, rows sorted by play count descending - artist_account_id filter + response echo unchanged; 400 also covers invalid page/limit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
| "/api/catalogs/measurements": { | ||
| "get": { | ||
| "description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song.", | ||
| "description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song. Optionally scope the read to one artist with artist_account_id: measurements and the valuation band then cover only the catalog's songs linked to that artist account. The read is always exhaustive - every matching measured song is returned and counted, with no row cap, regardless of catalog size.", |
There was a problem hiding this comment.
KISS - keep param details out of the main endpoint description.
| "description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song. Optionally scope the read to one artist with artist_account_id: measurements and the valuation band then cover only the catalog's songs linked to that artist account. The read is always exhaustive - every matching measured song is returned and counted, with no row cap, regardless of catalog size.", | |
| "description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song.", |
There was a problem hiding this comment.
Applied in 29231ea — the endpoint description is restored to the docs#265 original verbatim; the artist filter, page/limit, echo and no-cap semantics live on the query params and the response schema descriptions only.
…tails live on the params/schemas Review feedback on docs#267: restore the short docs#265 endpoint description; the artist filter, pagination, echo and no-cap semantics are documented on the query params and response schema. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
…logId}/measurements
REST amendment on chat#1850: path for identity, query for modifiers —
mirrors the research measurements family
(/api/research/albums/{id}/measurements). catalogId is a required path
param; the query carries only the optional snake_case modifiers
(artist_account_id, page, limit). A missing id is unroutable (404);
a malformed uuid in the path is a 400.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
…api/catalogs/measurements (#763) * feat(catalogs): artist_account_id filter + no-cap pagination on GET /api/catalogs/measurements (chat#1850) Two fixes on the same read path (v2 of api#757): - FIX the silent 1,000-row cap: the catalog tracklist, the measurement series, and the artist-link read all paginate to exhaustion with .range() loops (and the measurements IN clause is chunked at 500 ISRCs to stay under URL limits) BEFORE dedupe/derivation, so total_streams and the valuation band cover every measured song. Live repro was a 2,679-song catalog valued 3.4x low off exactly 1,000 rows. - ADD optional artist_account_id (uuid, 400 on malformed): scopes measurements + valuation to the catalog's songs linked to that artist account via song_artists (catalog_songs ∩ song_artists). The response echoes the applied filter as artist_account_id (null when unfiltered) so clients can verify the scope before rendering artist-labeled money. Per the docs contract in recoupable/docs#267. TDD: RED confirmed before GREEN for every unit; full suite 3,945 tests, tsc 0 new errors, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(catalogs): measurements read = whole-scope SQL aggregate + caller-paginated page (chat#1850) Amendment per the 2026-07-07 decision on chat#1850: the app-code .range() loop-to-exhaustion is rejected. The read path becomes: - aggregates (measured_song_count, total_streams -> valuation band) from the get_catalog_measurements_aggregate RPC: one SQL aggregate over the latest-per-ISRC subquery, whole scope, no row cap - the measurements array from the get_catalog_measurements_page RPC: page/limit params (default 1/50, limit max 100, 400 on invalid), rows sorted by playcount desc, same pagination envelope as GET /api/catalogs/songs - artist_account_id filter + response echo unchanged Both RPCs ship in recoupable/database#42 (applied to the shared project). Deletes the loop-based helpers (selectAllSongMeasurements, selectSongArtistIsrcs, selectCatalogSongTitles, resolveCatalogSongsInScope, latestMeasurementsPerIsrc) whose only consumer was this handler. TDD: RED confirmed before GREEN per unit; full suite 3,940 tests, tsc 200 pre-existing errors (0 new), lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(catalogs): catalogId moves to the path — GET /api/catalogs/[catalogId]/measurements (chat#1850) REST amendment on chat#1850: path for identity, query for modifiers — mirrors the research measurements family (app/api/research/albums/[id]/measurements). The route becomes a dynamic segment; the validator takes the path id (uuid, 400 on malformed; a catalogId smuggled into the query string is ignored) and the query carries only artist_account_id/page/limit. A missing id no longer routes (404 from Next), so the 'catalogId is required' 400 is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(catalogs): move auth into validateGetCatalogMeasurementsQuery (SRP) Review feedback on #763: the handler was doing auth validation itself. The validator now owns both auth and input validation (auth first), matching the measurements-family validator convention (validateGetResearchTrackHistoricStatsRequest et al) and returns { accountId, ...params }. Behavior note: an unauthenticated request with malformed params now 401s before the 400 param check, same as the research siblings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Amends the
GET /api/catalogs/measurementscontract (docs#265, merged) for the data plumbing v2: artist-scoped valuation bucket of recoupable/chat#1850.What changed (all in
api-reference/openapi/releases.json; the reference pageapi-reference/songs/catalog-measurements.mdxis frontmatter-only and unchanged)artist_account_id(uuid): when present, measurements + valuation cover only the catalog's songs linked to that artist account viasong_artists; when absent, the whole catalog. A song linked to multiple artists counts for each. Malformed values → 400 (400 description updated).artist_account_id(uuid, nullable): echoes the applied filter — the uuid when the response was artist-scoped,nullwhen whole-catalog. Lets clients (the chat homepage hero) verify the response scope before rendering artist-labeled numbers, instead of showing wrong-scoped money against a pre-v2 deployment that ignores the unknown param.measurementsandtotal_streamsnow state they cover ALL matching songs with no row cap. The current implementation silently truncates at 1,000 rows (live repro: feat(catalogs): GET /api/catalogs/measurements — per-ISRC latest playcounts + derived valuation band (chat#1850) api#757 (comment)) — being fixed in the api v2 PR.The OpenAPI JSON diff is purely additive/amending on the measurements path + response schema; every other path/schema is byte-identical (verified by structural diff after a parse round-trip).
Amended 2026-07-07
Per the dated decision on chat#1850: whole-scope SQL aggregates + a caller-paginated measurements array replace the loop-to-exhaustion read. The contract now documents
page/limitparams (defaults 1/50, max 100), thepaginationenvelope (same shape as catalog songs), and a top-levelmeasured_song_count; the aggregates (measured_song_count,total_streams,valuation) explicitly cover the ENTIRE scope regardless of pagination. Filter + echo semantics unchanged. Review feedback applied: the endpoint description stays the short docs#265 original; all details live on params/schemas.Renamed 2026-07-07
The endpoint is now
GET /api/catalogs/{catalogId}/measurements— path for identity, query for the optional modifiers (artist_account_id,page,limit), matching the research measurements family (REST decision on recoupable/chat#1850). Review feedback also applied: short endpoint description, details on params/schemas.Merge order
This docs PR merges first, then recoupable/database#42 (the RPCs), then the api v2 implementation PR (recoupable/api#763), then the chat#1852 hero rework. Contract-first per documentation-driven development.
Refs recoupable/chat#1850.
🤖 Generated with Claude Code
Summary by cubic
Respecs the v2 catalog measurements API: moves
catalogIdinto the path and adds artist-scoped reads with caller pagination while keeping aggregates full-scope with no row cap. Supports chat#1850 with artist-scoped valuation and safer client rendering via scope echoes.New Features
artist_account_idquery param; response echoesartist_account_id; malformed values return 400.page/limit(default 1/50, max 100) and apaginationenvelope; rows sorted by play count; invalid values return 400.measured_song_count; aggregates (measured_song_count,total_streams,valuation) cover the entire scope regardless of pagination — no row cap.Refactors
GET /api/catalogs/{catalogId}/measurements;catalogIdis a required path param (uuid).catalogIdand invalidpage/limit; endpoint description stays short with details in params/schemas.Written for commit acb9068. Summary will update on new commits.
Summary by CodeRabbit