Skip to content

feat: list ordered playlist items - #27

Merged
rianjs merged 1 commit into
mainfrom
feat/23-list-playlist-items
Jul 24, 2026
Merged

rianjs merged 1 commit into
mainfrom
feat/23-list-playlist-items

Conversation

@rianjs

@rianjs rianjs commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add sptfy playlists items list <playlist-reference> on Spotify's current /items API
  • preserve absolute positions and track breadcrumbs across playlist-bound continuation pages
  • render track, episode, local, unavailable, and future item shapes safely as token-efficient text
  • verify exact ordered fixture IDs through static and real Spotify live smoke coverage

Verification

  • make check
  • make snapshot
  • focused client/output/playlist-command/session/root tests
  • static live-smoke harness
  • real make live-smoke against the dedicated owned playlist fixture, including expected positions 0, 1, 2 and continuation

Closes #23

@rianjs

rianjs commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Independent test assessment — PR #27 / issue #23

Verdict

Needs a small test/validation follow-up before every acceptance criterion is proved. The implementation matches Spotify's current playlist contract and the broad suite is strong, but page-total/continuation consistency is neither validated nor tested, and the static/live evidence has two narrower auditability gaps.

Spotify's current reference defines GET /playlists/{playlist_id}/items, the item response member, 1–50 limits, and additional_types; the February 2026 changelog records /tracks → /items and track → item. The PR is aligned with both:

Evidence that is sufficient

  • Provider coverage proves the fixed host, exact /v1/playlists/{id}/items path, exact additional_types=episode&limit=…&offset=… query, 1/50 request bounds, resumed offset, one request despite a hostile next URL, required items/track artist/album image/episode image arrays, nonnegative totals and durations, and present item-ID validation.
  • Current item decoding and rendering cover track, episode, local, null/unavailable, future typed, and untyped/unknown shapes. Track breadcrumbs, non-track - cells, sanitization, artwork, and ID omission are asserted.
  • Absolute positions are proved at offsets 0, 10, and 20. Continuation tokens are bound to playlist-items:<playlist-id>; malformed, wrong-surface, and cross-playlist tokens fail before session opening.
  • Raw/URI/URL playlist references, invalid references, 1/50 command bounds, unknown fields, parent rendering, empty output, --id precedence, stdout data, stderr continuation hints, and both existing playlist scopes before provider calls are covered.
  • Session/root integration reaches the exact authenticated provider path and renders the stable default contract. The diff adds no mutation command, dependency, or OAuth scope.
  • The live script checks fixture mappings at positions 0 and 1, resumed position 2, and ID-only order. Its no-network harness executes successfully.

Independent runs in the PR worktree:

  • Focused Go suites: 194 passed across client, output, playlist command, session, and root.
  • scripts/live-smoke-test.sh: passed.
  • make check: passed, including lint and both full Go test variants; the worktree remained clean.
  • make snapshot and real make live-smoke were not rerun here because they generate artifacts or require the dedicated Spotify account. The PR body records both as passed and names playlist 6r6QiyzQmEqg9nVzM2tiTo, with positions 0, 1, 2 and continuation.

Gaps, ranked by worth fixing

  1. High — page metadata can contradict itself. internal/client/playlist_items_test.go:34 returns six rows at offset 20 with total:26 and a non-null next; that page is already exhausted. internal/client/client.go:369-383 accepts it and reports HasNext. Add a consistency guard/test for returned rows versus total and contradictory continuation metadata; change the positive hostile-URL fixture to a total greater than 26. This is the only observed production-behavior gap against “validate page metadata before rendering.”

  2. Medium — the static live harness does not prove the commands target the configured fixture and page sizes. The fake branch at scripts/live-smoke-test.sh:55-67 matches any playlists items list invocation and never asserts the playlist ID, --max 2, resumed token invocation, or --id --max 3. The harness can therefore pass after those arguments regress. Add strict argument checks, as the neighboring playlist-get fake already does.

  3. Medium — the recorded real-live result is an attestation, not a self-contained fixture mapping. The PR body records the playlist and positions 0/1/2, but no position:item-id values or captured output. Record the three mappings (or stable redacted hashes) and successful command/gate result so the empirical criterion is independently auditable.

  4. Low — populated full --extended output is only partially asserted. The command test checks URI, added-by, disc number, explicit, and restriction; the output test checks the complete extended header only on an empty page. URL, ADDED_AT, and TRACK_NUMBER are decoded but never proved end-to-end in a populated exact --extended row. One existing test row can cover all columns without adding a new test framework.

