Skip to content

fix(playback): advertise native Media3 HLS - #262

Merged
Quick104 merged 1 commit into
Silo-Server:mainfrom
blurbery:contrib/native-hls-playback-capability
Aug 30, 2026
Merged

fix(playback): advertise native Media3 HLS#262
Quick104 merged 1 commit into
Silo-Server:mainfrom
blurbery:contrib/native-hls-playback-capability

Conversation

@blurbery

@blurbery blurbery commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What happened

Two users on the current Android build could not reliably start HEVC/Dolby Vision playback. The matching server logs showed the native Media3 HLS path being treated like a web MediaSource/hls.js client, so the server could not choose the correct HLS sample-entry recipe for Android.

The server-side compatibility fallback already makes unchanged build 15 clients work, but the durable fix is for Android to say explicitly that its local HLS delivery is handled by native Media3.

What I changed

  • Add the native_hls_playback_v1 capability name to the shared playback contract.
  • Advertise it on local Media3 HLS for both phone and TV.
  • Do not advertise it for Original HTTP, progressive playback, or Cast.
  • Cover both form factors and all of those delivery boundaries with a Robolectric test.

This is intentionally delivery-scoped. It gives the server enough evidence to choose hvc1 for regular HEVC/DV7-to-HDR10 output and dvh1 for preserved DV5/DV8 without changing unrelated playback routes.

Live validation

I deployed the equivalent server behavior as build 122 and tested it with the two affected users on the unchanged Android build 15:

  • User 1's first Auto DV8 2160p route was ready in 102 ms. The manifest, init data, subtitles, and media requests were all HTTP 200, and progress reached 204 seconds.
  • User 1 then ran seven DV7 2160p sessions at roughly 68–89 Mbps, including seeks and resumes. Those sessions delivered 174/174 media segments successfully with no playback 4xx/5xx responses.
  • User 2 ran five HLS remux sessions plus a separate direct-play session of about ten minutes. The HLS runs delivered 106/107 initial segment requests; the one early 404 was a seek race, rebuilt immediately, and did not recur. The direct-play run produced 46 successful progress updates with clean 200/206 responses.
  • Across the post-deploy window there were no playback 5xx responses and none of the previous unsupported video sample-entry, transcode_start_failed, or spawn_failed signatures. There were no genuine FFmpeg crashes.

Both users confirmed playback now works, and User 2 continued watching multiple titles without another issue. This covers Auto and 2160p/4K, DV7 and DV8, audio-copy/AAC adaptation, seeking, HLS remux, and direct play. The server regression tests also cover Auto, Original, and 2160p planning.

Validation

./gradlew :android-shared:testDebugUnitTest \
  --tests org.siloserver.silo.common.player.PlaybackCapabilityDetectorDolbyVisionTest \
  --no-daemon --max-workers=2
BUILD SUCCESSFUL

./gradlew :android-shared:lintDebug --no-daemon --max-workers=2
BUILD SUCCESSFUL

git diff --check upstream/main...HEAD
<no output>

Coordination and related work

Together with the companion server change, this closes out the remaining Android Media3 sample-entry/startup failure. It is a better boundary than globally allowing hev1, which only moved the failure to the next Profile 8 validation and risked changing web playback behavior.

Risk

Low and bounded. This adds one capability token to one delivery class. Existing build 15 clients remain supported by the server compatibility path, while upgraded clients provide explicit evidence. Original, progressive, Cast, Apple, and web behavior are unchanged.

AI Disclosure

  • Tool(s): OpenAI Codex desktop
  • Model(s): Codex Sol (gpt-5.6-sol), Ultra reasoning
  • Involvement: AI-assisted. I directed the investigation and fix, supplied the affected-user reports, made the scope decisions, and helped reproduce and validate it on my live server.
  • Adversarial review: We checked the Android phone/TV boundary, confirmed the token is HLS-only, kept Original/progressive/Cast out of scope, traced the matching server recipe through final FFmpeg validation, and tested the previously failing Auto/Original/2160p and DV7/DV8 cases. The live build 15 sessions above were the final acceptance test.

