Skip to content

fix(playback): freeze v3 seek reanchor recipes - #548

Merged
Quick104 merged 5 commits into
Silo-Server:mainfrom
Rhainland:suspense/fix-protocol-v3-seek-reanchor
Aug 6, 2026
Merged

fix(playback): freeze v3 seek reanchor recipes#548
Quick104 merged 5 commits into
Silo-Server:mainfrom
Rhainland:suspense/fix-protocol-v3-seek-reanchor

Conversation

@Rhainland

@Rhainland Rhainland commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #547.

Protocol-v3 seek reanchors now reuse the exact durable playback recipe that produced the active route instead of replanning against mutable catalog, probe, capability, or subtitle inventory state.

Root cause

The seek-reanchor path treated a runtime correction as a fresh planning request. Inputs that were valid at playback start could drift before the seek, causing the server to select or construct a different route while the Apple client correctly required recipe continuity for the existing attempt.

Read-only production inspection also established that the reported request came from an iPhone (iPhone18,2) using an HLS remux route, rather than from tvOS. No production state was changed.

Changes

  • Persist a typed executable protocol-v3 recipe with each playback attempt.
  • Rebuild exact seek reanchors from the current durable plan and frozen operational inputs.
  • Freeze source codec and duration values passed into FFmpeg/HLS transport setup.
  • Bind downloaded subtitle artifacts to stable database row IDs in both generated URLs and serving.
  • Carry the same frozen subtitle recipe through artifact attachment and persistence, eliminating inventory-order TOCTOU races.
  • Classify subtitle-store lookup failures as retryable while keeping genuine identity drift non-retryable.
  • Share combined-index classification and stable downloaded-subtitle URL construction across all recipe paths.
  • Preserve atomic transport rollback and emit structured, non-sensitive drift diagnostics.
  • Return retryable seek_reanchor_recipe_unavailable for legacy active attempts whose migrated recipe is empty, allowing the client to start a fresh attempt safely.

Apple client cross-check

Compared against Silo-Server/silo-apple main at 570e781. The client retains the playback attempt ID, selected tracks, quality, and output-route generation during a seek reanchor and intentionally validates that the replacement remains the same recipe. A manual retry creates a new attempt ID. The server fix now matches that contract; no Apple-client change is required.

Verification

Focused regression tests:

go test ./internal/api/handlers -count=1
ok  github.com/Silo-Server/silo-server/internal/api/handlers  91.475s

go test ./internal/api/handlers -run '^TestHandleReplanPlaybackV3SeekReanchorPreservesFallbackRecipe$' -count=10
ok  github.com/Silo-Server/silo-server/internal/api/handlers  1.072s

go test ./internal/playback/planstore ./migrations -count=1
ok  github.com/Silo-Server/silo-server/internal/playback/planstore  0.521s
ok  github.com/Silo-Server/silo-server/migrations                    0.744s

Covered regressions:

  • TestBuildSubtitleURLsBindsDownloadedSubtitleIdentity
  • TestPrepareTransportV3UsesFrozenSourceMetadataAfterProbeDrift
  • TestHandleSubtitleUsesBoundDownloadedIdentityAfterInventoryReorder
  • TestAttachSubtitleArtifactV3UsesFrozenDownloadedIdentityWithoutOrdinalLookup
  • TestHandleReplanPlaybackV3SeekReanchorKeepsCurrentRecipeEligible
  • TestFrozenDownloadedSubtitleV3RejectsInventoryReordering
  • TestSubtitleArtifactStoreFailuresAreRetryable
  • TestExecutableRecipeV3SurvivesJSONRoundTrip

Additional checks:

golangci-lint run --new-from-merge-base="origin/main" ./...
0 issues.

go vet ./...
PASS

make build
PASS (Vite emitted its existing font-resolution and chunk-size warnings)

cd web && pnpm run lint
PASS

cd web && pnpm run format:check
All matched files use Prettier code style!

make verify-local-paths
PASS

make verify-settings-bindings
settings bindings are current