With items 1–2 fixed and item 3 recorded, the acceptance criteria are adequately proved; item 4 is cheap confidence rather than a likely implementation defect.

@rianjs

rianjs commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Real Spotify ordered-items evidence

make live-smoke passed against the configured dedicated playlist with these SHA-256 fixture fingerprints in provider order:

0:5d4d937033c7dde37469d627abcbbf9ec8b603d2792239b473ccbf750ea0a7cc
1:77921d61b898b3f9889bc60d2b1ded41e35938e8334410842f34a532125c9751
2:8a969ebb6b4c5481383f94311cd072781f530db369c9bee4388ecd5fe20a7356

The first --max 2 page matched positions 0 and 1, its opaque continuation resumed with position 2, and --id --max 3 matched the same three IDs in order. The raw Spotify IDs are intentionally not posted. The remainder of the hermetic live suite also passed and removed its credential/state on exit.

@rianjs
rianjs force-pushed the feat/23-list-playlist-items branch from f63cbfb to 6a42819 Compare July 24, 2026 17:48

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: 6a428197ec65
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
policies:conventions 0
architecture:solid-reviewer-agnostic 0
documentation:docs 0
automation:ci-release 1
automation:ci-release (1 finding)

Minor - scripts/live-smoke.sh:153

The continuation assertion only checks the value at row 3. A --max 2 response that includes extra rows (or a continuation with an incorrect parent/header) still passes as long as its first data row is 2:<expected-id>, so this smoke gate does not reliably validate page-size and continuation consistency. Assert the complete first and continuation page shapes/counts (including parent/header), and require the fixture/count expectation needed to prove that the continuation has exactly the expected remaining item.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Focused Go tests were run, but sandbox policy prevents httptest from binding a loopback port; affected client/root packages could not complete. Output, playlist-command, and session package tests passed.
policies:conventions complete_broad README.md, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Shared cli-common and .github convention copies were not available locally; review used the repo-local development guide and behavioral specification.
architecture:solid-reviewer-agnostic complete_broad internal/client/client.go, internal/cmd/playlistcmd/playlist.go, internal/output/playlist.go, internal/session/session.go unavailable Full make check was blocked by a local macOS cgo/keychain dependency type-check failure unrelated to the diff; focused client, command, output, and session tests passed with CGO disabled.; Review scope was limited to the four assigned changed files, with related tests and repository guidance inspected for context.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Reviewed only the assigned documentation files, validating their changed command/output claims against the local implementation and live-smoke harness; provider access-policy wording was checked against Spotify's current official API reference.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to the two assigned smoke scripts; no real Spotify live run was possible without fixture credentials.

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 3m 01s | unavailable | gpt-5.6-terra, gpt-5.6-sol | cr 0.10.267
Field Value
Model gpt-5.6-terra, gpt-5.6-sol
Reviewers go:implementation-tests, policies:conventions, architecture:solid-reviewer-agnostic, documentation:docs, automation:ci-release
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 3m 01s wall · 6m 24s compute
Cost unavailable
Tokens 1.7M in / 15.5k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 18.7k 505 11.0k unavailable unavailable 12s
go:implementation-tests gpt-5.6-terra 340.6k 3.2k 282.1k unavailable unavailable 1m 16s
policies:conventions gpt-5.6-terra 127.9k 1.5k 93.7k unavailable unavailable 33s
architecture:solid-reviewer-agnostic gpt-5.6-sol 807.2k 5.3k 736.8k unavailable unavailable 2m 26s
documentation:docs gpt-5.6-terra 289.6k 2.3k 240.4k unavailable unavailable 59s
automation:ci-release gpt-5.6-terra 100.0k 2.2k 69.6k unavailable unavailable 52s
orchestrator-rollup gpt-5.6-terra 39.9k 579 29.2k unavailable unavailable 4s

