Skip to content

feat: manage saved tracks#18

Merged
rianjs merged 1 commit into
mainfrom
feat/13-saved-library
Jul 23, 2026
Merged

feat: manage saved tracks#18
rianjs merged 1 commit into
mainfrom
feat/13-saved-library

Conversation

@rianjs

@rianjs rianjs commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add library tracks list/check/add/remove with token-efficient text output and stdout/stderr separation
  • request and persist the exact saved-library OAuth scopes, with pre-resource reauthorization hints for older credentials
  • use Spotify's current generic library URI endpoints in ordered 40-item chunks while validating and deduplicating complete batches before mutation
  • preserve clean composition-root injection and narrow client/session/output responsibilities

Provider contract

The deprecated track-specific check/save/remove endpoints are intentionally not used.

Verification

  • make check
  • make snapshot and package render verification
  • exact chunk-boundary and result-association tests for 40/41/80/81 inputs
  • independent Terra TDD review; all three major and one minor coverage findings addressed
  • reversible isolated encrypted-file live test: exact scopes, list shape, continuation resume, ID-only output, add/check/remove, and original membership restored
  • final architecture/YAGNI pass: no new dependency or generic library abstraction

Closes #13

@rianjs

rianjs commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Independent Terra TDD review

The one-shot GPT-5.6 Terra high-effort review found no blockers and four coverage gaps before commit:

  • Major: cross-chunk check-result association used uniform booleans;
  • Major: mutation tests did not assert the provider received only one normalized URI after mixed-form deduplication;
  • Major: live smoke did not verify membership had returned to the recorded original state;
  • Minor: the init commit path did not decode the stored envelope and assert the exact sorted expanded scopes.

All findings are addressed on exact head b5e3d72d4102a45be95a142797b32085a1333812:

  • 40/41/80/81 check fixtures now derive mixed results from each URI and assert the complete ordered result;
  • add/remove tests pass raw-ID, URI, and URL duplicates and assert one normalized provider URI;
  • live smoke exercises both original membership states and refuses to disable restoration until a final membership check equals the recorded state;
  • the init commit-path test decodes the stored credential and asserts user-library-modify,user-library-read,user-read-private in canonical order.

The reviewer explicitly accepted shared requestJSON coverage for 401/403, retries, cancellation, and body bounds rather than requesting redundant per-library tests.

@rianjs

rianjs commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Architect PR review — approved

Reviewed exact head b5e3d72d4102a45be95a142797b32085a1333812 against issue #13, cli-common conventions, and the current Spotify library contracts.

  • The composition root constructs the concrete session and injects a consumer-owned librarycmd.Session; session remains a thin delegation boundary.
  • librarycmd owns command validation, complete-batch normalization/deduplication, scope timing, output routing, and orchestration.
  • client owns fixed provider paths, 40-item request sequencing, typed response validation, and the existing bounded retry/cancellation transport seam; provider URLs are never followed.
  • output owns stable saved-track and membership rendering, including ADDED_AT, breadcrumb-rich track fields, and pure --id behavior.
  • Scope requests are the exact sorted set; older credentials fail before the resource request with one actionable overwrite hint.
  • Multi-chunk partial failure is reported honestly without speculative transaction machinery; live smoke verifies and restores original membership.
  • No raw HTTP surface, generic library-item framework, graph model, cache, worker pool, rollback abstraction, or new dependency was introduced.

Verification passed: authoritative make check, cross-platform make snapshot, package rendering, hermetic smoke in both original membership states, independent Terra TDD review with all findings closed, and an isolated encrypted-file live add/check/list/remove/continuation round trip with original state restored.

No architectural blocker, major, minor, or nit remains.

@rianjs-bot rianjs-bot Bot left a comment

Copy link
Copy Markdown

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: b5e3d72d4102
Profile: codex-rianjs-bot - Posting as: rianjs-bot[bot]

Summary

Reviewer Findings
go:implementation-tests 0
policies:conventions 0
structure:repo-health 0
documentation:docs 0
automation:ci-release 1
automation:ci-release (1 finding)

Major - scripts/live-smoke.sh:26