git diff --check
PASS

The repository-wide make lint still reports its documented pre-existing whole-tree backlog; the CI-equivalent merge-base-scoped lint above is clean. make test-go passes the previously failing handler package after rebasing onto current main; its only local failures are four unrelated macOS NVENC probe cases: TestResolveHWAccelWithFFmpegAutoPrefersNVENCOverIntel, TestResolveHWAccelWithFFmpegUsesNVIDIADeviceNodesWithoutDRM, TestFFmpegSupportsNVENCCachesByFFmpegPath, and TestFFmpegSupportsNVENCSmokeProbeUsesSafeFrameDimensions.

Review follow-up

  • Wrapped both downloaded-subtitle inventory and stable-ID repository failures with a shared sentinel and made only those transient failures retryable.
  • Added JSON persistence coverage that asserts meaningful zero-valued subtitle index fields remain present.
  • Added a test-database migration guard for frozen_recipe.
  • Added the JSONB check constraint as NOT VALID, then validate it in a later timestamped migration so the first Goose transaction releases its stronger lock before the scan.
  • Removed duplicated subtitle-segment and downloaded-URL construction logic.
  • Made the fallback/reanchor handler test independent of host FFmpeg capabilities and transport behavior by injecting a fixed transformation registry and fake FFmpeg, with explicit progressive-remux and HLS-remux route assertions.

Risk and compatibility

The migration adds a non-null JSONB recipe column defaulting to {} and a NOT VALID object constraint; a later committed migration performs validation under a separate lock phase. Existing in-flight attempts cannot reconstruct trustworthy frozen inputs and therefore receive the explicit retryable outcome instead of silently changing route. The downloaded-subtitle ID query parameter is additive; ordinal behavior remains available for older URLs.

AI Disclosure

  • Tool(s): Codex desktop, Claude
  • Model(s): gpt-5.6-sol; n/a (the Claude reviewer model ID was not available to the submitting agent)
  • Involvement: fully AI-generated
  • Adversarial review: Reviews found incomplete freezing of source transport metadata, two downloaded-subtitle identity races, non-retryable subtitle-store outages, duplicated identity-sensitive helpers, missing JSON persistence coverage, and migration/test-schema lock assumptions. The implementation and regression suite now cover each finding.

Summary by CodeRabbit

  • New Features

    • Downloaded subtitle links now use stable subtitle identifiers for more reliable playback.
    • Subtitle delivery supports direct ASS/VTT serving and conversion of other formats to VTT.
  • Bug Fixes

    • Improved playback seeking and replanning by preserving selected media sources, subtitle tracks, and playback settings.
    • Prevented subtitle mismatches when subtitle lists change.
    • Improved recovery when playback routes or source details change during playback.
  • Reliability

    • Added stronger validation for subtitle access and ownership, reducing invalid or cross-media subtitle requests.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bcb82ba7-8760-45d7-aa02-1896d76ebadd

📥 Commits

Reviewing files that changed from the base of the PR and between 083d37a and 9605014.

📒 Files selected for processing (10)
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/api/handlers/subtitle_search_test.go
  • internal/playback/executable_recipe_v3.go
  • internal/playback/executable_recipe_v3_test.go
  • internal/playback/plan_v3.go
  • internal/playback/protocol_v3_test.go
  • internal/playback/subtitle_policy_v3.go
  • migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql
  • migrations/sql/20260806120421_validate_playback_v3_frozen_recipe.sql
💤 Files with no reviewable changes (1)
  • migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/playback/executable_recipe_v3_test.go
  • internal/playback/executable_recipe_v3.go
  • internal/api/handlers/subtitle_search_test.go
  • internal/api/handlers/playback_v3.go

📝 Walkthrough

Walkthrough

Protocol V3 now persists executable playback recipes and reuses them for seek reanchors. Downloaded subtitles use stable IDs in URLs, artifact generation, and serving. Source metadata and subtitle inventory identities are validated against frozen recipes.

Changes