Comment thread scripts/live-smoke.sh Outdated
playlist_items_token=$(sed -n 's/^More results available (next: \(.*\))$/\1/p' "$playlist_items_page_err")
[[ -n $playlist_items_token ]] || { printf '%s\n' 'playlist items did not return a continuation token' >&2; exit 1; }
playlist_items_next=$("$SPTFY" --backend file playlists items list "$SPOTIFY_CLI_LIVE_PLAYLIST_ID" --max 2 --next-page-token "$playlist_items_token")
[[ $(awk -F ' \\| ' 'NR == 3 {print $1":"$3}' <<<"$playlist_items_next") == "2:${playlist_expected_item_ids[2]}" ]] || { printf '%s\n' 'playlist item continuation did not resume at position two' >&2; exit 1; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The continuation assertion only checks the value at row 3. A --max 2 response that includes extra rows (or a continuation with an incorrect parent/header) still passes as long as its first data row is 2:<expected-id>, so this smoke gate does not reliably validate page-size and continuation consistency. Assert the complete first and continuation page shapes/counts (including parent/header), and require the fixture/count expectation needed to prove that the continuation has exactly the expected remaining item.

Reply inline to this comment.

@rianjs
rianjs force-pushed the feat/23-list-playlist-items branch from 6a42819 to d09eb0e Compare July 24, 2026 17:54

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: d09eb0e9ee24
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 1
automation:ci-release 0
policies:conventions 0
architecture:solid-reviewer-agnostic 0
documentation:docs 0
go:implementation-tests (1 finding)

Major - internal/client/client.go:375

The page validation accepts an underfilled non-final response (for example, limit=10, offset=0, total=20, five items, and a non-empty next). The command then encodes offset + page.Limit (10) for continuation, silently skipping positions 5–9 despite the feature’s ordered, absolute-position contract. Reject HasNext pages unless they contain exactly limit entries (and add a malformed-response test for this case), or otherwise derive and validate a continuation offset that cannot skip returned-provider positions.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Focused Go tests passed for output, playlist command, and session; client/root package runs are blocked by sandbox restrictions on httptest listeners.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to the two assigned live-smoke scripts and their automation contract.
policies:conventions complete_broad README.md, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Focused Go tests could not build because the environment's clang module-cache path is split at a space; static live-smoke validation passed.; Shared cli-common and .github convention copies were not available locally; review used repo-local guidance and the changed contract/docs.
architecture:solid-reviewer-agnostic complete_broad internal/client/client.go, internal/cmd/playlistcmd/playlist.go, internal/output/playlist.go, internal/session/session.go unavailable Focused client, playlist-command, output, and session tests passed; the full client suite could not run because the sandbox prohibits httptest loopback listeners. Linting session code was also blocked by sandboxed cgo preprocessing.; Review was limited to the four assigned changed files plus relevant repository guidance, surrounding code, and focused tests.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Review limited to the assigned documentation files; implementation was inspected only to verify documented command and output behavior.

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 2m 26s | unavailable | gpt-5.6-terra, gpt-5.6-sol | cr 0.10.267
Field Value
Model gpt-5.6-terra, gpt-5.6-sol
Reviewers go:implementation-tests, automation:ci-release, policies:conventions, architecture:solid-reviewer-agnostic, documentation:docs
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 2m 26s wall · 6m 33s compute
Cost unavailable
Tokens 2.0M in / 16.7k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 67.0k 1.0k 49.9k unavailable unavailable 10s
go:implementation-tests gpt-5.6-terra 444.2k 4.5k 382.5k unavailable unavailable 1m 47s
automation:ci-release gpt-5.6-terra 112.0k 1.2k 86.5k unavailable unavailable 37s
policies:conventions gpt-5.6-terra 443.8k 3.2k 383.5k unavailable unavailable 1m 22s
architecture:solid-reviewer-agnostic gpt-5.6-sol 661.2k 4.2k 594.9k unavailable unavailable 1m 52s
documentation:docs gpt-5.6-terra 164.4k 1.5k 126.7k unavailable unavailable 38s
orchestrator-rollup gpt-5.6-terra 96.5k 1.1k 76.8k unavailable unavailable 5s

Comment thread internal/client/client.go
}
itemCount := len(*response.Items)
hasNext := response.Next != nil && *response.Next != ""
if itemCount > 0 && (itemCount > response.Total || response.Offset > response.Total-itemCount) ||

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page validation accepts an underfilled non-final response (for example, limit=10, offset=0, total=20, five items, and a non-empty next). The command then encodes offset + page.Limit (10) for continuation, silently skipping positions 5–9 despite the feature’s ordered, absolute-position contract. Reject HasNext pages unless they contain exactly limit entries (and add a malformed-response test for this case), or otherwise derive and validate a continuation offset that cannot skip returned-provider positions.