The cleanup path suppresses every failure while restoring the live account's original saved-track membership. If the smoke fails after remove/add (or is interrupted) and the compensating API call fails, the script exits without reporting that the dedicated account was left modified, despite this workflow claiming reversible state. Emit a clear stderr warning and preserve a failure status when restoration fails (and apply the same handling to the remove branch), rather than || true.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/auth/authorizer.go, internal/auth/authorizer_test.go, internal/auth/token_source_test.go, internal/client/client.go, internal/client/library_test.go, internal/cmd/initcmd/init_test.go, internal/cmd/librarycmd/library.go, internal/cmd/librarycmd/library_test.go, internal/cmd/root/root.go, internal/cmd/root/root_test.go, internal/output/track.go, internal/output/track_test.go, internal/session/session.go unavailable Focused Go implementation and behavioral-test review only. Full package suite execution is constrained by sandbox denial of loopback listeners used by existing httptest tests; focused librarycmd, output, session, and initcmd packages passed.
policies:conventions complete_broad README.md, internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable No local cli-common or shared .github convenience documentation was available, so this review used the repo-local development guide and behavioral specification only.; make check reached golangci-lint but could not complete because the sandboxed environment cannot preprocess the macOS keychain cgo dependency; this appears environmental rather than a changed-file convention failure.
structure:repo-health complete_broad internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go unavailable Focused structural review limited to the six assigned files and their directly related tests/docs. Package tests could not complete in this sandbox because the Go toolchain's clang invocation mishandles the workspace path containing spaces; diff and source inspection completed.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Review limited to the assigned documentation changes and their local implementation context; external linked documentation was not checked.
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 behavior.

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


Completed in 1m 59s | unavailable | gpt-5.6-terra | cr 0.10.267
Field Value
Model gpt-5.6-terra
Reviewers go:implementation-tests, policies:conventions, structure:repo-health, documentation:docs, automation:ci-release
Engine codex_cli · gpt-5.6-terra
Reviewed by cr · rianjs-bot[bot]
Duration 1m 59s wall · 5m 48s compute
Cost unavailable
Tokens 1.3M in / 13.6k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 17.2k 500 11.0k unavailable unavailable 11s
go:implementation-tests gpt-5.6-terra 419.6k 3.9k 358.7k unavailable unavailable 1m 35s
policies:conventions gpt-5.6-terra 339.4k 2.5k 283.6k unavailable unavailable 1m 11s
structure:repo-health gpt-5.6-terra 287.3k 2.8k 226.0k unavailable unavailable 1m 27s
documentation:docs gpt-5.6-terra 157.0k 1.8k 123.6k unavailable unavailable 41s
automation:ci-release gpt-5.6-terra 83.1k 1.6k 58.1k unavailable unavailable 36s
orchestrator-rollup gpt-5.6-terra 37.0k 594 27.6k unavailable unavailable 4s

Comment thread scripts/live-smoke.sh Outdated
@rianjs
rianjs force-pushed the feat/13-saved-library branch from b5e3d72 to 7bdfc2e Compare July 23, 2026 14:21

@rianjs-bot rianjs-bot Bot left a comment

Copy link
Copy Markdown

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: 7bdfc2e9cacd
Profile: codex-rianjs-bot - Posting as: rianjs-bot[bot]

Summary

Reviewer Findings
go:implementation-tests 0
policies:conventions 0
structure:repo-health 0
automation:ci-release 0
documentation:docs 0

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/auth/authorizer.go, internal/auth/authorizer_test.go, internal/auth/token_source_test.go, internal/client/client.go, internal/client/library_test.go, internal/cmd/initcmd/init_test.go, internal/cmd/librarycmd/library.go, internal/cmd/librarycmd/library_test.go, internal/cmd/root/root.go, internal/cmd/root/root_test.go, internal/output/track.go, internal/output/track_test.go, internal/session/session.go unavailable Sandbox prevents loopback listeners, so auth/client/root tests using httptest could not run; librarycmd, output, session, and initcmd packages passed with CGO disabled.
policies:conventions complete_broad README.md, internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Shared cli-common and .github convenience copies were not present locally; review is limited to repository-local guidance and the changed files.; make check reached golangci-lint but could not type-check a macOS keychain dependency because the environment's cgo/Xcode preprocessing failed.
structure:repo-health complete_broad internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go unavailable Review scoped to assigned changed files and their directly relevant tests/docs. Targeted Go tests could not complete in this sandbox because loopback binding is disallowed and cgo/clang cannot resolve the workspace path.
automation:ci-release complete_broad scripts/live-smoke-test.sh, scripts/live-smoke.sh unavailable Review limited to the assigned live-smoke scripts; live provider execution was not performed because it requires explicit credentials and dedicated-account opt-ins.
documentation:docs complete_broad README.md, spotify-cli-behavior.md unavailable Review limited to the two assigned documentation files and their local implementation context; external linked documentation was not independently fetched.

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


