Play/pause did nothing on the Android native video path — from the on-screen tap, from the control bar, and from a direct player_toggle invocation — while seek and skip kept working. That asymmetry was the whole clue: seek decides in player_seek_video, transport decides in toggle_playback. DR-195 is the cause. `html5_playing` is Rust's record of "a webview <video> is active and in this state", and toggle_playback/play/pause all route transport to that element whenever it is set. The player route mirrored element state into it UNCONDITIONALLY — from handleReportStart and, fatally, from handleReportProgress, which VideoPlayer calls on a 10-second interval. So on the native path the frontend re-declared every ten seconds that an element was playing when none existed, and every transport intent was emitted into the void. It also explains the flashing: the control bar and the JRay overlay both key off isPlaying, which was being contradicted on every tick. The mirror now lives in mirrorElementStateToRust() in VideoPlayer, gated on useHtml5Element — the only place that knows whether an element renders at all. The route cannot tell the paths apart, which is exactly how it came to lie. DR-193 hands transport authority back to the native backend when an item loads into it. Necessary but insufficient alone: the progress interval put the flag straight back, which is why the first device test after it still failed. DR-192 presents native video through a TextureView instead of a SurfaceView. A SurfaceView renders on its own layer outside the app window and punches a transparent region through it, and everything drawn above that hole — here, the entire Svelte UI — depends on that composition path. The overlay dropped its incremental damage: the DOM advanced (slider 476 -> 479 across three seconds) behind a screen showing neither, so the progress bar froze, controls would not fade and rotation lost the transport UI, while structural DOM changes got through, which is why the play overlay always appeared to work. It supersedes DR-191, which forced redraws in a loop and treated the symptom. DR-194 hides the video view across a resize and reveals it two frames later. A TextureView retains its last frame, so between a rotation and the re-fit landing that frame is stretched across the old rect and the previous frame flashes in what should be the letterbox bars. Verified on device (Honor ROD2-W09, Android 16) by driving ADB and reading the live DOM over the devtools socket: surface tap pauses (position frozen across 12 seconds, overlay raised, transport flipped) and resumes; the control bar does both. UT-189 drives the real 10-second interval under fake timers — an earlier version asserted on a freshly mounted player, passed with the guard deleted, and guarded nothing. Still open, and deliberately not claimed: DR-192's effect on the overlay repaint is unverified on device, DR-194's letterbox reset is untested, and the native default (DR-188) stays off pending DR-190, the background-audio return.
Development Scripts
Collection of utility scripts for building, testing, and deploying JellyTau.
Testing Scripts
test-all.sh
Run all tests (frontend + Rust backend).
./scripts/test-all.sh
test-frontend.sh
Run frontend tests only.
./scripts/test-frontend.sh # Run all tests
./scripts/test-frontend.sh --watch # Watch mode
./scripts/test-frontend.sh --ui # Open UI
test-rust.sh
Run Rust tests only.
./scripts/test-rust.sh # Run all tests
./scripts/test-rust.sh -- --nocapture # Show println! output
Android Scripts
build-android.sh
Build the Android APK.
./scripts/build-android.sh # Debug build
./scripts/build-android.sh release # Release build
deploy-android.sh
Install APK on connected Android device.
./scripts/deploy-android.sh # Deploy debug APK
./scripts/deploy-android.sh release # Deploy release APK
build-and-deploy.sh
Build and deploy in one command.
./scripts/build-and-deploy.sh # Build + deploy debug
./scripts/build-and-deploy.sh release # Build + deploy release
check-android.sh
Check Android development environment setup.
./scripts/check-android.sh
logcat.sh
View Android logcat filtered for the app.
./scripts/logcat.sh
Traceability & Documentation
extract-traces.ts
Extract requirement IDs (TRACES) from source code and generate a traceability matrix mapping requirements to implementation locations.
bun run traces # Generate markdown report
bun run traces:json # Generate JSON report
bun run traces:markdown # Save to docs/traceability.md
bun run traces:coverage # Coverage gate — exits non-zero below 50%
The script scans all TypeScript, Svelte, and Rust files (plus scripts/)
looking for TRACES: comments and generates a comprehensive mapping of:
- Which code files implement which requirements
- Line numbers and code context
- Coverage summary by requirement type (UR, IR, DR, JA)
bun run traces:coverage is the supported way to check requirement coverage
locally — it runs the same computation CI does. Coverage denominators are
derived from docs/requirements.md at run time; they are never hardcoded. An ID
that appears in a TRACES: comment but is not defined in requirements.md is
reported as orphaned and does not count toward coverage (see DR-093).
Removed:
check-req-coverage.sh,check-test-coverage.sh, andfind-req-implementations.shwere deleted in July 2026. They read an undocumented@req:tag convention parallel toTRACES:, greppedsrc-tauri/unscoped (hanging on ~40 GB oftarget/artifacts), and in one case reported "all requirements implemented" from an empty result set.extract-traces.tsis the single source of truth for requirement coverage. See docs/specs/req-coverage-script-removal.md.
Example TRACES comment in code:
// TRACES: UR-005, UR-026 | DR-029
function handlePlayback() { ... }
See docs/traceability.md for the latest generated mapping.
CI/CD Validation
The traceability system is integrated with Gitea Actions CI/CD:
- Automatically validates TRACES on every push and pull request
- Enforces minimum 50% coverage threshold
- Warns if new code lacks TRACES comments
- Generates traceability reports automatically
For details, see:
- Traceability CI Guide - Full CI/CD documentation
- TRACES Quick Reference - Quick guide for adding TRACES
Utility Scripts
clean.sh
Clean all build artifacts.
./scripts/clean.sh
NPM Script Aliases
You can also run these via npm/bun:
bun run test:all # All tests
bun run test:rust # Rust tests
bun run android:build # Build Android APK
bun run android:deploy # Deploy to device
bun run android:dev # Build + deploy debug
bun run android:check # Check environment
bun run clean # Clean artifacts