Reply inline to this comment.

@rianjs
rianjs force-pushed the feat/23-list-playlist-items branch from d09eb0e to 9da0e73 Compare July 24, 2026 17:59

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: 9da0e7303d1c
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
automation:ci-release 1
architecture:solid-reviewer-agnostic 1
policies:conventions 0
documentation:docs 0
automation:ci-release (1 finding)

Major - scripts/live-smoke.sh:154

The harness accepts an expected-ID list with three or more entries, but this assertion requires the continuation to contain exactly one row. A valid dedicated fixture with four or more configured items will return a full second --max 2 page and make make live-smoke fail even though pagination is correct. Either require exactly three fixture IDs/items at validation time, or validate a two-row continuation and its expected fourth item when present.

architecture:solid-reviewer-agnostic (1 finding)

Major - internal/client/client.go:455

U-L1: PlaylistItemPage is described as validated, and the project contract promises album and artist-ID breadcrumbs for every track, but this validation only checks that artists, album, and album.images are non-null. A malformed track with no artists, an empty/invalid album.id, or empty/invalid artist IDs is accepted and rendered with missing breadcrumbs, turning an upstream contract violation into apparently valid output. Require at least one artist and validate the album and every artist ID with spotifyref.ValidID; add malformed-response cases for each missing/invalid breadcrumb.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Focused Go tests could not complete in this sandbox because local test servers cannot bind and CGO compilation paths fail; review used static inspection and existing test coverage.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to the assigned live-smoke scripts. The shell harness passed; the tagged Go livesmoke check could not build because the sandbox's clang module-cache path is invalid.
architecture:solid-reviewer-agnostic complete_broad internal/client/client.go, internal/cmd/playlistcmd/playlist.go, internal/output/playlist.go, internal/session/session.go unavailable Full make check could not complete because the artifact checkout lacks a valid .git/refs directory and the sandbox blocks network/listener operations. Focused playlist client, command, output, and session tests passed with CGO disabled.; Review was limited to the four assigned changed files, with related tests and repository behavior guidance inspected for context.
policies:conventions complete_broad README.md, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Focused Go tests could not build in this sandbox because clang could not resolve the workspace path; shell smoke validation passed.; The optional local shared-conventions and shared-automation checkouts were absent, so review used the available repo-local development guide and behavioral specification.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Review limited to the assigned documentation files and their local implementation/test context; external Spotify documentation was not independently checked.

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 5m 03s | unavailable | gpt-5.6-terra, gpt-5.6-sol | cr 0.10.267
Field Value
Model gpt-5.6-terra, gpt-5.6-sol
Reviewers go:implementation-tests, automation:ci-release, architecture:solid-reviewer-agnostic, policies:conventions, documentation:docs
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 5m 03s wall · 8m 39s compute
Cost unavailable
Tokens 3.3M in / 23.6k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 174.2k 1.9k 140.8k unavailable unavailable 10s
go:implementation-tests gpt-5.6-terra 243.0k 2.4k 187.9k unavailable unavailable 1m 01s
automation:ci-release gpt-5.6-terra 172.0k 2.0k 138.5k unavailable unavailable 58s
architecture:solid-reviewer-agnostic gpt-5.6-sol 2.1M 10.7k 2.0M unavailable unavailable 4m 35s
policies:conventions gpt-5.6-terra 220.7k 2.3k 167.4k unavailable unavailable 1m 01s
documentation:docs gpt-5.6-terra 159.4k 2.2k 118.0k unavailable unavailable 46s
orchestrator-rollup gpt-5.6-terra 218.9k 2.0k 182.5k unavailable unavailable 4s