Completed in 2m 03s | unavailable | gpt-5.6-terra | cr 0.10.267
Field Value
Model gpt-5.6-terra
Reviewers go:implementation-tests, policies:conventions, structure:repo-health, automation:ci-release, documentation:docs
Engine codex_cli · gpt-5.6-terra
Reviewed by cr · rianjs-bot[bot]
Duration 2m 03s wall · 6m 12s compute
Cost unavailable
Tokens 1.5M in / 15.1k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 61.3k 1.0k 46.8k unavailable unavailable 10s
go:implementation-tests gpt-5.6-terra 464.0k 3.6k 400.9k unavailable unavailable 1m 34s
policies:conventions gpt-5.6-terra 264.5k 2.8k 223.7k unavailable unavailable 1m 31s
structure:repo-health gpt-5.6-terra 341.2k 2.8k 287.2k unavailable unavailable 1m 16s
automation:ci-release gpt-5.6-terra 132.0k 2.0k 104.7k unavailable unavailable 48s
documentation:docs gpt-5.6-terra 130.1k 1.7k 94.7k unavailable unavailable 46s
orchestrator-rollup gpt-5.6-terra 89.1k 1.1k 70.7k unavailable unavailable 3s

@rianjs

rianjs commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Architect post-review pass — approved

Reviewed exact head 7bdfc2e9cacdae2686702a088e8c56782d83ac7e after the automated-review fix.

The cleanup change is narrowly scoped operations glue: both restoration directions now report failure on stderr and force a non-zero exit, while the hermetic smoke test proves each failure path. It introduces no new abstraction or dependency and does not alter the command composition roots, package boundaries, provider contracts, or user-facing output model approved in the prior architecture pass.

Authoritative make check and all nine CI checks pass. No architectural drift, blocker, major, minor, or nit remains.

@rianjs
rianjs marked this pull request as ready for review July 23, 2026 14:25

@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: 7bdfc2e9cacd
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
architecture:solid-reviewer-agnostic 1
policies:conventions 0
structure:harness-engineering 0
architecture:solid-reviewer-agnostic (1 finding)

Major - internal/auth/authorizer.go:35

U-D1: requestedScopes is a package-level slice, and oauthConfig hands its backing array directly to every oauth2.Config. Mutating one returned config's Scopes can therefore change later authorization attempts and the fallback scopes persisted by Authorize, turning the exact OAuth grant contract into ambient mutable state. Construct a fresh scope slice for each authorization, or keep an array and explicitly copy it into both consumers.

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/auth/authorizer.go, internal/auth/authorizer_test.go, internal/auth/token_source_test.go, internal/client/client.go, internal/client/library_test.go, internal/cmd/initcmd/init_test.go, internal/cmd/librarycmd/library.go, internal/cmd/librarycmd/library_test.go, internal/cmd/root/root.go, internal/cmd/root/root_test.go, internal/output/track.go, internal/output/track_test.go, internal/session/session.go unavailable Full Go test execution is blocked by the sandbox's loopback-bind restriction and a clang path issue; source and focused behavioral tests were inspected.
architecture:solid-reviewer-agnostic complete_broad internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go unavailable Formatting and linting passed with sandbox-local caches and CGO disabled. The full test run was blocked by the sandbox prohibiting loopback listeners; targeted tests for the changed client, command, root wiring, output, and session behavior passed.; Review was limited to the assigned changed files, their surrounding code and tests, and repository-local development and behavioral guidance.; The canonical cli-common shared standards were not present in the checkout and could not be fetched.
policies:conventions complete_broad README.md, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Targeted Go tests could not build in this sandbox because the RTK proxy/clang invocation truncated the workspace path.; The referenced shared cli-common/.github convention sources were not present locally; review relied on the repository-local development guide and behavioral specification.
structure:harness-engineering complete_broad README.md, internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/output/track.go, internal/session/session.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Review focused on assigned changed files and relevant adjacent tests/wiring. Full checks were environment-blocked by sandboxed macOS/cgo keychain and loopback-listener restrictions; the hermetic live-smoke script passed.

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, architecture:solid-reviewer-agnostic, policies:conventions, structure:harness-engineering
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 4m 01s wall · 7m 32s compute
Cost unavailable
Tokens 2.3M in / 17.5k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 19.1k 649 11.0k unavailable unavailable 15s
go:implementation-tests gpt-5.6-terra 407.0k 2.9k 343.3k unavailable unavailable 1m 17s
architecture:solid-reviewer-agnostic gpt-5.6-sol 1.1M 8.2k 1.0M unavailable unavailable 3m 33s
policies:conventions gpt-5.6-terra 208.5k 1.8k 167.7k unavailable unavailable 53s
structure:harness-engineering gpt-5.6-terra 475.0k 3.2k 416.8k unavailable unavailable 1m 27s
orchestrator-rollup gpt-5.6-terra 41.0k 736 29.7k unavailable unavailable 4s

