Skip to content

feat(requestrouter): carry requested seasons to request routers - #25

Merged
Quick104 merged 1 commit into
mainfrom
t3code/implement-requested-sdk-feature
Sep 27, 2026
Merged

Quick104 merged 1 commit into
mainfrom
t3code/implement-requested-sdk-feature

Conversation

@Quick104

@Quick104 Quick104 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Related issue: #24
Used by: Silo-Server/silo-server#1554 (host), Silo-Community/silo-plugins-requests-arr#11 (Sonarr).

Silo is adding season picking to series requests, but RequestDescriptor has no field for seasons, so a request router can only add the whole series. The host therefore refuses to request the missing seasons of a series already in the library whenever a download server takes series (missing_seasons_requestable reports false); otherwise the plugin would add the whole series again.

Approach

Both additions are appended; nothing is renumbered, and buf breaking against main reports nothing.

Where Addition
RequestDescriptor seasons = 10 (repeated int32). Empty means the whole series, as every request means today; 0 is Specials.
CapabilityDescriptor request_router = 12, a new RequestRouterDescriptor { bool supports_seasons = 1; }

A plugin declares season support in its manifest:

{ "type": "request_router.v1", "id": "arr", "request_router": { "supports_seasons": true } }

A declaring plugin acquires only the requested seasons. When the series already exists upstream, it adds those seasons to what it already tracks and leaves the others alone, and repeating a request converges. The host sends a request for only the missing seasons of a series it already has only to declaring plugins.

Why a typed descriptor. It follows the existing watch_sync_provider and network_access_provider blocks, so the host reads it from the manifest without launching the plugin. An absent descriptor means false, which is what every plugin built on an older SDK reports; that is how the host tells the two apart. The alternative, a free-form feature string list, would be the first of its kind in the manifest and unvalidated.

Validator. The descriptor is optional, so existing request routers stay valid; it is rejected on any other capability type.

Stored metadata. convert round-trips request_router, because the host persists capability metadata with CapabilityRecordsFromManifest and reads it back with DecodeCapability. Without this the host would never see the flag.

Docs: a "Request routers" section in README.md and a presence note in docs/compatibility.md.

After merge this should be tagged v0.18.0 (additive, minor).

Validation

  • New tests:
    • a real Fulfill call over gRPC (bufconn) delivers seasons [0, 4] to the plugin;
    • a manifest with supports_seasons loads, and one without the descriptor loads as declaring no support;
    • the validator rejects the descriptor on a non-router capability;
    • the descriptor survives CapabilityRecordsFromManifest → DecodeCapability, stored metadata without it decodes as absent, and unknown descriptor fields are discarded.
  • go test ./..., go vet ./..., and the hello-scheduled-task, hello-runtime-host, and hello-network-access builds pass.
  • protoc is not installed on the build host, so make proto stops at its protoc check. I ran the step it wraps, buf generate, with protoc-gen-go v1.36.11 and protoc-gen-go-grpc v1.6.1. On a clean tree it produced no diff; after the change it touched only common.pb.go and request_router.pb.go, and a second run changed nothing.
  • gofmt -l . lists only pkg/pluginsdk/runtime/scan_source_test.go, which predates this change and is not touched by it.

Risks

None identified for mixed versions. Manifest loading has ignored unknown fields since v0.4.0, and DecodeCapability reads only the keys it knows, so a host built on an older SDK installs a plugin that sets the flag, drops the flag, and keeps whole-series behaviour. A plugin built on an older SDK ignores seasons as an unknown field and adds the whole series, which is why the host gates season-only requests on the manifest flag.

Checklist

  • I read and can explain the complete diff.
  • This pull request addresses one concern.

AI Disclosure

  • Harness: Claude Code (in T3 Code)
  • Tool(s): Claude Code, Claude Code subagent (feature-dev:code-reviewer)
  • Model(s): claude-opus-5-5 (implementation), claude-sonnet-5 (review)
  • Involvement: Fully AI-generated; the maintainer set scope in Carry requested seasons to request router plugins #24. Human review of the diff is pending.
  • Adversarial review: An independent claude-sonnet-5 reviewer read the diff read-only. It checked field numbering and reuse, import cycles, generated descriptor tables, the convert round-trip, validation, the documented contract against the code, and whether the tests would fail without the change. It found no issues. It could not run commands, so the validation above was run separately.

🤖 Generated with Claude Code

Note

Add requested seasons to request-router proto and manifest contract

  • Adds a repeated seasons field (field 10) to pluginv1.RequestDescriptor; an empty list means the whole series and zero means Specials in request_router.proto
  • Adds pluginv1.RequestRouterDescriptor with a supports_seasons flag, attached to CapabilityDescriptor at field 12 in common.proto
  • Manifest loading and capability conversion now round-trip the request-router descriptor; manifest.go rejects the descriptor on non-request_router.v1 capabilities
  • Documents season semantics, backward compatibility, and host routing rules in README.md and compatibility.md
  • Behavioral Change: seasons is a repeated field with no presence semantics; older plugins ignore it and fulfil whole-series requests