Playback V3 frozen execution

Layer / File(s) Summary
Executable recipe and subtitle contracts
internal/playback/executable_recipe_v3.go, internal/playback/plan_v3.go, internal/playback/subtitle_policy_v3.go, internal/playback/protocol_v3_test.go, internal/playback/*_test.go
ExecutableRecipeV3 stores playback, source, subtitle, and plan identity data. Planner results propagate downloaded subtitle IDs and frozen source metadata.
Recipe persistence
internal/playback/protocol_store_v3.go, internal/playback/planstore/*, migrations/sql/*
Attempt records persist frozen recipes in JSONB. Replan updates include the recipe.
Frozen playback and seek reanchors
internal/api/handlers/playback_v3.go, internal/api/handlers/playback_v3_test.go
Playback start freezes recipes. Seek reanchors reconstruct routes from durable recipes, validate identities, use frozen source metadata, and report bounded route changes.
Stable downloaded subtitle identity
internal/api/handlers/playback.go, internal/api/handlers/stream.go, internal/api/handlers/*_test.go
Downloaded subtitle URLs include downloaded_subtitle_id. Subtitle serving validates and resolves this ID before using ordinal fallback.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PlaybackHandler
  participant PlanStore
  participant SubtitleRepository
  participant Transport
  Client->>PlaybackHandler: start or seek playback
  PlaybackHandler->>PlanStore: save or load frozen recipe
  PlaybackHandler->>SubtitleRepository: validate downloaded subtitle identity
  PlaybackHandler->>Transport: prepare route from frozen metadata
  Transport-->>PlaybackHandler: return playback transport
  PlaybackHandler-->>Client: return playback response
Loading

Possibly related PRs

Suggested labels: v1

Suggested reviewers: quick104

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: freezing protocol-v3 seek reanchor recipes for playback.
Linked Issues check ✅ Passed The changes persist and reuse recipes, preserve atomic replacement, bind subtitles by stable ID, handle unavailable recipes, and report bounded drift fields for issue #547.
Out of Scope Changes check ✅ Passed The changes support issue #547 by implementing frozen recipes, stable subtitle identity, persistence, reanchor handling, and related regression tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Rhainland
Rhainland marked this pull request as ready for review August 6, 2026 10:03

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (5)
internal/api/handlers/playback_v3.go (2)

1440-1464: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Share the subtitle segment classification.

freezeExecutableRecipeV3 and validateFrozenSubtitleIdentityV3 (Lines 1480-1507) each re-derive externalCount, embeddedCount, and the external/embedded/downloaded boundaries. The comments on both functions state that the layout must mirror buildSubtitleURLs. Three copies of that layout can drift independently.

Extract one classifier that maps a combined index to a segment and a within-segment offset, then call it from both functions.

As per coding guidelines: "extract shared logic instead of duplicating it".

🤖 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 `@internal/api/handlers/playback_v3.go` around lines 1440 - 1464, Extract a
shared subtitle-index classifier for the external, embedded, and downloaded
segments, returning the segment type and within-segment offset. Replace the
boundary calculations in freezeExecutableRecipeV3 and
validateFrozenSubtitleIdentityV3 with this classifier, while preserving each
function’s existing segment-specific handling and validation.

Source: Coding guidelines


882-883: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the identity-bound downloaded subtitle URL builder.

These two lines duplicate buildSubtitleURLs in internal/api/handlers/playback.go at Lines 2117-2118. Both sites produce the URL that HandleSubtitle parses for downloaded_subtitle_id. If one site changes the parameter order, the separator, or the extension handling, the two producers diverge silently.

Add one helper and call it from both sites.

♻️ Proposed helper
// downloadedSubtitleStreamURL builds the identity-bound artifact URL for a
// downloaded subtitle. Both the inventory listing and frozen-recipe artifact
// attachment must emit the identical shape.
func downloadedSubtitleStreamURL(sessionID string, trackIndex int, codec string, fileID, downloadedID int) string {
	return subtitleStreamURL(sessionID, trackIndex, codec, fileID) +
		"&" + downloadedSubtitleIDParam + "=" + strconv.Itoa(downloadedID)
}
-		url := subtitleStreamURL(sessionID, selectedIndex, string(downloaded.Format), file.ID)
-		url += "&" + downloadedSubtitleIDParam + "=" + strconv.Itoa(downloaded.ID)
+		url := downloadedSubtitleStreamURL(sessionID, selectedIndex, string(downloaded.Format), file.ID, downloaded.ID)
🤖 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 `@internal/api/handlers/playback_v3.go` around lines 882 - 883, The downloaded
subtitle URL construction is duplicated and can diverge between playback
handlers. Add a shared downloadedSubtitleStreamURL helper using the existing
subtitleStreamURL and downloadedSubtitleIDParam symbols, then replace the inline
URL assembly in both buildSubtitleURLs and the playback_v3 call site with this
helper while preserving their existing arguments.
internal/playback/executable_recipe_v3_test.go (1)

5-37: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add JSON round-trip coverage for the durable recipe.

This test exercises the in-memory freeze and thaw only. The recipe is persisted as JSON, so the production path is marshal, store, load, unmarshal, then Valid. A wrong or missing struct tag passes this test and fails after a restart.

SubtitleTrackIndex and SubtitleTransportTrackIndex deliberately omit omitempty because -1 and 0 are meaningful. A JSON assertion pins that decision.

Add a marshal/unmarshal step, then re-run the existing field comparisons.

💚 Proposed addition
func TestExecutableRecipeV3SurvivesJSONRoundTrip(t *testing.T) {
	plan := &PlanV3{PlanID: "plan:frozen"}
	recipe := FreezeExecutableRecipeV3(PlannerResultV3{
		Plan: plan, PlayMethod: PlayRemux,
		SourceVideoCodec: "hevc", SourceDurationSeconds: 7_201,
		SubtitleTrackIndex: -1, SubtitleTransportTrackIndex: 0,
	})
	recipe.SubtitleSource = SubtitleSourceDownloadedV3
	recipe.DownloadedSubtitleID = 71

	encoded, err := json.Marshal(recipe)
	if err != nil {
		t.Fatalf("marshal: %v", err)
	}
	var decoded ExecutableRecipeV3
	if err := json.Unmarshal(encoded, &decoded); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if decoded != recipe {
		t.Fatalf("decoded = %#v, want %#v", decoded, recipe)
	}
	if !decoded.ValidFor(*plan) {
		t.Fatalf("decoded recipe no longer matches its plan: %#v", decoded)
	}
}
🤖 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 `@internal/playback/executable_recipe_v3_test.go` around lines 5 - 37, Add JSON
persistence coverage alongside
TestExecutableRecipeV3RoundTripPreservesOperationalFields by marshaling a frozen
ExecutableRecipeV3, unmarshaling it into a new value, and asserting equality
plus ValidFor against the original plan. Include meaningful SubtitleSource,
DownloadedSubtitleID, SubtitleTrackIndex=-1, and SubtitleTransportTrackIndex=0
values so JSON tags and non-omitempty behavior are verified; add any required
encoding/json import.
migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql (1)

2-5: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider adding the CHECK constraint as NOT VALID, then validating it separately.

The column addition is metadata-only, but the CHECK constraint in the same ALTER TABLE forces a validating scan under ACCESS EXCLUSIVE. playback_v3_attempts rows expire and are swept, so the table is normally small and the blocking window is short. If the table can grow under load, split the constraint to keep writes available.

♻️ Proposed split
 -- +goose Up
 ALTER TABLE playback_v3_attempts
-    ADD COLUMN frozen_recipe JSONB NOT NULL DEFAULT '{}'::jsonb,
-    ADD CONSTRAINT playback_v3_attempts_frozen_recipe_object
-        CHECK (jsonb_typeof(frozen_recipe) = 'object');
+    ADD COLUMN frozen_recipe JSONB NOT NULL DEFAULT '{}'::jsonb;
+
+ALTER TABLE playback_v3_attempts
+    ADD CONSTRAINT playback_v3_attempts_frozen_recipe_object
+        CHECK (jsonb_typeof(frozen_recipe) = 'object') NOT VALID;
+
+ALTER TABLE playback_v3_attempts
+    VALIDATE CONSTRAINT playback_v3_attempts_frozen_recipe_object;

As per coding guidelines: "Prioritize performance, reliability, predictable behavior under load and failures".

🤖 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 `@migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql` around lines
2 - 5, Split the migration so the frozen_recipe column is added separately from
the playback_v3_attempts_frozen_recipe_object constraint; create the CHECK
constraint as NOT VALID to avoid a blocking validation scan, then validate it in
a separate migration step or statement when appropriate.

Sources: Coding guidelines, Linters/SAST tools

internal/playback/planstore/postgres_test.go (1)

119-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a schema guard for the frozen_recipe column.

The fixture now persists FrozenRecipe, so every subtest needs migration 20260806083316. newPlanstoreFixture probes for playback_v3_attempts and for current_replan_request_id, but not for frozen_recipe. If a test database lacks the new migration, SaveAttempt fails with a column-not-found error instead of skipping. Extend the existing probe pattern.

♻️ Proposed guard in `newPlanstoreFixture`
var hasFrozenRecipe bool
if err := pool.QueryRow(ctx, `
	SELECT EXISTS (
		SELECT 1 FROM information_schema.columns
		WHERE table_name = 'playback_v3_attempts' AND column_name = 'frozen_recipe'
	)`).Scan(&hasFrozenRecipe); err != nil {
	t.Fatalf("check frozen_recipe column: %v", err)
}
if !hasFrozenRecipe {
	t.Skip("test database has not applied the playback v3 frozen recipe migration")
}

As per coding guidelines: "A Go test that cannot pass yet must use t.Skip with the reason in its own source".

🤖 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 `@internal/playback/planstore/postgres_test.go` around lines 119 - 122, The new
FrozenRecipe fixture requires a schema guard before tests execute. Extend
newPlanstoreFixture’s existing information_schema probes to check for the
frozen_recipe column on playback_v3_attempts, fail the test if the probe errors,
and call t.Skip with a clear migration-related reason when the column is absent.

Source: Coding guidelines

🤖 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.

Inline comments:
In `@internal/api/handlers/playback_v3.go`:
- Around line 533-537: Define a sentinel for subtitle-store failures, wrap the
repository error from ListDownloadedSubtitles in freezeExecutableRecipeV3 and
validateFrozenSubtitleIdentityV3, and classify matching errors as retryable.
Update internal/api/handlers/playback_v3.go:533-537, 1264-1284, and 1326-1331 so
store outages return retryable outcomes while genuine identity changes remain
non-retryable.

---

Nitpick comments:
In `@internal/api/handlers/playback_v3.go`:
- Around line 1440-1464: Extract a shared subtitle-index classifier for the
external, embedded, and downloaded segments, returning the segment type and
within-segment offset. Replace the boundary calculations in
freezeExecutableRecipeV3 and validateFrozenSubtitleIdentityV3 with this
classifier, while preserving each function’s existing segment-specific handling
and validation.
- Around line 882-883: The downloaded subtitle URL construction is duplicated
and can diverge between playback handlers. Add a shared
downloadedSubtitleStreamURL helper using the existing subtitleStreamURL and
downloadedSubtitleIDParam symbols, then replace the inline URL assembly in both
buildSubtitleURLs and the playback_v3 call site with this helper while
preserving their existing arguments.

In `@internal/playback/executable_recipe_v3_test.go`:
- Around line 5-37: Add JSON persistence coverage alongside
TestExecutableRecipeV3RoundTripPreservesOperationalFields by marshaling a frozen
ExecutableRecipeV3, unmarshaling it into a new value, and asserting equality
plus ValidFor against the original plan. Include meaningful SubtitleSource,
DownloadedSubtitleID, SubtitleTrackIndex=-1, and SubtitleTransportTrackIndex=0
values so JSON tags and non-omitempty behavior are verified; add any required
encoding/json import.

In `@internal/playback/planstore/postgres_test.go`:
- Around line 119-122: The new FrozenRecipe fixture requires a schema guard
before tests execute. Extend newPlanstoreFixture’s existing information_schema
probes to check for the frozen_recipe column on playback_v3_attempts, fail the
test if the probe errors, and call t.Skip with a clear migration-related reason
when the column is absent.

In `@migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql`:
- Around line 2-5: Split the migration so the frozen_recipe column is added
separately from the playback_v3_attempts_frozen_recipe_object constraint; create
the CHECK constraint as NOT VALID to avoid a blocking validation scan, then
validate it in a separate migration step or statement when appropriate.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85c89be7-e59f-4f12-a59e-841f9001f62f

📥 Commits

Reviewing files that changed from the base of the PR and between 31a26b2 and 707e43d.

📒 Files selected for processing (15)
  • internal/api/handlers/playback.go
  • internal/api/handlers/playback_subtitle_urls_test.go
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/api/handlers/stream.go
  • internal/api/handlers/stream_test.go
  • internal/api/handlers/subtitle_search_test.go
  • internal/playback/executable_recipe_v3.go
  • internal/playback/executable_recipe_v3_test.go
  • internal/playback/plan_v3.go
  • internal/playback/planstore/postgres.go
  • internal/playback/planstore/postgres_test.go
  • internal/playback/protocol_store_v3.go
  • internal/playback/protocol_v3_test.go
  • migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql

Comment thread internal/api/handlers/playback_v3.go
@Rhainland
Rhainland force-pushed the suspense/fix-protocol-v3-seek-reanchor branch from 707e43d to 3eb9a9d Compare August 6, 2026 11:44
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot added the v1 Silo v1 scope - auto-adds to the Silo v1 project label Aug 6, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql`:
- Around line 9-10: Remove the VALIDATE CONSTRAINT statement from this
migration, leaving playback_v3_attempts_frozen_recipe_object defined as NOT
VALID. Add a later timestamped Goose SQL migration containing the validation for
that constraint, preserving the existing constraint and table names.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88afb547-814c-4b30-8169-f7b4da19c208

📥 Commits

Reviewing files that changed from the base of the PR and between 31a26b2 and 3eb9a9d.

📒 Files selected for processing (15)
  • internal/api/handlers/playback.go
  • internal/api/handlers/playback_subtitle_urls_test.go
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/api/handlers/stream.go
  • internal/api/handlers/stream_test.go
  • internal/api/handlers/subtitle_search_test.go
  • internal/playback/executable_recipe_v3.go
  • internal/playback/executable_recipe_v3_test.go
  • internal/playback/plan_v3.go
  • internal/playback/planstore/postgres.go
  • internal/playback/planstore/postgres_test.go
  • internal/playback/protocol_store_v3.go
  • internal/playback/protocol_v3_test.go
  • migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql
🚧 Files skipped from review as they are similar to previous changes (11)
  • internal/playback/protocol_store_v3.go
  • internal/playback/protocol_v3_test.go
  • internal/playback/plan_v3.go
  • internal/api/handlers/subtitle_search_test.go
  • internal/api/handlers/stream_test.go
  • internal/api/handlers/stream.go
  • internal/api/handlers/playback.go
  • internal/playback/planstore/postgres.go
  • internal/api/handlers/playback_subtitle_urls_test.go
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go

Comment thread migrations/sql/20260806083316_add_playback_v3_frozen_recipe.sql Outdated
@Quick104
Quick104 merged commit fa1d7ba into Silo-Server:main Aug 6, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Silo v1 Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v1 Silo v1 scope - auto-adds to the Silo v1 project

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[bug] Protocol-v3 seek reanchor replans against mutable route inputs

2 participants