Comment thread internal/auth/authorizer.go Outdated
@rianjs
rianjs force-pushed the feat/13-saved-library branch from 7bdfc2e to 49ae681 Compare July 23, 2026 14:31
@rianjs

rianjs commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Architect final recheck — approved

Reviewed exact head 49ae681d7540b5b81a09bef47a869830dec54e7e after the approved-member scope-ownership finding.

The package-level mutable scope slice is gone. Each OAuth config now receives a fresh exact scope slice, and the token envelope fallback consumes that same attempt-specific config. The regression test proves mutation cannot cross authorization attempts. This tightens ownership without changing the composition root, interfaces, provider behavior, dependencies, or command surface.

Authoritative make check passes. No architectural blocker, major, minor, or nit remains.

@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: 49ae681d7540
Profile: codex-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
architecture:solid-reviewer-agnostic 0
policies:conventions 0
structure:harness-engineering 0

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
go:implementation-tests complete_broad internal/auth/authorizer.go, internal/auth/authorizer_test.go, internal/auth/token_source_test.go, internal/client/client.go, internal/client/library_test.go, internal/cmd/initcmd/init_test.go, internal/cmd/librarycmd/library.go, internal/cmd/librarycmd/library_test.go, internal/cmd/root/root.go, internal/cmd/root/root_test.go, internal/output/track.go, internal/output/track_test.go, internal/session/session.go unavailable Review scoped to the assigned Go implementation and tests.
architecture:solid-reviewer-agnostic complete_broad internal/auth/authorizer.go, internal/auth/authorizer_test.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, internal/output/track.go, internal/session/session.go unavailable Full make check could not complete because the artifact checkout lost its empty .git/refs directory and the sandbox blocks local test listeners/configured temporary paths. Focused saved-library tests and linting passed under sandbox-compatible settings.; Review scope was limited to the seven assigned changed files, with surrounding repository guidance and relevant tests inspected for context.
policies:conventions complete_broad README.md, internal/cmd/librarycmd/library.go, internal/cmd/root/root.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Shared cli-common and .github convenience copies were not present in the review checkout; review used the repository-local development guide and behavioral specification. make test-live-smoke could not run because the sandbox-provided TMPDIR did not exist.
structure:harness-engineering complete_broad README.md, internal/auth/authorizer.go, internal/client/client.go, internal/cmd/librarycmd/library.go, internal/output/track.go, internal/session/session.go, scripts/live-smoke-test.sh, scripts/live-smoke.sh, spotify-cli-behavior.md unavailable Review was limited to the assigned production/docs/smoke files and their adjacent tests; sandbox restrictions prevented running the targeted Go tests and accessing the base Git object directly.

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


Completed in 6m 08s | 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, architecture:solid-reviewer-agnostic, policies:conventions, structure:harness-engineering
Engine codex_cli · gpt-5.6-terra, gpt-5.6-sol
Reviewed by cr · monit-reviewer
Duration 6m 08s wall · 9m 12s compute
Cost unavailable
Tokens 3.7M in / 20.4k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection gpt-5.6-terra 70.5k 1.2k 51.5k unavailable unavailable 11s
go:implementation-tests gpt-5.6-terra 305.3k 2.7k 249.9k unavailable unavailable 1m 00s
architecture:solid-reviewer-agnostic gpt-5.6-sol 2.4M 9.5k 2.3M unavailable unavailable 5m 39s
policies:conventions gpt-5.6-terra 267.4k 2.1k 220.4k unavailable unavailable 50s
structure:harness-engineering gpt-5.6-terra 523.6k 3.7k 460.0k unavailable unavailable 1m 26s
orchestrator-rollup gpt-5.6-terra 102.9k 1.2k 80.4k unavailable unavailable 3s

@rianjs
rianjs merged commit a8a68b6 into main Jul 23, 2026
9 checks passed
@rianjs
rianjs deleted the feat/13-saved-library branch July 23, 2026 14:40
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: manage saved tracks

2 participants