Comment thread scripts/live-smoke.sh Outdated
playlist_items_token=$(sed -n 's/^More results available (next: \(.*\))$/\1/p' "$playlist_items_page_err")
[[ -n $playlist_items_token ]] || { printf '%s\n' 'playlist items did not return a continuation token' >&2; exit 1; }
playlist_items_next=$("$SPTFY" --backend file playlists items list "$SPOTIFY_CLI_LIVE_PLAYLIST_ID" --max 2 --next-page-token "$playlist_items_token")
[[ $(wc -l <<<"$playlist_items_next") -eq 3 ]] || { printf '%s\n' 'playlist item continuation did not return exactly one row' >&2; exit 1; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The harness accepts an expected-ID list with three or more entries, but this assertion requires the continuation to contain exactly one row. A valid dedicated fixture with four or more configured items will return a full second --max 2 page and make make live-smoke fail even though pagination is correct. Either require exactly three fixture IDs/items at validation time, or validate a two-row continuation and its expected fourth item when present.

Reply inline to this comment.

Comment thread internal/client/client.go Outdated
Images *[]Image `json:"images"`
} `json:"album"`
}
if json.Unmarshal(raw, &track) != nil || track.Artists == nil || track.Album == nil || track.Album.Images == nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

U-L1: PlaylistItemPage is described as validated, and the project contract promises album and artist-ID breadcrumbs for every track, but this validation only checks that artists, album, and album.images are non-null. A malformed track with no artists, an empty/invalid album.id, or empty/invalid artist IDs is accepted and rendered with missing breadcrumbs, turning an upstream contract violation into apparently valid output. Require at least one artist and validate the album and every artist ID with spotifyref.ValidID; add malformed-response cases for each missing/invalid breadcrumb.

Reply inline to this comment.

@rianjs
rianjs force-pushed the feat/23-list-playlist-items branch from 9da0e73 to bbdac0a Compare July 24, 2026 18:07

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: bbdac0ad2e08
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
automation:ci-release 1
architecture:solid-reviewer-agnostic 0
policies:conventions 0
documentation:docs 1
automation:ci-release (1 finding)

Minor - scripts/live-smoke.sh:153

The continuation command captures only stdout, so the harness never verifies that the expected final page has no further continuation token. A broken paginator could return the expected position-2 row while emitting another More results available marker (or a fixture could acquire a fourth item), and this smoke test would still pass even though it has not validated the complete three-item fixture. Redirect this command's stderr to a continuation error file and assert it contains no continuation marker (ideally is empty); extend the fake harness to exercise that failure case.

documentation:docs (1 finding)

Major - spotify-cli-behavior.md:353