Summary by CodeRabbit

  • New Features

    • Added support for advertising native HLS playback capabilities on compatible HLS deliveries.
    • Enables improved handling of Dolby Vision and HEVC video formats through the native playback pipeline.
  • Tests

    • Added coverage verifying native HLS support is advertised for mobile and TV HLS playback, but not other delivery types.

@coderabbitai

coderabbitai Bot commented Aug 30, 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: 26208fad-c5de-40dd-b211-d7a49cc762d0

📥 Commits

Reviewing files that changed from the base of the PR and between 791ad01 and 2f05d77.

📒 Files selected for processing (3)
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlaybackCapabilityDetector.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackCapabilityDetectorDolbyVisionTest.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/playback/PlaybackProtocolV3.kt

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


📝 Walkthrough

Walkthrough

The playback protocol adds a native HLS feature flag. Android capability detection advertises the flag on Media3 HLS delivery only. Robolectric tests verify the behavior for mobile and TV form factors.

Changes

Native HLS playback capability

Layer / File(s) Summary
Define, advertise, and validate native HLS support
shared/src/commonMain/.../PlaybackProtocolV3.kt, android-shared/src/.../PlaybackCapabilityDetector.kt, android-shared/src/.../PlaybackCapabilityDetectorDolbyVisionTest.kt
Adds NATIVE_HLS_PLAYBACK_V1_FEATURE, includes it in HLS delivery capabilities, and verifies that mobile and TV detectors exclude it from original HTTP and progressive delivery classes.

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

Merge Risk: ⚪ Minimal · up to 2f05d

This narrowly adds a native Media3 HLS capability for Android local HLS delivery while leaving other playback routes unchanged. No actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: quick104, rxwatcher

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changes do not implement the primary requirements of linked issue #657, which concern the DV7-to-HDR10 remux recipe, FFmpeg capability checks, versioned recipe propagation, and fail-closed behavio… Link this pull request to an issue that covers native Media3 HLS capability advertisement, or implement the requirements from #657 in this pull request. Do not use #657 as the direct issue for this scope without documented alignment or an u…
Out of Scope Changes check ⚠️ Warning The Android and shared-protocol changes are outside the scope of linked issue #657. That issue explicitly keeps native-HLS selection and broader Dolby Vision routing out of scope. Remove the native Media3 HLS capability changes from this pull request, or link the pull request to the appropriate native-HLS issue and update the scope documentation.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: advertising native Media3 HLS playback support.
Full details: Linked Issues check

Explanation

The changes do not implement the primary requirements of linked issue #657, which concern the DV7-to-HDR10 remux recipe, FFmpeg capability checks, versioned recipe propagation, and fail-closed behavior. The linked issue explicitly excludes native-HLS selection.

Resolution

Link this pull request to an issue that covers native Media3 HLS capability advertisement, or implement the requirements from #657 in this pull request. Do not use #657 as the direct issue for this scope without documented alignment or an updated issue description requiring these changes.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@Quick104
Quick104 merged commit 1eebab9 into Silo-Server:main Aug 30, 2026
3 checks passed
Quick104 added a commit that referenced this pull request Aug 30, 2026
#257)

* feat(android): server-driven card presentation presets on phone and TV

Adds the "Cards & Posters" preference to both Android clients, porting the
iOS/tvOS feature built on the canonical `ui.card_presentation` setting.
The value is the contract's whole two-field object -- `poster_size`
(compact/standard/large) and `caption` (title_metadata/title/artwork) --
surfaced as the same four presets Apple and the web UI offer (Balanced,
Compact, Cinema, Artwork Only) plus per-axis pickers, with a synthetic
"Custom" entry when the pair matches no preset.

