Closed
opened 2026-07-11 23:21:16 +02:00 by timothy
·
3 comments
No Branch/Tag Specified
main
renovate/lucene.net
renovate/dotnet-monorepo
renovate/sqlitepclraw.bundle_e_sqlite3-3.x
renovate/meziantou.analyzer-3.x
renovate/cliwrap-3.x
913-scripted-playout-401
release/v26.15.0-notes
issue-806-guard-populations
scratch/767b-poisoned
scratch/767b-control
release/v26.14.0-notes
release/v26.14.0
docs/510-skill-logo-bug-policy
fix/510-watermark-resolution-policy
fix/629-verdict-classifier-falseopens
fix/609-decisions-edit-token-scope
issue-135-clear-to-none
release/v26.12.0-notes
fix/409b-lastscan-api-parity
fix/401-updatechannel-mirror-422
fix/327-playlist-rename-validation
fix/410-scancancel-log-level
fix/409-447-librariesscreen-neverscanned
fix/338-zap-exit-code
fix/367-plex-budget-message
fix/310-debom-legacy-cs
ci/604-lane-rebalance
feat/388-design-mirror
feat/247-test-ownership
feat/247-primary-action
feat/357-player-owned-playback
feat/357-jellyfin-plugin-poc
fix/289-mcp-hardening
issue58-mcp
feat/244-channels-extract
ci/auto-bump-prod-compose
feat/multi-rerun-collections-api
feat/collections-api
feat/quick-wins
feat/185-docs-part2
feat/140-collections-screen
feat/146-channel-edit
feat/147-classic-ui-link
issue22-renovate-dashboard
feat/91-cutover
feat/63-composite-create
feat/65-library-browse
feat/85-epg
feat/86-schedule-editor
feat/109-dashboard-data
feat/99-session-tracking
fix/dockerfile-node-tag
feat/59-spa-foundation
docs/59-ui-redesign-brief
feat/102-json-guide
feat/111-schedule-durations
feat/104-artwork-upload
feat/103-media-sources-api
feat/playouts-read-api
feat/108-health-api
feat/105-picker-list-endpoints
issue-97-channel-state-api
issue42-jellyfin-musicvideos
issue46-rest-api-error-contract
dependabot/nuget/ErsatzTV.FFmpeg.Tests/multi-d307a2e06f
qsv-improvements
hdr-vulkan-cuda-test
v26.16.0
v26.15.0
v26.14.0
v26.13.0
v26.12.0
v26.11.0
v26.10.0
v26.9.0
v26.8.0
v26.7.0
blazor-final
v26.6.0
v26.5.0
v26.4.0
v26.3.1
v26.3.0
v26.2.0
v26.1.1
v26.1.0
v25.9.0
v25.8.0
v25.7.1
v25.7.0
v25.6.0
v25.5.0
v25.4.0
v25.3.1
v25.3.0
v25.2.0
v25.1.0
v0.8.8-beta
v0.8.7-beta
v0.8.6-beta
v0.8.5-beta
v0.8.4-beta
v0.8.3-beta
v0.8.2-beta
v0.8.1-beta
v0.8.0-beta
v0.7.9-beta
v0.7.8-beta
v0.7.7-beta
v0.7.6-beta
v0.7.5-beta
v0.7.4-beta
v0.7.3-beta
v0.7.2-beta
v0.7.1-beta
v0.7.0-beta
v0.6.9-beta
v0.6.8-beta
v0.6.7-beta
v0.6.6-beta
v0.6.5-beta
v0.6.4-beta
v0.6.3-beta
v0.6.2-beta
v0.6.1-beta
v0.6.0-beta
v0.5.8-beta
v0.5.7-beta
v0.5.6-beta
v0.5.5-beta
v0.5.4-beta
v0.5.3-beta
v0.5.2-beta
v0.5.1-beta
v0.5.0-beta
v0.4.5-alpha
v0.4.4-alpha
v0.4.3-alpha
v0.4.2-alpha
v0.4.1-alpha
v0.4.0-alpha
v0.3.8-alpha
v0.3.7-alpha
develop
v0.3.6-alpha
v0.3.5-alpha
v0.3.4-alpha
v0.3.3-alpha
v0.3.2-alpha
v0.3.1-alpha
v0.3.0-alpha
v0.2.5-alpha
v0.2.4-alpha
v0.2.3-alpha
v0.2.2-alpha
v0.2.1-alpha
v0.2.0-alpha
v0.1.5-alpha
v0.1.4-alpha
v0.1.3-alpha
v0.1.2-alpha
v0.1.1-alpha
v0.1.0-alpha
v0.0.62-alpha
v0.0.61-alpha
v0.0.60-alpha
v0.0.59-alpha
v0.0.58-alpha
v0.0.57-alpha
v0.0.56-alpha
v0.0.55-alpha
v0.0.54-alpha
v0.0.53-alpha
v0.0.52-alpha
v0.0.51-alpha
v0.0.50-alpha
v0.0.49-prealpha
v0.0.48-prealpha
v0.0.47-prealpha
v0.0.46-prealpha
v0.0.45-prealpha
v0.0.44-prealpha
v0.0.43-prealpha
v0.0.42-prealpha
v0.0.41-prealpha
v0.0.40-prealpha
v0.0.39-prealpha
v0.0.38-prealpha
v0.0.37-prealpha
v0.0.36-prealpha
v0.0.35-prealpha
v0.0.34-prealpha
v0.0.33-prealpha
v0.0.32-prealpha
v0.0.31-prealpha
v0.0.30-prealpha
v0.0.29-prealpha
v0.0.28-prealpha
v0.0.27-prealpha
v0.0.26-prealpha
v0.0.25-prealpha
v0.0.24-prealpha
v0.0.23-prealpha
v0.0.22-prealpha
v0.0.21-prealpha
v0.0.20-prealpha
v0.0.19-prealpha
v0.0.18-prealpha
v0.0.17-prealpha
v0.0.16-prealpha
v0.0.15-prealpha
v0.0.14-prealpha
v0.0.13-prealpha
v0.0.12-prealpha
v0.0.11-prealpha
v0.0.10-prealpha
v0.0.9-prealpha
v0.0.8-prealpha
v0.0.7-prealpha
v0.0.6-prealpha
v0.0.5-prealpha
v0.0.4-prealpha
v0.0.3-prealpha
v0.0.2-prealpha
v0.0.1-prealpha
Labels
Clear labels
ad-hoc
api
bug
ci-cd
content
dependencies
enhancement
frontend
in-progress
jellyfin
parked
priority: high
priority: low
priority: medium
review
security
One-off / ad-hoc work not tracked by a dedicated issue
REST API / HTTP endpoints
Something isn't working
Build, test, deploy pipeline
Channel content / schedules / playlists
Dependency updates (Renovate)
New feature or improvement
ChicoryTV React SPA frontend
Claimed by an active session — do not pick up
Jellyfin tuner / IPTV integration
Excluded from automatic queue pickup; work only when explicitly selected
Adversarial review finding
Security / vulnerability fix
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: timothy/ersatztv#286
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
From the #197 cold API contract review. Phase 2 contract-freeze gate — must decide/land before declaring v1 frozen (can't be added compatibly after).
Finding (BLOCKER):
/api/*is completely unversioned (no path/header/media-type;info.versionis cosmetic). The first breaking change silently breaks the SPA and the MCP client with no negotiation path.Fix (Phase-2 #7): mechanical route sweep
/api/...→/api/v1/...+ an internal in-pipeline rewrite/api/{nonversioned}→/api/v1/...(rewrite, not redirect — bodies/auth survive; MCP/curl safe) carryingDeprecation: true+Sunsetheaders; regenv1.json+v1.d.ts+ sweep the SPA literal paths; MCP regenerates. Then/api/v1is additive-only (append todecisions.md). Owner decision needed: sunset window for the legacy rewrite (recommend 2 releases). Full rationale + rejected alternatives (Asp.Versioning, header/media-type) in the #197 roadmap. Refs #197.Done-when
/api/*routes swept to/api/v1(controllers, Location headers, scanner callback), uniform across auth/scan/scripted;ApiVersionRewriteMiddlewarerewrites legacy/api/*→/api/v1/*in-pipeline withDeprecation/SunsetheadersApiRouteVersioningTests(^/api/v\d+/over the wholeControllers.Apisurface).NETbuild + full test suite green; SPA typecheck + vitest + check:api + eslint greenv1.jsonall/api/v1/…,endpoint-index.md,v1.d.ts)decisions.mdversioning entry,api-conventions.md§1/§9, route/parity docs)/api/v1/*end-to-end AND a legacy/api/*call is rewritten (200 +Deprecationheader)Review-verdict: MERGEABLE @ 682dceec)682dceec)Bundling — own session, land LAST. The
/api/v1route sweep rewrites every controller route and regenerates all artifacts, so it conflicts with controller-attribute changes (Bundle A: #280/#281/#282/#284/#285) and spec changes (Bundle C: #287/#288). Sequence it after Bundle A + Bundle C merge, then regenerate once. Phase-2 freeze gate. See #197 roadmap.timothy referenced this issue2026-07-12 11:52:46 +02:00
claiming — session started 2026-07-12 (night). This is the #197 frontier BLOCKER; PR #76 (MCP) just closed as superseded so the fresh MCP builds against the frozen
/api/v1this lands.Owner decision (sunset window): going with the issue's recommendation — 2 releases for the legacy
/api/*→/api/v1/*in-pipeline rewrite before the compat shim is removed (emitDeprecation: true+Sunsetheaders meanwhile). Will confirm approach details after reading the #197 roadmap.Closed — merged to main (PR #326,
682dceec, fast-forward)What was done. Versioned the entire REST API to
/api/v1. The whole/apisurface — machine API, browser-session auth (/api/v1/auth/*), scanner loopback (/api/v1/scan/*), scripted-build (/api/v1/scripted/*) — now lives under/api/v1, uniformly, so there's no unversioned corner. Legacy unversioned/api/*callers are handled byApiVersionRewriteMiddleware, an in-pipeline rewrite (not redirect) that preserves method/body/auth/query and advertisesDeprecation: true+ aLink: rel="deprecation"(+SunsetwhenApi:LegacyRoutesSunsetis set).Root cause. n/a — not a bug; a pre-ossification contract gate.
/apiwas unversioned from the original REST build, which was acceptable while the SPA + server shipped lockstep, but becomes a one-way door once external/MCP consumers depend on the contract.Owner decisions taken. Sunset window = 2 releases (the compat shim removal is a tracked Phase-3 follow-up, not this PR). Uniform versioning (incl. internal/auth surfaces) rather than a carve-out, so the rewrite needs no exclusion list. Route-convention standardization requested by the owner mid-session: leading-slash absolute route per method (class-
[Route]retained only for the two shared-{id}-prefix controllers).Files changed (218). 251 controller route attributes + ~24
Locationheaders +CallLibraryScannerHandler.GetBaseUrl+ theStartuprequest-log literal; newErsatzTV/Middleware/ApiVersionRewriteMiddleware.cs+ registration; newErsatzTV.Tests/Controllers/ApiRouteVersioningTests.cs(enforces^/api/v\d+/+ aKnownNonApiRoutesallowlist); regeneratedv1.json(160 paths) /endpoint-index.md/v1.d.ts; 945 SPA request literals + test mocks; docs (decisions.md,api-conventions.md§1/§9,rest-api/spa-conventions/blazor-route-parity/e2e-local/domain-model).Review. Cold fork (MERGEABLE-WITH-NITS) + independent Codex (BLOCKED → 2 findings: test silent-skip of non-
/apiroutes; deprecationLinkunderETV_BASE_URL) → both fixed in682dceec→ fix-commit re-review MERGEABLE. Live-E2E confirmed versioned routes, the legacy rewrite header,/api/v2passthrough, no auth bypass, and SPA boot with 0 console errors./api/v1is now frozen: additive-only. Renames/removals/retypes require/api/v2. The fresh read+write MCP (#58) builds against this frozen surface.Deferred / follow-ups. Phase-3: sunset + remove the legacy-rewrite shim after 2 releases. No new issues filed. Cosmetic review nits deliberately not filed (the
Linkrelis a draft relation; harmless).Docs updated: all in-PR (see files above).