This says playlists get remains available for public playlist reads, but Spotify’s current Get Playlist reference also limits that endpoint to playlists owned by the current user or where the user is a collaborator. This will cause maintainers to expect a supported public-read fallback that returns 403 in practice. Remove the public-read claim and document the same ownership/collaborator restriction for playlists get if it is intended to describe the provider contract.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Focused Go implementation/test review only. Full focused test run was constrained by sandbox loopback-listener restrictions and compiler path issues; internal/output tests passed.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to the assigned live-smoke harness scripts and their automation assertions.
architecture:solid-reviewer-agnostic complete_broad internal/client/client.go, internal/cmd/playlistcmd/playlist.go, internal/output/playlist.go, internal/session/session.go unavailable Full make check could not run reliably in the managed sandbox because listener-based tests cannot bind ports and the workspace path triggers a CGO toolchain issue; focused tests for all assigned layers, static live-smoke checks, formatting checks, and lint analysis completed without findings.; Review was limited to the four assigned changed files and relevant repository guidance/tests.
policies:conventions complete_broad README.md, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Focused Go tests could not build because the toolchain failed to resolve the workspace path containing spaces; the static live-smoke harness completed successfully.; The optional local cli-common and shared .github standards copies were not present, so review used repo-local development and behavioral documentation.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Narrow documentation review; implementation and relevant tests were inspected to verify the documented command/output contracts.

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 4m 01s | unavailable | gpt-5.6-terra, gpt-5.6-sol | cr 0.10.267
Field Value
Model gpt-5.6-terra, gpt-5.6-sol
Reviewers go:implementation-tests, automation:ci-release, architecture:solid-reviewer-agnostic, policies:conventions, documentation:docs
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 4m 01s wall · 7m 56s compute
Cost unavailable
Tokens 2.5M in / 23.1k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 270.1k 2.4k 226.8k unavailable unavailable 9s
go:implementation-tests gpt-5.6-terra 302.0k 3.3k 252.2k unavailable unavailable 1m 21s
automation:ci-release gpt-5.6-terra 103.4k 2.4k 73.7k unavailable unavailable 53s
architecture:solid-reviewer-agnostic gpt-5.6-sol 978.8k 7.9k 868.9k unavailable unavailable 3m 26s
policies:conventions gpt-5.6-terra 215.9k 2.2k 170.5k unavailable unavailable 1m 02s
documentation:docs gpt-5.6-terra 261.5k 2.5k 209.7k unavailable unavailable 58s
orchestrator-rollup gpt-5.6-terra 323.8k 2.5k 277.8k unavailable unavailable 5s

Comment thread spotify-cli-behavior.md Outdated
opaque continuation token is bound to the playlist-items surface and parsed
playlist ID; provider response URLs are never followed. Spotify currently
permits this item read only for playlists the current user owns or
collaborates on; `playlists get` remains available for public playlist reads.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says playlists get remains available for public playlist reads, but Spotify’s current Get Playlist reference also limits that endpoint to playlists owned by the current user or where the user is a collaborator. This will cause maintainers to expect a supported public-read fallback that returns 403 in practice. Remove the public-read claim and document the same ownership/collaborator restriction for playlists get if it is intended to describe the provider contract.

Reply inline to this comment.

Comment thread scripts/live-smoke.sh Outdated
[[ $(awk -F ' \\| ' 'NR == 4 {print $1":"$3}' "$playlist_items_page_out") == "1:${playlist_expected_item_ids[1]}" ]] || { printf '%s\n' 'playlist item one did not match the fixture' >&2; exit 1; }
playlist_items_token=$(sed -n 's/^More results available (next: \(.*\))$/\1/p' "$playlist_items_page_err")
[[ -n $playlist_items_token ]] || { printf '%s\n' 'playlist items did not return a continuation token' >&2; exit 1; }
playlist_items_next=$("$SPTFY" --backend file playlists items list "$SPOTIFY_CLI_LIVE_PLAYLIST_ID" --max 2 --next-page-token "$playlist_items_token")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The continuation command captures only stdout, so the harness never verifies that the expected final page has no further continuation token. A broken paginator could return the expected position-2 row while emitting another More results available marker (or a fixture could acquire a fourth item), and this smoke test would still pass even though it has not validated the complete three-item fixture. Redirect this command's stderr to a continuation error file and assert it contains no continuation marker (ideally is empty); extend the fake harness to exercise that failure case.

Reply inline to this comment.

Add current-schema playlist item reads with absolute positions, mixed-media-safe text output, playlist-bound continuation tokens, and ordered live-smoke verification.