Macroscope summarized cd4c295.

Series requests can name seasons, but RequestDescriptor had no field for
them, so a request router could only add the whole series. The host
therefore refuses to request the missing seasons of a series it already
has whenever a download server takes series.

Add, both appended without renumbering:
- RequestDescriptor.seasons = 10. Empty means the whole series; 0 is
  Specials.
- CapabilityDescriptor.request_router = 12, a RequestRouterDescriptor
  with supports_seasons = 1. Plugins that honour seasons set it; an
  absent descriptor, as in every plugin built on an older SDK, means
  false, so the host can tell the two apart from the manifest.

The manifest validator keeps the descriptor optional and rejects it on
other capability types. convert round-trips it so the host can read the
flag from stored capability metadata.

Closes #24

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 191705ab-4e60-411e-a3cd-5af314636702

📥 Commits

Reviewing files that changed from the base of the PR and between 488af9b and cd4c295.

⛔ Files ignored due to path filters (2)
  • pkg/pluginproto/silo/plugin/v1/common.pb.go is excluded by !**/*.pb.go
  • pkg/pluginproto/silo/plugin/v1/request_router.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • README.md
  • docs/compatibility.md
  • pkg/pluginsdk/convert/convert.go
  • pkg/pluginsdk/convert/request_router_test.go
  • pkg/pluginsdk/manifest/manifest.go
  • pkg/pluginsdk/manifest/request_router_test.go
  • pkg/pluginsdk/runtime/request_router_test.go
  • proto/silo/plugin/v1/common.proto
  • proto/silo/plugin/v1/request_router.proto

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The protocol adds season-scoped requests and a request-router capability descriptor. Manifest validation and capability conversion handle the descriptor. Documentation and tests describe and check the new fields and season delivery.

Changes

Season-aware requests

Layer / File(s) Summary
Season request and router descriptor contract
proto/silo/plugin/v1/*, README.md, docs/compatibility.md, pkg/pluginsdk/runtime/request_router_test.go
RequestDescriptor adds season scope, where an empty list means the whole series and season 0 means Specials. CapabilityDescriptor adds an optional request-router descriptor. The documentation describes the supports_seasons behavior, and a runtime test checks that requested seasons reach the router.
Manifest validation and capability conversion
pkg/pluginsdk/manifest/*, pkg/pluginsdk/convert/*
Manifest validation rejects request-router descriptors on capabilities of other types. Capability conversion serializes and decodes router metadata. Tests cover descriptor loading, metadata round-trip, absence, and unknown fields.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to cd4c2

No identified issue blocks merging the season-aware request router changes after normal checks.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to cd4c2

Season-scoped requests require coordinated host and plugin behavior. Older plugins treat them as whole-series requests, so a rollout mistake could acquire more seasons than requested. The reviewed SDK does not establish that this happens in production.

Retained concerns

  • Medium · security · inferred: Season-only fulfillment relies on the host selecting a router with validated season support and on that router honoring the declared scope. Those enforcement paths are not established by this SDK change; a season-only call to an older router would be interpreted as a whole-series request.
Security review details

Security Blast Radius

  • inferred — If a season-only call reaches a router that ignores its season list, the independently affected asset is the upstream series reachable through that call’s connection, potentially including unrequested seasons. The reviewed source does not establish wider tenant or service exposure.

Trust Boundaries and Controls

  • observed — Normal manifest loading calls validation, which rejects a request-router descriptor on a different capability type. By contrast, public capability-record decoding reconstructs any present request-router metadata without that type check; the provenance and downstream use of such records are not shown.

Resilience and Maintainability Implications

  • inferred — The SDK test establishes delivery of one call, not requester-identity preservation, authorization, duplicate suppression, partial-failure cleanup, or recovery across host and plugin. These controls cannot be assessed from the supplied production source.

Hardening Proposals

  • proposed — At host integration, verify that season-only routing uses a validated request-router declaration from the intended installation, preserves request authorization, and fails closed when support or provenance is absent.
  • proposed — At plugin integration, exercise repeated, concurrent, interrupted, and recovered requests against existing upstream series to verify that only requested seasons are added and other seasons remain unchanged.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 5 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: carrying requested seasons to request routers.
Description check ✅ Passed The description is directly related to the changeset and explains the problem, implementation, compatibility behavior, validation, and risks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 5 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T05:08:17.087832Z cd4c295 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Quick104
Quick104 merged commit 6c1da00 into main Sep 27, 2026
3 checks passed
@Quick104
Quick104 deleted the t3code/implement-requested-sdk-feature branch September 27, 2026 05:33
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.

1 participant