Per-device preferences: writes land at `profile_client` by default so a
choice roams between like devices (Android TV joins the `tv` family, phone
`mobile`, tablet `tablet` via a new X-Silo-Client-Family header), and an
"Only this device" toggle writes at `profile_device` instead, which the
server resolves ahead of the family and profile layers. Turning it off
deletes that row so resolution falls back.

Rendering follows Apple's semantics: rails and standalone cards scale
0.86/1.0/1.2, fixed grids shift a column either way, adaptive grids scale
their min cell width, and the caption axis gates the title and metadata
lines. The TV Skyline row band height is now derived from the scaled card
height and caption rows rather than a fixed 0.50 fraction, so large cards
have room without clipping the marquee -- reproducing today's band exactly
at the standard preset.

State lives in a new CardPresentationStore (android-shared), capability-
gated on the settings contract, cached per server/profile/family/device for
a jump-free cold start, with optimistic writes, latest-wins coalescing, and
rollback on failure. It refreshes on foreground and reconnect alongside the
existing overlay prefs, and clears on sign-out and on profile/server switch.

Closes #163.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(diagnostics): expand Android client evidence (#261)

* fix(playback): advertise native Media3 HLS (#262)

* fix(tv): prefer native mounted transport for quick skips instead of re-anchoring + map seek input + fix second press for seek (#259)

* fix(tv): prefer the mounted transport for quick skips instead of re-anchoring

Server plans for growing-HLS transcodes and copy-remux transports publish
an open-ended seek window with can_seek_anywhere=false, so every quick
skip went through a protocol-V3 seek_reanchor: buffering UI, an API
round trip, and a full restream, even when the target sat inside content
the player already had mounted.

decideSeek now accepts an optional mountedSeekableSourceRange: the
source-time extent the currently mounted transport provably covers. The
TV screen reports the Media3 window's seekability and extent alongside
its existing position poll, and a target inside that range is a plain
Player.seekTo. Targets the mounted transport cannot serve (before the
stream origin, past the produced manifest head, or a growing progressive
copy remux with no known length) still reanchor, and the existing
same-route reanchor recovery remains the fallback for native seeks that
fail with 404/410/416.

* fix(tv): route media rewind and fast-forward keys through the player bridge

The remote's rewind/fast-forward keys (plus SKIP_BACKWARD/SKIP_FORWARD)
were unmapped, so the player bridge's null-action branch only revealed
the transport controls and swallowed the first press - users had to
press twice to seek - and the second press fell through to Media3's
session-level seekBack/seekForward, bypassing the app's route-aware
seek path and the Watch Together room transport gate.

Map them to SkipBack/SkipForward like D-pad left/right. They ignore
dpadHorizontalSeek because they are never focus navigation, and UP
halves and auto-repeats are consumed so the system media-key fallback
cannot seek a second time.

* fix(tv): invalidate mounted-window seek facts when the transport is replaced

The mounted-window facts feeding decideSeek's mountedSeekableSourceRange hint describe one Media3 item, but they were neither suppressed during a transport handoff nor cleared when a mount won. After a reanchor/replan/content switch, the previous transport's window end mapped through the new plan's timelineOffsetSeconds overstates the new transport's extent, and a quick skip inside that window could take a native path that clamps to the new item's short window - a silent, wrong-position seek with no error and no self-heal. The sharpest instance: onTransportMountApplied re-evaluates pendingNativeSeekAfterMount at exactly that moment of maximal staleness.

Drop onPlayerWindowChanged reports while transportMountGate is suppressing (the same guard onPositionChanged applies), and clear both facts at the top of the applied(nonce) branch before the queued-seek re-evaluation. The next poll tick (<=500ms) repopulates from the mounted item; until then the hint is absent, which at worst costs an unnecessary reanchor.

* docs(tv): document the seek-hint and media-key contracts

KDoc for tvPlayerRemoteKeyAction (the full key-to-action mapping incl. why unmapped keys only reveal controls) and executeSeekTarget (the seek-commit routing ladder), plus one-line contracts on the new test functions. Addresses CodeRabbit's docstring-coverage pre-merge check on PR #259.

---------

Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com>

* fix(tv): keep the skyline rail gliding after a detail round trip (#260)

returnRestoreRequest is bumped when a restore ladder starts and never
reset for the feed's lifetime, while detailReturnPending is re-armed by
every focus move. After the first detail round trip, every later focus
move therefore re-fired TvMediaRow's restore effect and its instant
scrollToItem, which cancels the rail pin's animated glide — the carousel
snapped instead of scrolling until the feed was disposed again.

Pass the restore request only while a ladder is actually in flight
(restorationsInFlight > 0): the instant row scroll exists solely to
compose a ladder's destination into the horizontal window. Also log
restore scrolls through TvFocusLog so a regression is visible in logcat
instead of only on screen.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: blurbery <271863866+blurbery@users.noreply.github.com>
Co-authored-by: evulhotdog <365456+evulhotdog@users.noreply.github.com>
Quick104 added a commit that referenced this pull request Aug 30, 2026
* feat: improve SiloCast remote controls

* refactor(android): unify image loading on shared OkHttp and defer artwork presentation during scroll (#256)

- Replace coil-network-ktor3 with coil-network-okhttp and register the
  fetcher explicitly: new SiloOkHttp foundation shares one ConnectionPool
  between the Ktor API clients and Coil, with per-client tuned dispatchers
  so image bursts and API fan-outs never queue behind each other. Loader
  config is unified in buildSiloImageLoader so phone and TV cannot drift.
- Extend Home's existing artwork-presentation deferral to all artwork
  scroll surfaces via DeferImagePresentationWhileScrolling: freshly decoded
  images hold their thumbhash until the scroll settles (memory-cache hits
  still present instantly), now covering horizontal rows, grids, detail
  feeds, and vertical-scroll columns.
- Fix stale-capture load-more triggers in CatalogGrid and SearchResults:
  keyless remember { derivedStateOf } froze first-composition
  hasMore/isLoading values, breaking pagination in some entry states.
- Declare ktor-client-core explicitly in android-shared/androidApp (it was
  only reaching the compile classpath transitively via the old Coil
  artifact); regenerate dependency locks and verification metadata and
  drop stale TV lock pins.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* feat(diagnostics): expand Android client evidence (#261)

* fix(playback): advertise native Media3 HLS (#262)

* fix(tv): prefer native mounted transport for quick skips instead of re-anchoring + map seek input + fix second press for seek (#259)

* fix(tv): prefer the mounted transport for quick skips instead of re-anchoring

Server plans for growing-HLS transcodes and copy-remux transports publish
an open-ended seek window with can_seek_anywhere=false, so every quick
skip went through a protocol-V3 seek_reanchor: buffering UI, an API
round trip, and a full restream, even when the target sat inside content
the player already had mounted.

decideSeek now accepts an optional mountedSeekableSourceRange: the
source-time extent the currently mounted transport provably covers. The
TV screen reports the Media3 window's seekability and extent alongside
its existing position poll, and a target inside that range is a plain
Player.seekTo. Targets the mounted transport cannot serve (before the
stream origin, past the produced manifest head, or a growing progressive
copy remux with no known length) still reanchor, and the existing
same-route reanchor recovery remains the fallback for native seeks that
fail with 404/410/416.

* fix(tv): route media rewind and fast-forward keys through the player bridge

The remote's rewind/fast-forward keys (plus SKIP_BACKWARD/SKIP_FORWARD)
were unmapped, so the player bridge's null-action branch only revealed
the transport controls and swallowed the first press - users had to
press twice to seek - and the second press fell through to Media3's
session-level seekBack/seekForward, bypassing the app's route-aware
seek path and the Watch Together room transport gate.

Map them to SkipBack/SkipForward like D-pad left/right. They ignore
dpadHorizontalSeek because they are never focus navigation, and UP
halves and auto-repeats are consumed so the system media-key fallback
cannot seek a second time.

* fix(tv): invalidate mounted-window seek facts when the transport is replaced

The mounted-window facts feeding decideSeek's mountedSeekableSourceRange hint describe one Media3 item, but they were neither suppressed during a transport handoff nor cleared when a mount won. After a reanchor/replan/content switch, the previous transport's window end mapped through the new plan's timelineOffsetSeconds overstates the new transport's extent, and a quick skip inside that window could take a native path that clamps to the new item's short window - a silent, wrong-position seek with no error and no self-heal. The sharpest instance: onTransportMountApplied re-evaluates pendingNativeSeekAfterMount at exactly that moment of maximal staleness.

Drop onPlayerWindowChanged reports while transportMountGate is suppressing (the same guard onPositionChanged applies), and clear both facts at the top of the applied(nonce) branch before the queued-seek re-evaluation. The next poll tick (<=500ms) repopulates from the mounted item; until then the hint is absent, which at worst costs an unnecessary reanchor.

* docs(tv): document the seek-hint and media-key contracts

KDoc for tvPlayerRemoteKeyAction (the full key-to-action mapping incl. why unmapped keys only reveal controls) and executeSeekTarget (the seek-commit routing ladder), plus one-line contracts on the new test functions. Addresses CodeRabbit's docstring-coverage pre-merge check on PR #259.

---------

Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com>

* fix(tv): keep the skyline rail gliding after a detail round trip (#260)

returnRestoreRequest is bumped when a restore ladder starts and never
reset for the feed's lifetime, while detailReturnPending is re-armed by
every focus move. After the first detail round trip, every later focus
move therefore re-fired TvMediaRow's restore effect and its instant
scrollToItem, which cancels the rail pin's animated glide — the carousel
snapped instead of scrolling until the feed was disposed again.

Pass the restore request only while a ladder is actually in flight
(restorationsInFlight > 0): the instant row scroll exists solely to
compose a ladder's destination into the horizontal window. Also log
restore scrolls through TvFocusLog so a regression is visible in logcat
instead of only on screen.

* feat(android): server-driven card presentation presets on phone and TV (#257)

* feat(android): server-driven card presentation presets on phone and TV

Adds the "Cards & Posters" preference to both Android clients, porting the
iOS/tvOS feature built on the canonical `ui.card_presentation` setting.
The value is the contract's whole two-field object -- `poster_size`
(compact/standard/large) and `caption` (title_metadata/title/artwork) --
surfaced as the same four presets Apple and the web UI offer (Balanced,
Compact, Cinema, Artwork Only) plus per-axis pickers, with a synthetic
"Custom" entry when the pair matches no preset.

Per-device preferences: writes land at `profile_client` by default so a
choice roams between like devices (Android TV joins the `tv` family, phone
`mobile`, tablet `tablet` via a new X-Silo-Client-Family header), and an
"Only this device" toggle writes at `profile_device` instead, which the
server resolves ahead of the family and profile layers. Turning it off
deletes that row so resolution falls back.

Rendering follows Apple's semantics: rails and standalone cards scale
0.86/1.0/1.2, fixed grids shift a column either way, adaptive grids scale
their min cell width, and the caption axis gates the title and metadata
lines. The TV Skyline row band height is now derived from the scaled card
height and caption rows rather than a fixed 0.50 fraction, so large cards
have room without clipping the marquee -- reproducing today's band exactly
at the standard preset.

State lives in a new CardPresentationStore (android-shared), capability-
gated on the settings contract, cached per server/profile/family/device for
a jump-free cold start, with optimistic writes, latest-wins coalescing, and
rollback on failure. It refreshes on foreground and reconnect alongside the
existing overlay prefs, and clears on sign-out and on profile/server switch.

Closes #163.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(diagnostics): expand Android client evidence (#261)

* fix(playback): advertise native Media3 HLS (#262)

* fix(tv): prefer native mounted transport for quick skips instead of re-anchoring + map seek input + fix second press for seek (#259)

* fix(tv): prefer the mounted transport for quick skips instead of re-anchoring

Server plans for growing-HLS transcodes and copy-remux transports publish
an open-ended seek window with can_seek_anywhere=false, so every quick
skip went through a protocol-V3 seek_reanchor: buffering UI, an API
round trip, and a full restream, even when the target sat inside content
the player already had mounted.

decideSeek now accepts an optional mountedSeekableSourceRange: the
source-time extent the currently mounted transport provably covers. The
TV screen reports the Media3 window's seekability and extent alongside
its existing position poll, and a target inside that range is a plain
Player.seekTo. Targets the mounted transport cannot serve (before the
stream origin, past the produced manifest head, or a growing progressive
copy remux with no known length) still reanchor, and the existing
same-route reanchor recovery remains the fallback for native seeks that
fail with 404/410/416.

* fix(tv): route media rewind and fast-forward keys through the player bridge

The remote's rewind/fast-forward keys (plus SKIP_BACKWARD/SKIP_FORWARD)
were unmapped, so the player bridge's null-action branch only revealed
the transport controls and swallowed the first press - users had to
press twice to seek - and the second press fell through to Media3's
session-level seekBack/seekForward, bypassing the app's route-aware
seek path and the Watch Together room transport gate.

Map them to SkipBack/SkipForward like D-pad left/right. They ignore
dpadHorizontalSeek because they are never focus navigation, and UP
halves and auto-repeats are consumed so the system media-key fallback
cannot seek a second time.

* fix(tv): invalidate mounted-window seek facts when the transport is replaced

The mounted-window facts feeding decideSeek's mountedSeekableSourceRange hint describe one Media3 item, but they were neither suppressed during a transport handoff nor cleared when a mount won. After a reanchor/replan/content switch, the previous transport's window end mapped through the new plan's timelineOffsetSeconds overstates the new transport's extent, and a quick skip inside that window could take a native path that clamps to the new item's short window - a silent, wrong-position seek with no error and no self-heal. The sharpest instance: onTransportMountApplied re-evaluates pendingNativeSeekAfterMount at exactly that moment of maximal staleness.

Drop onPlayerWindowChanged reports while transportMountGate is suppressing (the same guard onPositionChanged applies), and clear both facts at the top of the applied(nonce) branch before the queued-seek re-evaluation. The next poll tick (<=500ms) repopulates from the mounted item; until then the hint is absent, which at worst costs an unnecessary reanchor.

* docs(tv): document the seek-hint and media-key contracts

KDoc for tvPlayerRemoteKeyAction (the full key-to-action mapping incl. why unmapped keys only reveal controls) and executeSeekTarget (the seek-commit routing ladder), plus one-line contracts on the new test functions. Addresses CodeRabbit's docstring-coverage pre-merge check on PR #259.

---------

Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com>

* fix(tv): keep the skyline rail gliding after a detail round trip (#260)

returnRestoreRequest is bumped when a restore ladder starts and never
reset for the feed's lifetime, while detailReturnPending is re-armed by
every focus move. After the first detail round trip, every later focus
move therefore re-fired TvMediaRow's restore effect and its instant
scrollToItem, which cancels the rail pin's animated glide — the carousel
snapped instead of scrolling until the feed was disposed again.

Pass the restore request only while a ladder is actually in flight
(restorationsInFlight > 0): the instant row scroll exists solely to
compose a ladder's destination into the horizontal window. Also log
restore scrolls through TvFocusLog so a regression is visible in logcat
instead of only on screen.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: blurbery <271863866+blurbery@users.noreply.github.com>
Co-authored-by: evulhotdog <365456+evulhotdog@users.noreply.github.com>

* fix: address SiloCast review findings

* fix: reconcile mute state for volume changes

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: blurbery <271863866+blurbery@users.noreply.github.com>
Co-authored-by: evulhotdog <365456+evulhotdog@users.noreply.github.com>
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.

2 participants