Closes #23
@rianjs
rianjs force-pushed the feat/23-list-playlist-items branch from bbdac0a to a17b61c Compare July 24, 2026 18:14

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: a17b61cc7be7
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
automation:ci-release 0

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Focused Go implementation and behavioral-test review; the targeted test command could not complete because the sandbox disallows test listeners and the toolchain failed on the workspace path.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to assigned live-smoke harness files and automation behavior.
unassigned incomplete_unassigned unavailable README.md, spotify-cli-behavior.md changed files were not assigned to a selected reviewer

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 1m 53s | unavailable | gpt-5.6-terra | cr 0.10.267
Field Value
Model gpt-5.6-terra
Reviewers go:implementation-tests, automation:ci-release
Engine codex_cli · gpt-5.6-terra
Reviewed by cr · monit-reviewer
Duration 1m 53s wall · 1m 59s compute
Cost unavailable
Tokens 1.2M in / 10.0k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 384.1k 2.7k 331.3k unavailable unavailable 8s
go:implementation-tests gpt-5.6-terra 259.5k 3.4k 204.8k unavailable unavailable 1m 19s
automation:ci-release gpt-5.6-terra 105.0k 1.0k 81.4k unavailable unavailable 27s
orchestrator-rollup gpt-5.6-terra 445.9k 2.8k 391.4k unavailable unavailable 4s

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: a17b61cc7be7
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
automation:ci-release 0
documentation:docs 0
policies:conventions 0
architecture:solid-reviewer-agnostic 0

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/client/client.go, internal/client/playlist_items_test.go, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, internal/output/playlist.go, internal/output/playlist_test.go, internal/session/session.go, internal/session/session_test.go unavailable Full client/root package tests are blocked by the sandbox disallowing httptest listeners; focused playlist-item client tests and playlistcmd/output/session tests passed.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to assigned smoke-harness scripts and their automation contract.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Review limited to assigned documentation files; implementation and current official Spotify endpoint documentation were consulted to verify documented behavior.
policies:conventions complete_broad README.md, internal/cmd/playlistcmd/playlist.go, internal/cmd/playlistcmd/playlist_test.go, internal/cmd/root/root_test.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Focused test commands could not build because the sandboxed clang module-cache path is unavailable; static inspection found no convention drift.; Reviewed only the assigned files and repository-local development guidance. The optional local cli-common and shared-automation documentation copies were unavailable, so no unverified shared-policy assumptions were made.
architecture:solid-reviewer-agnostic complete_broad internal/client/client.go, internal/cmd/playlistcmd/playlist.go, internal/output/playlist.go, internal/session/session.go unavailable Full make check could not complete because the sandbox blocked the default golangci-lint cache and macOS cgo preprocessing; focused playlist tests passed and scoped lint passed using writable temporary caches with cgo disabled.; Review was limited to the four assigned changed files, with relevant repository guidance and tests inspected for context.

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 2m 59s | unavailable | gpt-5.6-terra, gpt-5.6-sol | cr 0.10.267
Field Value
Model gpt-5.6-terra, gpt-5.6-sol
Reviewers go:implementation-tests, automation:ci-release, documentation:docs, policies:conventions, architecture:solid-reviewer-agnostic
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 2m 59s wall · 6m 59s compute
Cost unavailable
Tokens 3.1M in / 21.8k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 514.3k 3.2k 452.6k unavailable unavailable 9s
go:implementation-tests gpt-5.6-terra 408.7k 4.0k 351.5k unavailable unavailable 1m 38s
automation:ci-release gpt-5.6-terra 83.6k 1.0k 56.1k unavailable unavailable 32s
documentation:docs gpt-5.6-terra 424.0k 3.1k 356.4k unavailable unavailable 1m 09s
policies:conventions gpt-5.6-terra 252.8k 2.1k 197.1k unavailable unavailable 1m 00s
architecture:solid-reviewer-agnostic gpt-5.6-sol 788.5k 5.1k 705.3k unavailable unavailable 2m 23s
orchestrator-rollup gpt-5.6-terra 584.9k 3.3k 520.4k unavailable unavailable 4s

@rianjs
rianjs merged commit 7c55594 into main Jul 24, 2026
9 checks passed
@rianjs
rianjs deleted the feat/23-list-playlist-items branch July 24, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: list ordered playlist items

2 participants