yuliskov/SmartTube
Browse media content with your own rules on Android TV
Single-maintainer risk — review before adopting
weakest axistop contributor handles 97% of recent commits; no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit today
- ✓4 active contributors
- ✓MIT licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ⚠Small team — 4 contributors active in recent commits
- ⚠Single-maintainer risk — top contributor 97% of recent commits
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: diversify commit ownership (top <90%)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/yuliskov/smarttube)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/yuliskov/smarttube on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: yuliskov/SmartTube
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/yuliskov/SmartTube shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Single-maintainer risk — review before adopting
- Last commit today
- 4 active contributors
- MIT licensed
- CI configured
- ⚠ Small team — 4 contributors active in recent commits
- ⚠ Single-maintainer risk — top contributor 97% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live yuliskov/SmartTube
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/yuliskov/SmartTube.
What it runs against: a local clone of yuliskov/SmartTube — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in yuliskov/SmartTube | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of yuliskov/SmartTube. If you don't
# have one yet, run these first:
#
# git clone https://github.com/yuliskov/SmartTube.git
# cd SmartTube
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of yuliskov/SmartTube and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "yuliskov/SmartTube(\\.git)?\\b" \\
&& ok "origin remote is yuliskov/SmartTube" \\
|| miss "origin remote is not yuliskov/SmartTube (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "build.gradle" \\
&& ok "build.gradle" \\
|| miss "missing critical file: build.gradle"
test -f ".github/workflows/CI.yml" \\
&& ok ".github/workflows/CI.yml" \\
|| miss "missing critical file: .github/workflows/CI.yml"
test -f "PRIVACY.md" \\
&& ok "PRIVACY.md" \\
|| miss "missing critical file: PRIVACY.md"
test -f "chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesList.java" \\
&& ok "chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesList.java" \\
|| miss "missing critical file: chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesList.java"
test -f "chatkit/build.gradle" \\
&& ok "chatkit/build.gradle" \\
|| miss "missing critical file: chatkit/build.gradle"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/yuliskov/SmartTube"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
SmartTube is a free, open-source Android TV media client (14.9MB Java codebase) that lets you browse and play content from public sources with a TV-optimized interface. It integrates SponsorBlock, supports 8K/60fps/HDR playback, live chat viewing, and custom UI buttons—all without requiring Google Play Services, making it ideal for privacy-conscious users on FireTV, Nvidia Shield, and other Android TV devices. Multi-module Gradle project: chatkit/ is a third-party chat UI library (stfalcon fork); main app logic inferred in sibling modules (not listed in top 60 but referenced via .github/workflows/CI.yml). Build configured in build.gradle with Gradle 7.4.2, Kotlin plugin, and dependency resolution via Maven Central + JitPack. GitHub Actions orchestrate CI, virus scanning, and cleanup workflows.
👥Who it's for
Android TV / TV box users who want an alternative to official apps with better customization, privacy controls, and community features; developers contributing to FOSS media players and Android TV application architecture.
🌱Maturity & risk
Actively developed and production-ready. The project has significant Java/Kotlin codebase (14.9MB Java, 58KB Kotlin), established CI/CD pipelines (GitHub Actions: CI.yml, virustotal_scan.yml, cleanup.yml), and F-Droid distribution. Recent security incident (malware in build environment) was disclosed transparently and mitigated via VirusTotal scanning, indicating mature security practices. Development is ongoing but the maintainer recently had to rebuild their development environment.
Single-maintainer risk (yuliskov as sole owner visible in repo name). Recent security incident where build environment was compromised—though remediated, this signals elevated vigilance needed. Breaking device compatibility: FireTV devices shipping after Oct 2025 run VegaOS (not Android), so SmartTube becomes incompatible. Dependency on external services (SponsorBlock, Google login flow) means stability depends on third-party API health.
Active areas of work
The repo underwent emergency rebuild after development environment compromise (disclosed in README). VirusTotal scanning is now integrated into CI pipeline (virustotal_scan.yml). F-Droid distribution has been re-verified. The chatkit submodule and main app dependencies (OkHttp, WorkManager, Kotlin stdlib) are being maintained with forced version resolution to ensure compatibility.
🚀Get running
git clone --recurse-submodules https://github.com/yuliskov/SmartTube.git
cd SmartTube
./gradlew build
./gradlew installDebug # assumes Android TV emulator or device connected
Daily commands:
./gradlew build # Compile all modules
./gradlew assembleDebug # Create debug APK
./gradlew installDebug # Install to connected Android TV device/emulator
./gradlew test # Run unit tests (if configured in submodules)
🗺️Map of the codebase
build.gradle— Top-level Gradle configuration defining Android SDK, Kotlin, and plugin versions—every contributor must verify compatibility before submitting changes..github/workflows/CI.yml— CI/CD pipeline that auto-builds and scans releases with VirusTotal; understand this to trust the security model and release process.PRIVACY.md— Documents the security incident and mitigation strategy; critical for understanding the app's trust model and one-time connection codes.chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesList.java— Core UI component for rendering live chat/messages; a load-bearing dependency for the media browsing feature.chatkit/build.gradle— Chat library module configuration; contributors adding chat features must understand its dependencies and ProGuard rules.common/build.gradle— Common module bundling shared utilities; defines consumer rules that ripple into dependent modules.README.md— Project overview and context; essential for understanding SmartTube's purpose as an Android TV media browser with custom rules.
🛠️How to make changes
Add a new message content type (e.g., video, file)
- Add a new enum constant in MessageContentType to identify the new type. (
chatkit/src/main/java/com/stfalcon/chatkit/commons/models/MessageContentType.java) - Create a custom view holder class extending ViewHolder for rendering the new content (e.g., VideoMessageHolder). (
chatkit/src/main/java/com/stfalcon/chatkit/messages/MessageHolders.java) - Register the new holder in MessagesListAdapter.registerViewHolder() or configure it in the adapter's initialization. (
chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesListAdapter.java) - Create layout XML files (e.g., item_incoming_video_message.xml, item_outcoming_video_message.xml). (
chatkit/src/main/res/layout/)
Customize message styling and appearance
- Review and modify MessagesListStyle to adjust colors, padding, fonts, and animations. (
chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesListStyle.java) - Update drawable resources for message bubbles (shape_incoming_message.xml, shape_outcoming_message.xml). (
chatkit/src/main/res/drawable/) - Modify color and dimen values in res/values/colors.xml and res/values/dimens.xml. (
chatkit/src/main/res/values/) - Update message layout XMLs (item_incoming_text_message.xml, etc.) to apply new styling. (
chatkit/src/main/res/layout/)
Integrate a new library into the shared common module
- Add the dependency to the common module's build.gradle under dependencies. (
common/build.gradle) - If the library uses reflection or dynamic class loading, add ProGuard keep rules to common/proguard-rules.pro. (
common/proguard-rules.pro) - Create wrapper classes or interfaces in common/src/main/java to abstract the library's API. (
common/src/main/java) - Update consumer-rules.pro if the library requires specific ProGuard rules for dependent modules. (
common/consumer-rules.pro)
Add a custom dialog or screen using the DialogsList component
- Implement IDialog interface for your dialog model with required getters. (
chatkit/src/main/java/com/stfalcon/chatkit/commons/models/IDialog.java) - Create a custom view holder extending ViewHolder or use DialogsListAdapter with custom styling. (
chatkit/src/main/java/com/stfalcon/chatkit/dialogs/DialogsListAdapter.java) - Define a layout XML for each dialog item (similar to item_dialog.xml). (
chatkit/src/main/res/layout/item_dialog.xml) - Apply DialogListStyle for consistent appearance and set listeners for click events. (
chatkit/src/main/java/com/stfalcon/chatkit/dialogs/DialogListStyle.java)
🔧Why these technologies
- Android SDK / Gradle — Required for building Android TV applications; Gradle 7.4.2 enables multi-module support and dependency management at scale.
- Kotlin + Java mixed codebase — Kotlin provides modern language features and null safety while maintaining Java interoperability; supports gradual migration.
- RecyclerView (via chatkit) — Efficient list rendering for messages and dialogs; essential for smooth Android TV performance with limited RAM.
- ProGuard / code shrinking — Reduces APK size and obfuscates code; critical for Android TV devices with storage constraints and post-incident security posture.
- GitHub Actions CI/CD + VirusTotal scanning — Automated security scanning post-incident; ensures every build is scanned for malware before release.
- One-time connection codes (Google OAuth) — Minimal-privilege authentication; limits damage if credentials are compromised, addressing the security incident context.
⚖️Trade-offs already made
-
Chat library (chatkit) as a separate module vs. embedded in main app
- Why: Promotes code reuse and allows independent testing and versioning; reduces main app complexity.
- Consequence: Adds module-crossing dependency management and import overhead; requires careful synchronization of data models.
-
IMessage/IUser/IDialog interfaces vs. concrete classes
- Why: Enables flexible implementations and testing with mock objects; decouples library from specific data schemas.
- Consequence: Developers must implement contracts correctly; no compile-
🪤Traps & gotchas
- Submodule gotcha: Must clone with
--recurse-submodulesor manually init submodules; partial clones will fail to build. 2. OkHttp version pinning: Forced to 4.x viaresolutionStrategy.forceinbuild.gradle; upgrading breaks compatibility with legacy Android versions. 3. Kotlin stdlib duplication: Bothkotlin-stdlib-jdk8andkotlin-stdlib-jdk7are forced to same version to avoid WorkManager conflicts. 4. Build environment security: VirusTotal scanning expects API key in CI environment; locally you'll build without scanning (fine for dev, not production). 5. Device incompatibility post-Oct 2025: Amazon FireTV devices transition to VegaOS; any FireTV 4K Select or newer will not run this app—README explicitly warns of this breaking change. 6. No Google Play Services: App deliberately avoids GSF, but some features (OAuth login) rely on Google Account provider on the device itself.
🏗️Architecture
💡Concepts to learn
- Android TV Leanback Library — SmartTube is built for TV with D-pad navigation and no touch input; Leanback (androidx.leanback) provides TV-optimized UI components (BrowseFragment, PreferenceFragment, etc.) that differ drastically from phone apps and require different design patterns.
- ProGuard / R8 Obfuscation — The
chatkit/proguard.txtrules define which classes survive obfuscation; incorrect rules break reflection-based Android code and third-party libraries. Critical for shipping a protected APK. - Gradle Dependency Resolution Strategy — The forced version pinning for OkHttp and Kotlin stdlib (
resolutionStrategy.force) resolves transitive dependency conflicts. Misunderstanding this causes silent bugs where the wrong version ships. - RecyclerView + ViewHolder Pattern — The chatkit library uses
InputTrackingRecyclerViewAdapterand customViewHolderclasses (inchatkit/src/main/java/com/stfalcon/chatkit/commons/) to efficiently render lists. Essential for TV performance with limited memory. - One-Time OAuth Codes — SmartTube uses limited-scope one-time connection codes for Google authentication instead of storing permanent tokens, reducing attack surface if backups are compromised. This is an important privacy/security pattern.
- Gradle Wrapper & Reproducible Builds — The commented-out
wrapper {}block andgradleVersionvariable indicate an attempt at deterministic builds. This matters post-security-incident because VirusTotal scanning requires bit-for-bit reproducibility to detect tampering. - WorkManager & Dependency Conflicts — The build.gradle forces multiple Kotlin versions to resolve WorkManager conflicts with lifecycle libraries. This reflects a broader issue in AndroidX—task scheduling needs careful version alignment or background work breaks silently.
🔗Related repos
jellyfin/jellyfin-androidtv— Competitor Android TV media client with similar goals (privacy-first, custom UI, no Google Play Services); valuable for feature parity and architectural comparison.TeamNewPipe/NewPipe— Sibling FOSS YouTube client for phones/tablets using similar content extraction; SmartTube reuses some NewPipe extraction logic, making this a key dependency to understand.akomskyy/mediacenterjs— Alternative media center framework; demonstrates different approaches to modular media player architecture on Android.stfalcon/ChatKit— The upstream chatkit library (SmartTube includes a fork inchatkit/directory); understanding the original helps debug UI customizations specific to TV.yt-dlp/yt-dlp— Command-line tool for media extraction that SmartTube likely wraps or integrates with for fetching video metadata and streams.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add GitHub Actions workflow for automated APK scanning with VirusTotal
The README prominently mentions that all builds are now scanned with VirusTotal after a security incident, but there is no CI workflow (.github/workflows/) that automates this scanning. The existing CI.yml likely builds the APK but doesn't integrate VirusTotal scanning. A new workflow would ensure every release is automatically scanned and results are published, reinforcing the security commitment made in the README.
- [ ] Create .github/workflows/virustotal_scan.yml (note: virustotal_scan.yml exists but may not be properly configured for APK scanning post-build)
- [ ] Configure the workflow to trigger on release/tag events and scan the generated APK
- [ ] Add VirusTotal API integration to post scan results as a GitHub release comment or artifact
- [ ] Document the workflow in a SECURITY.md or update README.md with scan result links
Add unit tests for chatkit commons utilities (DebouncedOnClickListener, ImageLoader, Style)
The chatkit module contains several utility classes (chatkit/src/main/java/com/stfalcon/chatkit/commons/) that lack test coverage. These are foundational components used across the messaging UI. DebouncedOnClickListener in particular is critical for preventing duplicate actions on Android TV (where button presses can be erratic). Adding tests would catch regressions early.
- [ ] Create chatkit/src/test/java/com/stfalcon/chatkit/commons/ directory structure
- [ ] Write unit tests for DebouncedOnClickListener.java covering debounce timing and callback invocation
- [ ] Write unit tests for Style.java covering configuration parsing and defaults
- [ ] Add tests for ImageLoader.java covering caching and error handling scenarios
- [ ] Update chatkit/build.gradle to include testing dependencies (JUnit, Mockito)
Create ARCHITECTURE.md documenting the chatkit module integration and Android TV optimization patterns
The repo contains a custom chatkit module (a modified version from stfalcon) with Android TV-specific adaptations like FocusFixRelativeLayout.java and WrapWidthTextView.java, but there is no documentation explaining why these modifications exist or how the messaging system integrates with the main SmartTube app. This knowledge is critical for new contributors and prevents duplicate work.
- [ ] Document the chatkit module's role in SmartTube's architecture (README indicates it's for browsing content with custom rules, likely including chat/comments)
- [ ] Explain Android TV-specific modifications in chatkit/src/main/java/com/stfalcon/chatkit/commons/widgets/ and why they're necessary
- [ ] Add design diagrams or flowcharts showing how MessagesList, DialogsList, and MessageInput components interact
- [ ] Document the security incident mentioned in README and how it affects the chatkit module or build verification
- [ ] Link to specific files in chatkit and explain the message model interfaces (IMessage, IUser, IDialog)
🌿Good first issues
- Add unit test coverage for chatkit MessagesList component: The
chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesList.javahas no visible test file. A new contributor could write tests for message rendering, scrolling behavior, and RecyclerView adapter interactions—critical for a chat UI component. - Document Gradle dependency resolution strategy in CONTRIBUTING guide: The forced versions in
build.gradle(OkHttp 4.x, Kotlin stdlib duplication) are undocumented. A contributor could write a CONTRIBUTING.md explaining why these constraints exist, saving future maintainers debugging time when dependency upgrades fail. - Add ProGuard rule tests for chatkit obfuscation: The
chatkit/proguard.txtfile defines obfuscation rules but there's no test verifying that the obfuscated APK still functions. A new contributor could add a CI step that validates ProGuard rules don't break reflection-dependent Android framework calls.
⭐Top contributors
Click to expand
Top contributors
- @yuliskov — 97 commits
- @Resshi — 1 commits
- @iBasim — 1 commits
- @Kaigara1071 — 1 commits
📝Recent commits
Click to expand
Recent commits
b115a35— AccountDialog: more fixes to show an account icon (yuliskov)c442b5b— AccoundDialog: show the icon in the title (yuliskov)3cd7cbd— AppDialog: refactor (switch to CharSequence) (yuliskov)ffd4b47— player error fixer: refactor (yuliskov)f5f1233— AccountsDialog: show account icons along with its names in the list; upd SharedModules (yuliskov)66827c9— upd MediaServiceCore (yuliskov)5f1024a— revert previous commit (yuliskov)635b371— Add to Watch later: change order in the menu (yuliskov)863ee3e— Add to Watch later routine refactor (yuliskov)37b6e25— Add to Watch later menu fix (yuliskov)
🔒Security observations
SmartTube has a moderate security posture with notable concerns. The most critical issue is the historical supply chain compromise mentioned in the README, which impacts user trust despite claimed remediation. Technical vulnerabilities include outdated build tools (Gradle 7.4.2), forced use of older OkHttp3 for legacy compatibility, and unspecified dependency versions. The project demonstrates security awareness through VirusTotal scanning and F-Droid distribution, but needs to modernize its build infrastructure and provide clearer transparency about the previous compromise. The architecture appears sound (minimal chatkit library dependencies shown), but the dependency management strategy requires refinement for production-grade security.
- High · Outdated Gradle Build Tools —
build.gradle (classpath 'com.android.tools.build:gradle:7.4.2'). The project uses Gradle 7.4.2 which is outdated. Current versions (8.x+) include important security fixes and improvements. Older versions may have unpatched vulnerabilities in the build system itself. Fix: Update to the latest stable Gradle version (8.x or higher) to receive security patches and improvements. - High · Historical Supply Chain Compromise —
README.md and project history. The README explicitly mentions the development environment was infected with malicious software, potentially affecting builds and public keys. While the developer claims to have remediated the issue, there is historical evidence of compromise affecting previous releases. Fix: Users should verify app signatures against newly published public keys. The developer should maintain transparency about affected versions. Consider implementing reproducible builds to enable verification by third parties. - Medium · Dependency Resolution without Pin Versions —
build.gradle (allprojects -> configurations.all section). The build.gradle uses force resolutionStrategy but many dependencies lack pinned versions. The use ofresolutionStrategy.force 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:'without a version number could pull arbitrary versions. Fix: Specify explicit versions for all dependency overrides. Remove version-less declarations like 'kotlin-stdlib-jdk7:' and replace with 'kotlin-stdlib-jdk7:<VERSION>'. - Medium · JCenter Repository Deprecation —
build.gradle (commented jcenter() lines). While JCenter is commented out, references to outdated repositories and the removal of JCenter indicates the project may have had dependency sources that are no longer maintained. Fix: Ensure all dependencies are sourced from active, maintained repositories (Google, Maven Central, JitPack). Regular dependency audits should be conducted. - Medium · OkHttp3 Version Pinning —
build.gradle (resolutionStrategy.force 'com.squareup.okhttp3:okhttp:' + okhttpVersion). The build forces an older OkHttp3 version for Android 4 compatibility, which may lack modern security features and bug fixes available in newer versions. Fix: Evaluate if Android 4 support is still necessary. If possible, increase minimum API level to enable use of modern, secure dependencies. Otherwise, regularly audit the pinned OkHttp version for known CVEs. - Low · Missing Security Headers Configuration —
Project-wide configuration. No evidence of security headers configuration (CSP, X-Frame-Options, etc.) visible in the provided file structure. This is relevant for any web-facing components. Fix: If the application includes any webview components or backend services, implement appropriate security headers. - Low · No Visible Dependency Lock File —
Project root. No gradle.lock or similar dependency lock file is visible in the provided file structure, which means builds may not be fully reproducible. Fix: Enable and commit gradle.lock file to ensure reproducible builds and easier vulnerability tracking across versions.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.