Each successfully tested merge to main publishes GitHub and F-Droid APKs for TV and touchscreen devices, the optional collection archive, release metadata and one full Native core AAR, then updates Milkbeat through the canonical core alias. Use a feature branch and PR for changes. Android APK CI retains full Git history and tags with filter: blob:none; current source and historical version metadata are fetched on demand rather than downloading every historical evidence blob.
- Include a substantive
## Release notessection in every PR body. Follow.github/pull_request_template.md. - Write for people using the app: describe the changed behavior, its effect, and relevant limits. Include a concrete trigger or before/after example when useful.
- State only changes supported by the final diff and verified evidence. Avoid invented performance figures, broad crash-free claims, or promises beyond the implementation.
- Keep release notes aligned with the final PR scope. Rewrite them when implementation changes.
- Use an
Internalsubsection for documentation, tests, or CI-only work with no user-visible effect, and explain the actual change. A bare “No user-visible changes” is insufficient. - Put test commands/results in
## Validation, outside release notes. Omit badges, agent transcripts, implementation process, and manual install instructions from the release-notes section. - Let CI append the platform-specific Downloader codes, APK/core AAR links, checksums and comparison link. Update the matching canonical TV or phone/tablet install blockquote in
README.mdwhen its Downloader code changes. - Preserve upstream attribution when describing backported fixes.
- Do not bump versions in routine PRs. The base version/code/commit in
app/build.gradledefine the automatic release sequence. Change them together only for a planned new release line; followdocs/RELEASING.md.
For release tooling changes, install tools/fdroid/requirements.txt in a task-specific virtual environment, then run its Python with -m unittest discover -s .github/scripts/tests -v (includes pinned F-Droid importer/discovery/sync checks). CI uses the same pinned environment. Reviewed PR builds reuse workflow definitions from trusted main; introduce new test dependencies there before PR sources require them. Review factual accuracy of the PR release notes: CI validates their presence and placeholders, not the truth of English prose.
Keep the repository-specific sections below current whenever a task changes architecture, commands, dependencies, documentation or constraints, in the same worktree and PR as that task. Record only facts backed by repository files or observed command results; mark unverified commands and unresolved facts explicitly, and link to existing docs instead of duplicating them. Do not import assumptions from other repositories (including Milkbeat).
Historical upstream rebase checkpoint before main synchronization: 2026-10-06, pin 6f6480746 (unreleased projectM 4.2 master), evaluator 22fb0cfd. Three consolidated patches apply; 329 host controls, 21 native sanitizer controls and 84 app/core JVM tests pass. Both ARM core/profile builds and a fresh recursive 85ceac83 debug-core/release-APK build pass. Milkbeat 6802630c builds against the exact migration AAR and passes 28 focused consumer tests. Strict MkDocs and the AM6 three-line engine label check pass. The full frozen-audio TV matrix, final CI/GitHub Codex review, merge, publication and automatic Milkbeat update remain open; these local checks are not whole-corpus fidelity or performance claims.
Resolution selector maintenance (2026-10-06): resolution_mode is the new opt-in app preference. Keep legacy setMode Auto-normalizing for Milkbeat; use setResolutionMode for explicit selections. Validate FPS stability, memory relief/recovery, pressure-cleanup signaling inside the height callback, display bounds and separate Auto history. Advanced is a framework ScrollView with D-pad focus scrolling; keep all rows within the overscan-safe bounds. Run the isolated resolution/trails setup cases with ./gradlew :app:assembleDebug :app:assembleDebugAndroidTest -PsetupScreenshotTest then adb -s SERIAL shell am instrument --user USER -w -e setup_case resolution nl.neerdael.projectmtv.setuptest.test/com.example.projectm.visualizer.MusicCategoryInstrumentation (or native_trails). Optional setup_case resolution_recording records the actual menu at 4K with synthetic unsigned mono PCM; wait for resolution-recording.ready in the test app external cache, start the recorder, then create resolution-recording.go. Requires a 4K display and ample RAM; verified on the task-owned API36 TV emulator with 8 GB guest RAM and Apple M4 Pro/Metal host GPU 2026-10-06. Verified menu/trails cases on the task-owned API36 TV emulator 2026-10-06; 101 JVM tests and strict MkDocs pass.
CI/review workflow discovery: 2026-10-05 against main at 910e837b; JDK 21.0.11, resolved JUnit 4.13.2/Hamcrest 1.3, release JVM tests, release APK/Native AAR and strict MkDocs build verified. Last full discovery: 2026-10-04, against main at 4fc66208 (projectM patch series 0001–0029). "Verified" below means the command was run with the stated result on that date; everything else is described from the source files and CI configuration.
Latest integration update (2026-10-07): PR #51 merged as 16a37189478771da591d134a6bd1f8fc46eee808 and published v2.3.17, unchanged AAR SHA256 4bdd2ddd4ba249664f59a042d3b0eaf7f6bd4b643d2d126c609b2cc1d753ab9e. Historical 0051 is ported as current 0011: compute sine/cosine after float conversion, reuse the sine component and supply cosine at attribute 8 through an instance-owned VertexBuffer. Preserve prepared-mesh replay and authored/nonfinite equation values. The user explicitly limits this release migration to bug-specific validation; do not restart the complete preset matrix. The frozen v2.3.16 matrix keeps its original workers/artifacts/helpers and completes once as a separate pre-rotation checkpoint. Focused host rotation/zoom controls pass with ASan/UBSan; final Android witness checks, CI/review and publication remain open.
Historical integration update (2026-10-07): main PR #50 merged as dd59a791 and published v2.3.16. Its verified unchanged AAR SHA256 is 08e5a1c9c3df1433407769ace6e8fc379566d50402258eef98fe948cb5f708cd. Custom-pack app/JNI changes are integrated; historical patch 0050 is ported as current 0010. The v2.3.15 matrix below remains an immutable completed checkpoint. It does not certify the new renderer or latest baseline; 134 JVM tests, 31 normal and 31 sanitizer renderer controls, both-ABI builds and custom-pack upload/cold restart pass. Fresh source/runtime/image validation is running; final review/CI remain required. See synchronization evidence.
Resumed migration validation (2026-10-06): main 43023889/PR #49 is merged, built and published as v2.3.15; its verified unchanged AAR SHA256 is fa4bdd657a592b41eeef7d75c82982bf1fecf5404b99aba8ebba5c56f6a91327. Candidate renderer source c3872be3 uses nine patches, including the legacy texture-byte compatibility repair. 329 host controls, 30 native controls in normal and ASan/UBSan builds, 101 JVM tests, both ABI Android builds, 28 exact-AAR Milkbeat consumer tests and strict MkDocs pass. Resolution and Native trails setup journeys pass after RECORD_AUDIO grant and a cold .setuptest launch. Final frozen validation passed 2026-10-07: 509 preset/profile comparisons across 448 unique presets (2,036 source runs), 509 unchanged-AAR runtime runs and 48 source image-proof replays. All source RGB frames 0–479 match across engines/repeats with seed 12345; alpha is excluded. See completed evidence. Final-head review/CI/merge/publication remain separate gates.
For this resumed task, the user waives TV validation in favor of the task-owned API34 ARM64 emulator-5624, launched with host GPU/HVF acceleration and reporting Apple M4 Pro/GLES3.0. The API36 guest produced a shader-binary metadata export error; API34 passes strict GL checks without weakening them. Floating-color extensions are available for the RG16F UV target on this backend. Do not operate unrelated devices. Freeze renderer/SDK/fingerprint, source/artifact/asset/PCM hashes and all original issue profiles. Use the release-bound random/regression workflow; no helper/worker mutations during a frozen run. Original MilkDrop2 reference: /Users/jneerdael/Scripts/milkdrop2_v2.25c_OPEN_SOURCED_20130514_orig_code.
Custom upload maintenance (2026-10-07): QR screenshots decode to the current endpoint at 1920×1080/320dpi and 1280×720/213dpi; the 50,000-preset + PNG replacement journey and cold restart pass on the isolated API36 ARM64 TV emulator. Retain pack files until native active/outgoing/loading/prewarm leases retire. At nonstandard 1280×720/320dpi (640×360dp), the pre-existing main settings panel clips lower rows; this remains UI debt. No physical-TV or API21 device coverage was run.
Automatic memory maintenance (2026-10-08): devices with at least 3,584 MiB kernel-visible RAM use Android threshold + 128 MiB instead of the total-RAM/5 reserve floor; smaller devices retain the conservative floor. Preserve the 64 MiB recovery margin, resize overlap, two-preset/trails estimates, lowMemory checks and completed-allocation/generation rules. Use aggregate ActivityManager memory and FPS; no root, explicit background-process cleanup, per-app memory inventory or additional setting is introduced. Do not add cached PSS to available RAM or subtract resident music-player RAM twice. See memory policy. Validation status belongs in that task’s evidence and PR; no new device-performance claim is recorded here.
ProjectM TV is a music visualizer for Android TV, powered by ProjectM TV Engine, the maintained projectM fork based on unreleased projectM 4.2 master (commit 6f6480746) plus this repository's patch series. It renders MilkDrop presets that react to the audio another app plays on the same TV; it is not a music player. GitHub APKs and the canonical AAR bundle 9,606 Cream of the Crop presets; F-Droid APKs generate a 20-preset starter subset and offer an explicit full-collection download. GitHub: johnneerdael/ProjectM-Android (rename verified 2026-10-12 with gh repo view; old ProjectM-TV/projectm-android-tv URLs redirect).
:app |
:core |
|
|---|---|---|
| Type | Android application (APK) | Android library (AAR), the engine |
| Namespace / Java package | com.example.projectm.visualizer (legacy package; the installed ID differs) |
nl.neerdael.projectm.core |
| Application ID | nl.neerdael.projectmtv (since 1.9.7); suffixes .profile, .presettest, .setuptest for side-by-side builds |
– |
| Languages | Java (source/target 1.8), XML views | Java + C++17 (JNI), CMake; builds libprojectmtv.so |
| SDK levels | compileSdk 34, targetSdk 34, minSdk 21 | compileSdk 34, minSdk 21; ABIs armeabi-v7a, arm64-v8a |
| Dependencies | project(':core'); test: JUnit 4.13.2. ZXing Core 3.5.4 for QR encoding; no AndroidX, Kotlin or Compose. Framework Views remain the UI toolkit |
test: JUnit 4.13.2; projectM built from source |
- Devices: Default TV APK requires Leanback;
-PandroidTouchscreen=truebuilds ProjectM Android (nl.neerdael.projectm) with optional Leanback, required touchscreen and regular Launcher only. Both need OpenGL ES 3.0/audio output and API21+. Build outputs share paths; stage each APK/mapping before switching the property. See touchscreen build. - Build types:
debug;release(R8 minify + resource shrinking, release key whenSIGNING_KEYSTORE_PATHis set, otherwise the local debug key);profile(initWith release, debug key,.profilesuffix,profileableviaapp/src/profile/AndroidManifest.xml). Gradle properties-PpresetLabDeviceTest/-PsetupScreenshotTestgive the debug build the.presettest/.setuptestsuffix and a distinct app name. - Entry points:
ProjectMApplication(one-time preference migrations,ProjectMCore.init);MainActivity(singlesingleTasklandscape activity: UI, remote keys, audio capture, settings panels);TrackListenerService(notification listener used only for media sessions);Updater+UpdateFileProvider(GitHub auto-update defaults On unless explicitly saved Off); F-Droid uses a compiled no-op updater and excludes the installer provider/permission. Engine:ProjectMCore,ProjectMJNI,VisualizerView,VisualizerRenderer,QualityController,DeviceProfile,DisplayInfo; nativecore/src/main/cpp/native-lib.cppplussnapshot_fade.cppandpreset_prewarm.cpp. - projectM relationship: submodule
third_party/projectmtracks upstreamhttps://github.com/projectM-visualizer/projectm.git, pinned to unreleased projectM 4.2 master commit6f64807467e312034883a4389e6aa80a675458bc(CMake numeric version4.2.0, not an upstream release). All retained engine changes are the ordered patch seriestools/projectm-patches/NNNN-*.patch, applied at CMake configure time bycore/src/main/cpp/CMakeLists.txtand linked statically intolibprojectmtv.so. The first 44 historical patches are consolidated into0001-tv-rendering-and-preset-compatibility.patch,0002-hlsl-compatibility-and-float-roundtrip.patchand0003-evaluator-thread-local-rand-and-lone-dot.patch; consult docs/UPSTREAM_PATCH_VALUE.md for the current-series assessment and image evidence; the linked archived assessment preserves the historical retained/upstream/obsolete mapping. Historical 0045–0049 are ported as current 0004–0008 (shape sampling, blur bounds, signed zoom and live wave/display controls). Current 0009 preserves the released SOIL2 user-texture premultiplication bytes in the 4.2 stbi upload; Current 0010 ports historical 0050 for per-preset texture lookup across custom-pack switches; Current 0011 ports historical 0051 for CPU warp rotation trig through 4.2 separate vertex buffers. Current 0012 aligns custom shape fills/outlines with D3D9 pixel centres per actual authored/native target; restore both shared shader matrices after each batch and preserve evaluated positions, radii, colours and UVs. Current 0013 removes the redundant composite UV half-texel bias; same-size pass-through must copy feedback texels without extra blur. Validate patch application and focused rotation controls after synchronization. The personal forkjohnneerdael/projectmis not referenced by the build; it is used for upstream PRs. - App/core boundary and Milkbeat:
:appholds UI, audio capture, track titles and the updater;:coreholds the engine, JNI, presets, textures and preset indexes. Milkbeat consumes the released core AAR: CI publishes the single NativeprojectM-TV-core-<version>.aarand canonical alias, then dispatchesprojectm-core-releaseto Milkbeat (see Generated artifacts). Milkbeat consumes canonical artifact names and uses QualityController; managed hosts acknowledge context/configuration generations and publish a coherent size/trails/transition tuple after live RAM review. Retired fixed-resolution and static-RAM methods normalize to Auto. - Build tasks:
./gradlew assembleRelease(APK atapp/build/outputs/apk/release/app-release.apkand AAR atcore/build/outputs/aar/core-release.aar),./gradlew :core:assembleRelease,./gradlew assembleDebug,./gradlew assembleProfile. - Single Native core:
:coreand the APK use Native rendering; canonicalprojectM-TV-core[-<version>].aarnames contain Native bytes. The separate capped/core-native artifacts are retired for new releases; preserve historical releases. Deprecated-PprojectmCoreRenderingPolicy=nativeremains accepted;cappedis rejected. QualityController defaults to Auto up to the physical panel and uses live FPS/memory headroom. Its fixed-mode/static-RAM compatibility methods normalize to Auto. The additive resolution selector leaves legacysetModeAuto-normalizing for existing consumers. JNI defaults Standard trails; settings are additive (setNativeTrails,getNativeTrailsStatus). The underlying projectM C API retains explicit-off compatibility controls. Seedocs/RELEASING.md.
Renderer line ownership (patch0037): reusable colored points, LineBatch, LineRenderer and generic line shaders live in Renderer; reference/sample/fade/dot/canvas/thick-pass policies remain in MilkdropPreset. Line strips and motion-vector quads use Mesh's streamed instance buffer, stored custom attributes/divisors and byte offsets; submit thick-pass groups while bound and change strip offsets without repeating enable/divisor setup. Preserve preset-construction/prewarm shader compilation, cached uniform locations, per-instance styles, prepared Native-trails replay and packed/floating motion-UV consumers. Do not replace these Android paths with the upstream PR's lazy shared shader preparation. See patch0037 and its linked validation evidence. Updating docs/THIRD_PARTY.md changes a notice file included in the official collection archive; regenerate tools/preset-distribution/official-archive.json with python3 tools/preset_distribution.py lock-archive --root . --output tools/preset-distribution/official-archive.json, then validate archive generation and python3 -m unittest tools/test_preset_distribution.py -v.
The focused tools/milk-analyzer subset imports PR #25's selector-domain proof and
models the native implicit-global policy. It also includes the separate beta activity
scorer using the published standard core AAR through JNI, and a source-bound collection
exporter/verifier. Its direct-delta beta model is fitted on eight historical user
judgments transferred onto native features; archived producer and derived-scoring
identities are kept separate. Optional offline refitting uses
tools/milk-analyzer/requirements-calibration.txt. These execution-based predictions are not independent source-only
visual forecasts. Build its source adapters against a hash-
identified host engine before python -m pytest tools/milk-analyzer -q; Preset Lab CI
performs this setup. Its results are source diagnostics, not visual certification.
Visual patch catalog maintenance (2026-10-09): the 18 audit repairs in 0017–0033 have native Apple M4 Pro master/main image pairs, with explicit synthetic diagnostics and matched CSS zooms. Preserve their exact source/worker/audio/dimension identities and distinguish whole-series comparison from isolated repair causality. Royal103 actual-width4K is separate from production Standard-trails4K. Use catalog evidence and its bounded image verifier; no physical-TV performance or Windows-pixel claim follows.
Pending branch ownership reuses the complete commit graph from the guarded GitLab comparison and follows first-parent ancestry. Preserve missing-head/history, metadata-only diff, fingerprint and exact-head push checks; a path-filtered 100-commit API history walk timed out twice on real fdroiddata.
-PprojectmDistribution=fdroid creates exactly one named flavor: fdroidTv, or fdroidAndroid with -PandroidTouchscreen=true. Default GitHub builds remain unflavored. App Java sources come from app/src/github/java or app/src/fdroid/java; the F-Droid manifest removes REQUEST_INSTALL_PACKAGES and UpdateFileProvider. Core starter assets are generated offline from tools/preset-distribution/starter-presets.json, keeping original file hashes, weights and mood memberships. The source corpus and default full AAR remain intact.
OfficialPresetPack and OfficialPresetDownload reuse the bounded custom-pack extractor, validate the APK-bound HTTPS archive hash/manifest, and publish an independent no-backup official slot. Native current/outgoing/loading/prewarm/search-path readers retain directory leases. Staging holds CustomPresetPack.STORE_LOCK from extraction through native ownership and pointer publication so cleanup cannot remove an in-progress generation. A timed-out queued GL publication is canceled before rollback; publication already running settles before its result is reported. Startup removes interrupted download.zip/current.tmp under the storage lock. Startup marks native official restoration pending before asynchronous file validation, and clears it in finally after restore/failure; saved mood application and fallback persistence wait for this interval. Invalid content falls back to starters. A process-owned job announces final states only after cleanup makes retry safe; UI listeners attach while resumed. The 20 starters use no external textures. org.json:json:20260814 is a F-Droid host-test dependency only; runtime JSON uses Android's platform API.
CI stages and checks the full 9,606-preset/74-texture AAR before F-Droid builds overwrite local core outputs. Four APKs use the same source/version/code/signing identity; the generator embeds a committed archive lock, and publication requires qualified starters. Keep Downloader 4821216/6565691 and projectM-TV.apk / projectM-Android.apk aliases stable. F-Droid aliases add -FDroid. See release tooling and F-Droid setup.
tools/fdroid/requirements.txt pins fdroiddata merge-request CI's fdroidserver master commit and ruamel.yaml 0.18.10: fdroidserver 2.4.5's ruamel <0.17.22 writer kept long Binaries/UpdateCheckData on the key line, and fdroiddata's fdroid rewritemeta job failed both submissions (2026-10-12, reproduced locally). Its parser/writer, rewritemeta, importer and actual checkupdates CLI are covered by tooling tests. Tag discovery (UpdateCheckMode: Tags, AutoUpdateMode: Version) reads each release tag's gradle.properties; UpdateCheckName: Ignore avoids fdroidserver's observed inability to resolve conditional Gradle IDs/phone labels. New merge requests use fdroiddata's App inclusion template. Source scanning removes docs/superpowers/evidence and tools/milk-analyzer/export-contract/knowledge-pack/golden-v1 from the build checkout. The latter contains compressed host-test vectors rejected by the F-Droid scanner before APK assembly; tooling tests exercise the real scanner before/after cleanup for both generated and local recipes. Local validation recipes target v2.3.48 so both removal paths exist in the selected source. Sync uses trusted main, an API/repository-capable FDROID_GITLAB_TOKEN, verified metadata-only branches, current upstream master and atomic expected-head Git pushes; it preserves maintainer fields and accepted build history (a managed-field edit is adopted only when it equals the current generator's output for the checkpointed release, whose version/code come from that tag's gradle.properties, e.g. an applied review suggestion mirrored in fdroid_metadata.py), and posts no discussion comments. Recovery revalidates the latest complete release. Public delivery/signature and live pipeline results remain separate from local/mock evidence.
Framework setup cases fdroid_starters, fdroid_pack, fdroid_pack_restart and fdroid_pack_removed run only in isolated F-Droid .setuptest APKs. The pack case uses a host-pushed archive via an explicitly injected response adapter; it exercises real validation, extraction, consent and native publication. Phone uses actual screen-coordinate touch injection; TV uses D-pad focus. Keep evidence bound to APK/test APK, preset/descriptor/archive/source hashes and device fingerprints. See distribution evidence.
-
I11 candidate0024 (2026-10-08): actual legacy/default mesh uses the original physical AD diagonal; compiled custom keeps BC and failed custom uses legacy. Select six offsets once, keep one existing index buffer, winding/quadrant/count/replay contracts, and include compiled-path identity in cached topology. Real corner-field GL/indices/affine/path-change control RED→GREEN. Initial Native4K/custom-path proof passes but initial cost+4.337% is not accepted. Revised complete viewport/aspect/path static mesh cache passes49 controls and fresh24-patch application; missing-aspect ablation fails. Revised native equivalence/cost and integration pending. See I11.
-
Negative warp powers (2026-10-08): patch0015 supplies original CPU negative effective zoom through instance-owned attribute9, after per-pixel/per-frame float conversion. Preserve raw equation values, positive GPU arithmetic, nonfinite fractional results and prepared replay; no epsilon, absolute value or NaN-to-zero policy. Exact Tulip source
c897d686…retains 33 NaNs/frame and its 30-frame published/candidate replay is byte-identical. Defined nested exponents1/2/3 fail baseline UV controls and pass after correction. See bounded evidence. Current warp uses eight active attributes and four extra bytes/vertex. Normal/sanitizer renderer37/37 and JVM137 pass; 329 host controls, fresh recursive both-ABI debug core and strict MkDocs also pass. External review/CI/merge/publication gates remain separate until recorded. Do not clear the source predictor's unresolved fractional-power guard or resume its shared random run from this evidence. -
Mesh initialization cache (2026-10-08): patch 0016 stores the viewport size in
PerPixelMesh/FinalComposite, so their static mesh data (and the warp mesh's vertex/index upload) is built once per size or grid change instead of every frame. Image output is unchanged; on the AM6 (Android 9, 32-bit) the pinned$$$ Royal - Mashup (102).milkrun at 1080p/30 fps used about 12% less process CPU (two runs per side, silence; GPU time unmeasured). Evidence:docs/superpowers/evidence/mesh-init-cache/README.md. Profile APKs: the installable file isapp/build/outputs/apk/profile/app-profile.apk(app/build/intermediates/apk/profile/can be stale); install withadb install -t. -
I10 candidate0023 (2026-10-08): compile-time legacy/default warp VS variant changes only the four oscillator-Y signs, with the same arithmetic/trig/pass counts. Custom warp keeps its current producer; actual failed-custom compilation uses the corrected default. Preserve CPU trig, negative-power attributes, equations, topology/traversal and prepared replay. Production vertex transform-feedback regression RED→GREEN and47 normal controls pass;23 patches apply. Native4K original/finite/cost qualification and integration remain pending. See I10.
-
MilkDrop audit checkpoint (2026-10-08): the33-entry ledger preserves baseline120547f3 (15 patches), verifies all9,606 preset hashes and adds source repairs0016–0022 for I17/I08/I22/I31/I09/I05/I06/I29. The normal renderer suite passes46/46 after correcting a fractional negative-orientation fixture. Source-specific tests preserve opacity policy, bounds-safe custom-wave windows, discrete dots/single-dot NaN semantics, gamma-only epsilon, original named-constant decimals, inverse per-pixel aspect, fresh wave-point host inputs and signed odd echo flips. I19's separate source-cap proposal remains deferred for measured +.158ms/2.56% clean4K cost; I22 retains a mosaic timing outlier requiring disposition. Final sanitizer/Android/host/JVM/review/CI/integration gates remain open. See audit ledger.
-
Capture maintenance (2026-10-08): Native direct rendering can restore only the draw framebuffer. The corpus worker explicitly binds GL_READ_FRAMEBUFFER0 for glReadPixels, records the prior/capture binding and restores the prior binding; the host rejects absent/nonzero capture bindings. Historical audit PNGs are intermediate-stage evidence. Fresh exact-AAR/preset worker APKs completed32 focused original480-frame final-output runs with exact selected RGB repeats; echo's four corrected diagnostic runs prove the final horizontal flip. All28 finite diagnostics (including echo) and24 additional I19 resolution-band runs also completed with exact selected RGB repeats. Source-correct I19 authored1280/Native4K final mean RGB154.689/154.714 at frame239; defer for measured extra render cost rather than inferred brightness fidelity loss. The capture guard suite passes57 tests/20 subtests; archived protocols need their original helper. This is focused emulator evidence, not full-corpus or physical-TV performance certification. See capture correction.
-
No
.codegraph/orgraphify-out/exists at the repository root (checked 2026-10-04). Usegit grep/rg; do not assume a code graph. -
Architecture: docs/ARCHITECTURE.md §5 (threading rules, transitions, resolution, frame pacing, threads, overlay UI, device tiers). Its title says v1.9 and §1–4 and §6–8 are historical analysis; verify against the code. Design specs, plans and evidence for engine work are in
docs/superpowers/{specs,plans,evidence}. -
projectM sources:
third_party/projectmshows patched code only after a CMake configure or a manual apply; the committed source of truth istools/projectm-patches/. Search both the submodule and the patches. -
Logs for tracing behavior: native tag
projectM-Native(LOAD,PREWARM,TRANSITION,OUTPUTlines per switch);VisualizerRendererlogsSTATS fps=… surface=… audio=…every 5 s. -
Committed generated data indexes (not code indexes):
core/src/main/assets/presets.idx(regenerate withtools/gen-preset-index.pywhenever presets or textures change; CI enforces--check) and the collection bundle incore/src/main/assets/preset-genres/(current beta schema2 produced/verified withtools/milk-analyzer/beta_export.py; legacy schema1 Preset Lab imports are historical).
Follow the project's established design system and platform conventions. Reuse existing theme tokens and components. Preserve accessibility, keyboard/focus behavior, responsiveness, and supported input methods. Avoid introducing decorative styles or changing appearance incidentally during a refactor.
- Toolkit: framework Views and XML only (no AndroidX, Leanback library, Material or Compose). One layout,
app/src/main/res/layout/activity_main.xml. Framework themesTheme.Leanback/Theme.Touchscreen, both based onTheme.ProjectM, inres/values/themes.xml(parent@android:style/Theme.Black.NoTitleBar.Fullscreen); text stylesOverlay.*inres/values/styles.xml; tokens inres/values/colors.xmlanddimens.xml; drawables inres/drawable*. Reusable views:OptionRow(focusable settings row),AudioMeterView,TrackCorner. Dialogs useandroid.R.style.Theme_DeviceDefault_Dialog_Alert. - Remote/D-pad contract (
MainActivity.onKeyDown/dispatchKeyEvent; documented in the README Remote control table anddocs/user-guide/controls.md; keep all three in sync):- No panel: Right/Next/Fast forward = random preset (hard cut); Left/Previous/Rewind = previous preset; Up/Down/Info = show the track again; Center/Enter/Menu = open the panel; Back exits.
- Panel open: Up/Down move focus between rows, Left/Right change a value, Center cycles or runs an action; Back closes (from Advanced or Track display it returns to the main panel); Menu closes; it hides after 10 s without input, and every key event restarts that timer.
- Touchscreen contract: surface swipe Right/Left matches remote random/previous hard cuts; single tap opens/closes the menu. Phone-only Audio input selects Microphone (activity-owned AudioRecord) or Phone playback (API29+ consented AudioPlaybackCapture in a mediaProjection foreground service); both stop on pause and use the existing unsigned mono core input. Validate
setup_case phone_audioon Android10+. Panels scroll vertically, rows cycle on tap, and touch close/back controls retain D-pad access. The timer is suspended during touch and renewed on release; the separate diagnostics card stays TV-only. Validate withsetup_case touchscreenin the isolated-PandroidTouchscreen=true -PsetupScreenshotTestbuild. - Layout: landscape (sensor-landscape for touchscreen), fullscreen, immersive sticky. The GL surface renders at its own size (
SurfaceHolder.setFixedSize) and is scaled by the display; the overlay UI uses the UI resolution. - Accessibility: no documented requirements and no TalkBack verification found; the layout has only three
contentDescriptionattributes. Keep every new control reachable by D-pad focus and give icon-only controls acontentDescription. - Visual references: user-guide app screenshots (
docs/user-guide/images/setup/,images/gallery/) were refreshed 2026-10-08 from released v2.3.23 on a task-owned API36 Android TV emulator at 1080p, with music from Milkbeat; the Android 9 settings-path captures (android-apps.png,special-app-access.png,notification-confirmation.png,track-display.png) predate them and are labelled. Patch proof images inimages/patches/are compressed copies of the current-patch-proof captures. Rendering changes need before/after captures (PR template).
Before implementing a component, parser, formatter, scheduler, transport, or similar utility:
- Check existing project code for a suitable implementation.
- Check the declared and resolved dependencies for a supported API.
- Verify the API and recommended usage against the version in use and official documentation.
- Implement a custom alternative only when the existing options are absent or unsuitable, and record the reason in the PR.
Prefer configuration, composition, or a small wrapper to copied library source or overlapping dependencies. Use maintained implementations for security-sensitive primitives.
| Need | Existing API / implementation | Version truth | Constraints |
|---|---|---|---|
| TV audio input | android.media.audiofx.Visualizer on the player's session (PlayerSessionFinder), on the AudioCapture HandlerThread → ProjectMJNI.addWaveform |
framework, minSdk 21 | 8-bit mono; needs RECORD_AUDIO; no microphone use |
| Phone audio input | AudioRecord microphone or API29+ AudioPlaybackCaptureConfiguration / MediaProjection (PhoneAudioInput, PhonePlaybackService, PhonePcmCapture) → unsigned mono ProjectMJNI.addWaveform |
framework, compileSdk34 | Foreground-only; Record audio permission, fresh playback consent per session; source player must allow capture |
| Playing track | MediaSessionManager via TrackListenerService / TrackWatcher |
framework | needs notification access; no notification content is read |
| Rendering | GLSurfaceView + GLES 3.0 + projectM C API via JNI |
third_party/projectm immutable commit + tools/projectm-patches/ |
projectM handle only on the GL thread |
| Frame pacing | Choreographer in VisualizerView |
framework | |
| Settings | SharedPreferences file projectm_settings |
framework | see Dependencies, state, and lifecycle |
| Knowledge pack reading | PresetKnowledgePack with package-private Json (strict RFC 8259), java.util.zip, java.security.MessageDigest |
JDK/framework | org.json is an Android framework class that JVM unit tests stub (returnDefaultValues), and a JSON dependency would break the small-APK policy; contract and golden vectors in tools/milk-analyzer/export-contract/knowledge-pack/ |
| ZIP import / LAN upload | ZipFile, ServerSocket, SecureRandom and the bounded PresetPackUploadServer |
Android/Java platform | One session/import; immutable no-backup generations, CRC/size checks and native reader leases |
| Upload QR | ZXing Core QRCodeWriter, plain black/white pixels with four-module quiet zone |
app/build.gradle (3.5.4) |
Encoder-only UI; no camera/scanner integration or new permission |
| Auto-update | GitHub source-set Updater + UpdateFileProvider; F-Droid compiled stub |
framework | GitHub defaults On, explicit Off preserved; F-Droid has no installer/provider/permission |
| Engine fixes | new patch in tools/projectm-patches/ |
patch series | never edit committed submodule files |
| Preset analysis | tools/preset-lab (numpy, opencv-python-headless) |
tools/preset-lab/pyproject.toml, requirements.lock |
offline only; does not change the Android renderer |
| Docs site | MkDocs with the Material theme (static GitHub Pages build) | docs/site-requirements.txt (mkdocs==1.6.1, mkdocs-material==9.7.7) |
Keep MkDocs on 1.6.x: Material warns that MkDocs 2.0 removes plugins/theme overrides; dark slate palette and overrides live in docs/user-guide/stylesheets/projectm.css |
| Build toolchain | AGP 8.12.0, Gradle 8.14.2, NDK 27.3.13750724, CMake 3.22.1 | build.gradle, gradle/wrapper/gradle-wrapper.properties, app/build.gradle, core/build.gradle |
no version catalog |
Adding AndroidX or another runtime dependency departs from the documented small-APK policy (app/build.gradle) and affects F-Droid reproducibility; justify it in the PR.
Avoid blocking work on latency-sensitive threads, unnecessary polling, duplicate requests, unbounded concurrency, and background work that outlives its owner. Honor existing cache, cancellation, visibility, lifecycle, and resource-release contracts. Back performance claims with measurements and state what was not measured.
- Threads (ARCHITECTURE §5 Threads): GL thread (
THREAD_PRIORITY_DISPLAY) owns the projectM handle (create, render, load, settings) and output measurement;AudioCaptureHandlerThread (THREAD_PRIORITY_AUDIO); a native worker for preset indexing and prefetch; a background shader-compilestd::threadstarted from the GL thread (preset_prewarm); the UI polls status every 500 ms. Other entry points only write atomics or mutex-protected buffers; keep it that way. - Visibility and cleanup:
MainActivity.onPausestops the track watcher, UI refresh, updater and audio, then callsvisualizerView.onPause();onResumerestarts them and re-checks notification access.onDestroyreleases projectM on the GL thread (queueEvent(renderer::release)); after context loss the nextonSurfaceCreatedcleans up. - Frame pacing/quality: half-refresh-rate pacing uses Choreographer. QualityController defaults to Auto up to the detected panel, with FPS hysteresis/CPU-bound probe checks and a live ActivityManager memory budget initialized from application context in ProjectMCore.init. Standard is the Native trails default; Medium/High add the same passes with different gain. Advanced › Resolution uses additive
resolutionModes,validResolutionModeandsetResolutionModefor Auto/fixed/Native; fixed modes bypass FPS adaptation/slow-preset skipping but retain live memory relief/recovery.resolution_modedefaults to0 and is separate from retiredrender_height. Mode changes require a matching completed-frame height and no pending allocation before taking resident credit; reduced modes and fixed/Native recovery resizes remain pending until the replacement frame. A positive FPS sample acknowledges its received height before memory review can request another size. Capture the confirmed resident allocation before editing height: growth reviews the delta, confirmed non-growth waits for the new rendered tuple before memory sampling, and startup/recreated/pending allocations receive no resident credit. No static-RAM control remains. UseQualityController.setRenderAllocationSettings(trails, transitionSeconds)to review the final tuple atomically. The controller captures the pre-edit allocation before height callbacks. After renewing the FPS generation, userevalidateForAllocationChange(): confirmed reductions defer memory sampling until the new tuple renders and releases old textures, while net growth and pending allocations require a full review. Managed clients publish dimensions/trails/transition as one generation-tagged rendering configuration; GL holds allocation settings until surface-size acknowledgement. UserevalidateForResumefor preserved/context-recreated resumes, reject stale generations, and base FPS on completed frames tagged with context/size. When an applied size change haswasLastChangeForMemoryPressure(), call the existing native pressure hook to flush pooled textures and pause prewarming. Do not claim a universal music-process survival guarantee from heuristic headroom. - No measured universal frame-time or memory bound is defined. Automatic memory uses documented conservative footprint/reserve estimates; see
docs/superpowers/specs/2026-10-05-native-trails.md. Compare before/after on the same TV, preset, render height and audio (PR template). - Measurement tools: profile build + simpleperf (docs/PROFILING.md); pin a preset with
adb shell setprop debug.projectmtv.preset '<name prefix>'and clear it afterwards (debug.projectmtv.update_fromalso exists);tools/tv-diagnostics.sh <tv-ip>:5555 --no-install --duration 180(docs/DIAGNOSTICS.md; profile builds require--package nl.neerdael.projectmtv.profilewith a matching--apkor--no-install; build/--releasemodes reject alternate package IDs before connecting); Record actual render sizes; use Advanced › Resolution for fixed/Native tests and compare the same preset, audio and trails. Memory relief and automatic blend scaling can still reduce actual size, so confirm STATS/Diagnostics. Preset Lab provides offline rendering comparisons (desktop timings do not establish TV performance). - Documented device coverage: NVIDIA SHIELD TV 2019 (
sif, 2 GB, 32-bit) and SHIELD TV Pro 2019 (mdarcy, 3 GB), Android 11; Ugoos AM6 (Amlogic S922X, Mali-G52 MP6, Android 9). No emulator configuration is committed. - The notification listener restarts the app after
am force-stop(within about a second, verified on the AM6, Android 9, and the AM9 Pro, Android 14): a stop/start is warm, and the process readsprojectm_settingsat that restart. A process that reads the file mid-write saves its migration flags over it. Resolveam get-current-useronce; use that numeric ID for settings/listener commands, permission grants, installation andam force-stop/am start. Filterps -A -o UID,PID,NAMEby exact process name andUID / 100000for that user;pidofincludes other users. Failed/malformed queries must not mean "no process". Write/data/user/<userId>/<package>/shared_prefs/projectm_settings.xml, not user 0's/data/dataalias. Disallow the listener only if that user enabled it, restore the same user's access, and checksurface=in its STATS lines (docs/PROFILING.md);tv-diagnostics.shuses this scope for its cold start and records the user. Command support was checked in Android 9/14 sources; older releases remain unverified. - Diagnostics signing conflicts:
--allow-uninstallattempts removal only for the captured Android user. Before removing data, inspectpm list usersand exact package results frompm list packages --user <userId>for every other user, including stopped users; refuse if another user has the package or a query fails/is malformed. Shared or preinstalled package code can retain the incompatible signing key. A retained conflict fails explicitly after the scoped retry; never broaden to all-user uninstall. Use--no-installor a matching-key APK instead (docs/DIAGNOSTICS.md). - Measured lessons (ARCHITECTURE §5): listing ~10k assets took 8.4 s and blocked UI inflation → the worker reads
presets.idx; GL linking at switches froze the picture 0.5–1.9 s → background prewarm and program caches; tile-based GPUs paid for render-target loads → historical patches 0009–0016. The port retains framebuffer invalidation/pass controls but adopts upstream same-size Mesh/VertexBufferglBufferSubDataupdates instead of the old warp orphan policy; driver performance has not been re-established.
Follow the existing dependency injection and ownership model. Prefer explicit dependencies and testable boundaries. Preserve instance identity, initialization timing, lifecycle, cancellation, and cleanup when refactoring. Keep migrations focused on the task and avoid creating duplicate services, caches, clients, or background workers.
- No DI framework.
MainActivityconstructs its collaborators directly (TrackWatcher,QualityController, theAudioCapturehandler thread).Updateris a process-wide singleton (Updater.get, ownUpdaterbackground thread; the activity attaches/detaches its listener). The engine is a process-wide native singleton behind staticProjectMJNImethods. - Initialization:
ProjectMCore.init(context)once fromApplication.onCreate(safe to repeat); it keeps the applicationAssetManagerfor the process lifetime and starts native preset indexing and the one-time texture copy before the first preset loads. - Persistence:
SharedPreferencesprojectm_settings(MainActivity,ProjectMApplication);files/skipped_presets.txt(skip list,ProjectMCore.skipListFile);files/textures/(texture copy);no_backup/engine_trail.txt(getNoBackupFilesDir(), excluded from backup/transfer; two fixed-size lines, render and prewarm thread, written bycore/src/main/cpp/diagnostics_trail.hwithpwriteand read once byExitDiagnostics.startbefore the engine starts).android:allowBackup="true". There is no schema version: one-time migrations inProjectMApplicationare guarded by boolean keys (skip_list_reset_1_9,frame_rate_reset_30); add a new key for a new migration rather than reusing one. - Custom packs:
CustomPresetPackstages a ZIP under app-privateno_backup/custom-presets/, keeps archives/presets/the active pointer underContext.getNoBackupFilesDir()(API21+) so packs cannot consume Android backup quota; imports.milkplus PNG/JPEG/TGA/BMP/DIB/DDS textures, writes a generated storage/display-path index and atomically replacescurrent.PresetPackUploadServerowns one temporary LAN listener, at most three request workers, one import at a time, a random session URL and strict length-delimited uploads; close on dialog dismissal/pause. Serialize staging, activation and cleanup withSTORE_LOCK. NativesetCustomPresetPackqueues startup restore; uploads prepare a request-specific index, await ready, then commit/discard. Bound index waits at 30 s and preserve storage on failure. Build large orders/membership maps off lock, cache category counts, and retire metadata on the worker. Use the platform UTF-8 decoder for preset names returned through JNI. Custom joins All only and becomes the saved selection on import. Do not mix uploaded entries into packaged predictive indexes or modify bundled assets. Limits: 50,000 presets, 5,000 textures; 2 GiB ZIP, 4 GiB combined expanded data, 8 MiB per preset, 64 MiB per texture, 250,000 entries. Resolve case-insensitive texture stems uniquely. Show a QR code rather than the session URL; keep its quiet zone and verify actual rendered pixels decode to the live endpoint. Run importer/upload JVM tests, native library tests and the isolated custom-pack device journey (setup_case custom_packthencustom_pack_restart, seedocs/user-guide/development.md). Verified on a task-owned API36 TV emulator with 4 GB guest RAM 2026-10-07: exactly 50,000 uploaded presets, replacement, category isolation, invalid-ZIP preservation and cold restart. Do not infer physical-TV performance from this. - Settings flow: Java writes settings through
ProjectMJNIsetters; native code applies dirty settings on the GL thread at the next frame. - Validation when changing these: native engine tests (commands, skip list, transitions, context loss),
QualityControllerTest, app JVM tests, and a TV check of pause/resume (Home and return), audio re-attachment and low-memory behavior.
Use the project's established resource or localization mechanism for user-facing text. Follow its locale ownership and translation workflow; do not invent an English-only or all-locales policy.
- Not localized today.
app/src/main/res/values/strings.xmlholdsapp_name(overridden byresValuefor test builds) the engine version label and Resolution selector labels; there are novalues-*locale directories and no translation workflow. UI text is English literals in Java (MainActivity,OptionRow, …) and the layout XML. Introducing localization is a deliberate change that should move strings to resources consistently. - Wording conventions: the TV app is "ProjectM TV" and the touchscreen app is "ProjectM Android". Both use "ProjectM TV Engine". Use "based on unreleased projectM 4.2 master" and pin
6f6480746for current upstream provenance. The numeric upstream version4.2.0does not mean an upstream 4.2 release. The core AAR shares the app release version;ProjectMJNI.getVersion()still reports the upstream numeric version; keep this API meaning. Preserve package/API/artifact names and historical evidence identities. Menu paths use›(e.g. Settings › Advanced › Auto-update). Keep setting names and values identical in the UI, the README settings tables anddocs/user-guide/settings.md. - Store listing text:
src/fdroidTv/fastlane/metadata/android/en-US/andsrc/fdroidAndroid/fastlane/metadata/android/en-US/(selected by the F-Droid recipe flavor, English only).
Place code according to its responsibility and actual consumers. Reuse shared code when appropriate without creating speculative abstractions. Split oversized or mixed-responsibility files along meaningful boundaries. Preserve behavior during refactors and remove obsolete code.
| Responsibility | Location |
|---|---|
| App UI, audio capture, track titles, updater | app/src/main/java/com/example/projectm/visualizer/ |
| App resources; profile-build manifest | app/src/main/res/; app/src/profile/ |
| App JVM tests | app/src/test/java/com/example/projectm/visualizer/ |
| On-device tests | app/src/androidTest/… (MusicCategoryInstrumentation is a custom Instrumentation and the configured runner; TrackAccessSetupTest) |
| Engine Java API | core/src/main/java/nl/neerdael/projectm/core/ |
| JNI and native engine glue | core/src/main/cpp/ (native-lib.cpp, snapshot_fade.*, preset_prewarm.*, CMakeLists.txt) |
| Core JVM tests | core/src/test/java/… (QualityControllerTest) |
| Native host tests | core/src/test/native/ (engine_test.cpp against stubs, fade_gl_test.cpp, projectm-regressions/) |
| Presets, textures, indexes | core/src/main/assets/{presets,textures,presets.idx,preset-genres} |
| projectM changes | tools/projectm-patches/NNNN-<slug>.patch (4-digit order, header explains the change and any upstream source) |
| Upstream engine (do not commit edits) | third_party/projectm |
| Tools | tools/*.sh, tools/*.py, tools/preset-lab/ |
| Release tooling / CI | .github/scripts/ (+ tests/), .github/workflows/ |
| Docs | README.md, docs/, docs/user-guide/ |
- JNI functions bind by name (
Java_nl_neerdael_projectm_core_ProjectMJNI_*), kept bycore/consumer-rules.pro; renamingProjectMJNI, its package or a native method requires matching native changes and breaks consumers. - No file-size limits are established.
MainActivity.java(~1,200 lines) andnative-lib.cpp(~2,000 lines) are large; split only along real responsibilities. - Root scripts
build.sh,build_android.sh,debug.sh,install.share local helpers not used by CI (not validated).
Preserve the release/version rules above. Treat the core AAR’s interface and compatibility with Milkbeat as an integration boundary; verify the actual API and consumers before changing it.
- Core API: public classes in
nl.neerdael.projectm.core(ProjectMCore,ProjectMJNIincl.TRANSITION_*constants,VisualizerView,VisualizerRenderer,QualityController,DeviceProfile,DisplayInfo,PresetFacets,PresetKnowledgePack) are used by:appand by Milkbeat through the released AAR. There is no API/ABI compatibility check. Before removing or changing public members or native signatures, inspect Milkbeat's usage. Because everymainmerge publishes the core and triggers a Milkbeat rebuild, a deliberate incompatible change needs a coordinated Milkbeat change; no written procedure exists yet (unresolved). - Native toolchain: NDK
27.3.13750724, CMake3.22.1, C++17, ABIsarmeabi-v7a/arm64-v8a, projectM linked statically. Keep the reproducible-build settings inCMakeLists.txt(-ffile-prefix-map, disabled flex/bison) anddependenciesInfooff inapp/build.gradle: F-Droid rebuilds and compares the release APK. - projectM patches: never commit edits inside
third_party/projectm(submodule hasignore = dirty). To write a patch: apply the existing series inthird_party/projectmand stage it (git -C third_party/projectm add -A) so new edits show as a clean diff, then usetools/regen-projectm-patch.sh <name>. That script only diffssrc/andtests/; patches touchingvendor/hlslparsermust be produced manually with agit diffthat includesvendor/hlslparser.vendor/projectm-evalis a nested submodule: diff it withgit -C third_party/projectm/vendor/projectm-eval diff --src-prefix=a/vendor/projectm-eval/ --dst-prefix=b/vendor/projectm-eval/and stage it withgit -C third_party/projectm/vendor/projectm-eval add -A. ItsScanner.c/Compiler.care pre-generated (the build disables flex/bison); for aScanner.lchange commit only the delta between two runs of the same flex (flex --noline --prefix=prjm_eval_ --header-file=Scanner.h -o Scanner.c Scanner.lbefore and after), because Apple's flex 2.6.4 skeleton differs from the committed one (historical patch 0034). Patch numbers are taken in merge order: checkmainfor new patches before numbering yours. For several new patches at once, a local-only branch in the submodule with one commit per patch (series, then each new patch) letsgit diff <commit> <commit>regenerate any of them; never stagethird_party/projectmin the superproject, and reset the submodule to the pinned commit afterwards. After pulling patch changes, reset the submodule (git submodule foreach --recursive git checkout -- .). Shader changes must link as GLSL ES 3.00 (glslangValidator -lwith#version 300 esprepended; PR template). - Preset equation loading: code the evaluator rejects is compiled once more in MilkDrop's form (numbered records joined,
///\\comments removed, NS-EEL's stray;in parentheses read as a space); a lone.is the number 0; a block that still does not compile is left out like MilkDrop'sCState::RecompileExpressions(q/t variables zero after a failed init) and reported throughprojectm_set_preset_initialization_warning_event_callback, whichnative-lib.cpplogs asPreset code left out (<preset>): <reason> (line N, column M). Only parse errors fail a load. Keep accepted programs on the unchanged path. - Random textures: image selection belongs to slots 00–15 in a preset and is reused across warp/composite and shader reloads. Rebuild the descriptor for each exact alias and requested mode. Within a shader, filtered
randNN_prefixaliases choose an empty slot before unfiltered aliases; competing prefixes keep lexical precedence. Across stages, the already selected slot wins.sampler_statefields remain ignored; name prefixes select mode. Default user-texture mode is linear/wrap. Bind emitted short aliases to the same unit as their full alias and deduplicate declaration lines. PreserveTexture::SourcePath()and base names for diagnostics; generated textures have no source path. Production usesstd::random_devicefor each new texture choice; a host seed does not establish Android/AAR association. - Blur framebuffer ownership: capture both caller read/draw framebuffer bindings before allocating or resizing blur textures.
Framebuffer::SetSizeunbinds both targets; saving after allocation sends later shapes/waves/borders to framebuffer zero when warp shaders sample blur. The native runner checks first use, unchanged size, resize, scaled blur, constant-color output and the unchanged midgit preset with isolated TGA assets. The same ownership control is verified on AM6/Mali-G52; Android framebuffer zero can be valid and hide the host error, so assert binding identity as well as checking GL errors (evidence:docs/superpowers/evidence/midgit-framebuffer/). - GLES sampling fixtures: allocate sized RGBA8 storage before an RGBA upload and assert allocation/upload GL errors immediately. An unsized RGB texture can accept RGBA uploads on desktop GL while GLES rejects them. Use unit-slope, exactly representable edge-crossing UV/filter weights for cross-driver numerical oracles; keep strict pixel tolerances and inspect actual texture bytes/sampler state before attributing differences to the engine.
- Native regression GL linkage: link engine-dependent regressions to
libprojectM::projectMand the harness-selected EGL/GL target. System projectM GLES discovery creates a directory-scoped imported target; the parent harness must not referenceOpenGL::GLES3directly unless the explicit-flag override made it global. Validate both normal discovery and explicitGL_LIBS/GL_CFLAGSroutes on Linux/Mesa, as well as macOS. - Matched TV benchmark selection: temporarily select All when a curated collection excludes the witness, restore the original selection afterward, and verify exact
BENCHMARK/LOADfilenames before accepting FPS rows. A short prefix can select a sibling (widest swing zero-sum.milk); use the full filename when it fits the Android property limit. Cold-start with the captured-user/listener recipe above so a preserved preset cannot override the debug pin. Record actual surface size, target FPS and audio state; silence windows do not establish live-music performance. - Predictor-established renderer fixes (historical 0045–0047; current 0004–0006): main-textured shapes own a repeat/linear sampler for every fill, including authored/native geometry; preserve named-image descriptor qualifiers. Blur bounds retain clamp-then-expand ordering, but upper bounds expand upward; reject nonfinite/unrepresentable ranges and progressive float32 cancellation using coherent
[0,1]defaults for storage and decoding. Current0015 supersedes0006’s authored-unit specialization: prepare negative effective zoom with CPUpowfand send it at attribute9, preserving fractional-domain NaNs/Inf and the positive GPU path. Invalid-coordinate appearance remains unsupported. Native controlsshape-sampler-regressions,blur-range-regressionsandwarp-zoom-regressionsexercise real draws, production normalization and vertex UV readback. Do not infer Windows appearance or TV GPU performance from host/emulator checks. - Live controls (historical 0048–0049; current 0007–0008): rendering consumes per-frame values without writing configuration defaults; keep reset-per-frame and instance ownership intact. Rebuild wave math when the truncated signed-remainder mode changes (16 modes). Legacy-only filters must support equation-only activation; custom composites retain shader policy. Run
dynamic-wave-controls,dynamic-display-controlsanddynamic-original-presets; preserve historical predictor/static-policy results. Seedocs/ARCHITECTURE.mdand the live-controls evidence. - Predictive collection identity:
tools/milk-analyzer/beta_export.py --check --bundle core/src/main/assets/preset-genresvalidates the beta schema2 source/texture/weight inventory, frozen scoring code/model, standard published-AAR identity, raw activity formula, relative ranks and exact overlapping group membership. The numerical profile is capped2.3.3 at128×72, not a certificate of Native/TV fidelity. A changed renderer needs a separately identified run; historical schema1 Preset Lab imports do not produce the current bundle. - Presets and textures: CI rejects presets that cannot react to audio or use excluded or missing textures (
tools/check-presets.py) and a stalepresets.idx(tools/gen-preset-index.py --check). Preset Lab CI rejects tracked audio/raw capture files undertools/preset-labandcore/src/main/assets/preset-genres. - Licensing and attribution: app code LGPL-2.1 (
LICENSE); presets and textures CC0 1.0 (LICENSES/CC0-1.0.txt). Record new third-party content and new patches in docs/THIRD_PARTY.md; keep upstream attribution in patch headers and release notes. - Collection archive identity:
docs/THIRD_PARTY.md,LICENSEandLICENSES/CC0-1.0.txtare included alongside the preset assets in the downloadable collection. After changing any included file, regeneratetools/preset-distribution/official-archive.jsonwithpython3 tools/preset_distribution.py lock-archive --root . --output tools/preset-distribution/official-archive.json, then verify a complete archived build using the command in.github/workflows/android-build.yml. - Privacy claims: network use is opt-in updates, explicit collection downloads and the temporary LAN custom-pack listener. Native crash reports remain in private no-backup storage, may contain raw stack/register memory, and never upload automatically. The viewer/copied report omits raw memory sections. Keep README/guide aligned with stored data and network use.
- Worktrees: never remove another task's worktree. Follow the mandatory workflow below for cleanup of this task's clean, pushed and merged worktree; it is authorized as part of implementation. For this research checkpoint the user has authorized removal of
.worktrees/native-feedback-recoveryonly after the main merge, all intended work is pushed, its tracked state is clean, task-owned jobs are stopped and external evidence archives are hash-verified. This exception does not authorize removing any other worktree or its data. - Internal laboratory policy: new renderer patch 0001 retains
PROJECTMTV_DISABLE_FEEDBACK_DIFFUSIONguards for legacy controls. This does not restore the retired public capped artifact or build policy. - Native trails/fallback: the renderer owns authored feedback/resources per preset; Standard skips native warp, Medium/High use centered headroom-limited detail. Preserve per-frame RNG reuse, native viewport restoration after canvas init, UV-map invalidation on resizing/mode changes, authored blur timing and authored geometry recurrence. The renderer reuses evaluated shape/wave geometry for native presentation and alternates the existing canvas buffers; do not rerun persistent equations or RNG for the native draw. Shader/canvas failures retain the legacy diffusion fallback and diagnostics. The new Android core defaults Standard, while the projectM C API remains off by default for compatibility tests. Historical dual-AAR/capped evidence does not validate new automatic quality. Use the owner-approved17 known-preset focused matrix against source/released2.3.3, rather than claiming whole-corpus coverage. Record source/PCM/clock/seed/capture identities and quantify brightness/structure; chaotic and near-black cases need visual review.
- Device ownership for native-trails-validation-fixes: use only this task's
emulator-5602(AVD/logs in ignoredbuild/native-trails-investigation/), or the user-authorized AM9192.168.51.53:5555and AM6192.168.50.80:5555. AM9 runs64-bit Android14/Mali-G310; AM6 runs32-bit Android9. Build matched validation roles with--abi armeabi-v7afor AM6. Do not operate other tasks' emulators. Always pass an explicitadb -s SERIALand captured Android user. Never wake a TV remotely; stop testing when it sleeps. - Do not commit:
local.properties, keystores, APK/AAR outputs,build/, raw diagnostics (see.gitignore).
Follow the repository's configured formatting and lint rules. Review automatic formatting changes and avoid unrelated churn. Fix violations rather than disabling checks to obtain a passing result.
- No project-wide source formatter or linter is configured: no
.editorconfig,.clang-format, ktlint/detekt,lint.xml, pre-commit or Python lint configuration outsidethird_party/. CI does not run Android Lint. Match the surrounding style (Java: 4-space indent). - Validate workflow changes with
actionlint. Older local versions reject GitHub's documentedqueue: maxandcache-mode: none; use narrow ignores for those two schema fields only if necessary, and do not suppress other errors. Also rungit diff --check. - The JNI library compiles with
-Wall -Wextra -Wno-unused-parameter; do not add warnings. - Checks that act as lint in CI:
release_notes.py validate(PR body),tools/gen-preset-index.py --check,tools/check-presets.py,mkdocs build --strict(user guide).tools/check-patch-series.shis not in CI; CI applies the series through CMake during the native tests andassembleRelease.
The release gate runs Preset Lab and Native trails/current-engine checks, not the retired historical core-corpus scanner. tools/core-corpus now retains shared engine-pin/frame-time, signal/session/PNG and worker/bridge components plus relevant unit controls. The old bulk producer/scanner, hard-coded smoke and historical fixture-fetch/source controls are removed. Preserve archived records; verify their helper hashes from the recorded source revision, not current reduced modules.
Choose validation that exercises the changed behavior. Compilation alone does not establish functional correctness. For UI or integration changes, exercise relevant user journeys and error paths when the environment supports them. Record baseline failures and environmental limitations honestly.
For release tooling changes, the required check is:
python3 -m unittest discover -s .github/scripts/tests -vPrerequisites (from app/build.gradle, core/build.gradle, wrapper and CI): JDK 21 (CI Temurin 21, matching F-Droid; sources compile as Java 1.8), Gradle wrapper 8.14.2, AGP 8.12.0, Android SDK platform 34, NDK 27.3.13750724, CMake 3.22.1 (CI installs the last three with sdkmanager), local.properties with sdk.dir (git-ignored; CI writes it), initialized submodules. CI runs on ubuntu-24.04.
| Command | Covers | Status |
|---|---|---|
git submodule update --init --recursive |
setup | Verified |
tools/check-patch-series.sh |
full series applies to a clean export of the submodule's HEAD (so the submodule must be at the pinned commit) |
Verified current migration: all three patches apply to a clean 6f6480746 export. Historical evidence: "all 29 patches apply" on main; the 32-patch series was verified with the same steps against e0b0a967; "all 35 patches apply" on 2026-10-04 with 0033–0035; 37 patches verified after integrating the parser fix for the random-binding task; all 41 patches verified on 2026-10-05; all 44 patches verified on 2026-10-06 for the float-literal fix; main separately verified all 47 for sampler/blur/zoom corrections |
tools/projectm-host-tests.sh [--gtest_filter=…] |
patched projectM GTest suite (build in build/projectm-host); needs CMake, Ninja, C++ compiler, Homebrew googletest |
Verified current migration on macOS: 329/329. Historical evidence: 179/179 on main, 204/204 with 0030–0032, 222/222 with 0001–0035 (configured with -DCMAKE_DISABLE_FIND_PACKAGE_FLEX=ON -DCMAKE_DISABLE_FIND_PACKAGE_BISON=ON to match the Android build's pre-generated parser) |
python3 -m unittest discover -s .github/scripts/tests -v |
release tooling | Verified migration run: 157 tests pass. Historical run: 74 tests OK (Python 3.13.12) |
python3 -m unittest discover -s tools -p test_tv_diagnostics.py -v |
diagnostics package/APK-source options; user-scoped cold start, process filtering, fail-safe queries and signing-conflict recovery; isolated fake adb, no TV/build/network | Run by Android CI; see task validation for the current revision |
core/src/test/native/run_native_tests.sh |
engine tests, GL fade overlay, patched-projectM regressions (ASan/UBSan); needs a C++17 compiler, JDK (jni.h), CMake, EGL/GLES dev libs on Linux (macOS uses OpenGL) |
Verified migration on macOS: engine/render-policy checks and 21/21 ASan/UBSan controls pass; EGL transition overlay skips without EGL/GLES development files. Linux EGL CI remains open. Historical 2026-10-04 runner also passed its engine/regression controls |
./gradlew testReleaseUnitTest (CI) / ./gradlew testDebugUnitTest (PR template) |
app and core JVM tests | Verified migration: 30 app + 54 core tests, zero failures/errors/skips. Historical 2026-10-04 testReleaseUnitTest also succeeded |
./gradlew assembleRelease / ./gradlew :core:assembleDebug / ./gradlew assembleProfile |
APK + AAR; patch application through CMake; profile build | Verified migration: debug/release core and profile APK for both ARM ABIs; fresh recursive 85ceac83 builds debug core and release APK for both ABIs. Historical 2026-10-04: :app:assembleProfile :core:assembleRelease, and :core:assembleDebug from a fresh recursive clone (CMake applied the series); a new worktree needs local.properties copied from the primary checkout |
tools/gen-preset-index.py --check, tools/check-presets.py |
asset checks | CI; not validated in this pass |
python -m pytest tools/preset-lab/tests (-m native needs the native worker; see tools/preset-lab/README.md) |
Preset Lab | Verified migration: standard worker full suite 164/164 including three native controls; later worker-build/Android-header suite 7/7. Those are separate runs, not an inferred 165-test full-suite result |
mkdocs build --strict (after pip install -r docs/site-requirements.txt) |
user guide | Verified migration strict build, no warnings; also verified 2026-10-04 |
./gradlew -PpresetLabDeviceTest :app:assembleDebug :app:assembleDebugAndroidTest, then adb -s DEVICE shell am instrument -r -w -e live_audio true nl.neerdael.projectmtv.presettest.test/com.example.projectm.visualizer.MusicCategoryInstrumentation |
music-category behavior on a TV (development guide) | needs a TV; not validated in this pass |
Review-fix loops (user rule, 2026-10-09): run the full local validation once before opening a PR. After that, for each review fix run only a quick check aimed at the changed code (the affected JVM test class, a compile, or one focused control), then push. Reviewed-PR CI (pr-builds.yml) runs the full native suite on Linux (including the GLES-only paths macOS cannot exercise), the JVM tests, both ARM builds, Preset Lab and the strict guide build. Do not rerun the ~20-minute run_native_tests.sh or repeat full device passes for every review round.
What to run when:
- JNI,
native-lib.cpp, transitions, skip list: native tests, JVM tests, a build, and a TV check for rendering/audio/frame rate. - Random-binding changes: real-GL
random-texture-*controls incore/src/test/native/projectm-regressions/inspect units/samplers and compare known TGA bytes. The optionalrandom-texture-regressions presets <new-fixture-dir>diagnostic records exact bundled asset identities and full-load/render results separately; full JPEG decoding exposes pre-existing SOIL2 UBSan warnings. See the random-binding evidence for unverified appearance/device limits. - projectM patches:
check-patch-series.sh,projectm-host-tests.sh, native tests,:core:assembleDebuglog, TV before/after; complete the PR template's projectM patches checklist. Shader translation changes: the native runner includes float32 round-trip/locale/nonfinite controls (float-literal-regressions), a one-frame warp/composite RGB control (float-literal-render), 95 unchanged hashed shader sections (float-literal-presets), andshader-parser-regressions,shader-render-regressionsandparser-presets(16 original files pinned byparser-presets.tsvhashes); also runPresetShaderTranslationTest(generated GLSL plus composite shaders rendered through the engine on macOS CGL; render cases skip without a GL context) and, where useful, translating the bundled presets' shaders before and after and compiling both withglslangValidatoras GLSL ES 3.00. - GL test readback: use
GL_RGBA/GL_UNSIGNED_BYTEfor normalized RGBA8 GLES targets, and drop alpha only when exporting RGB artifacts. Desktop CGL acceptingGL_RGBdoes not establish GLES portability; validate readback changes on Linux EGL/Mesa as well. Keep GL errors checked before and after readback. - Presets/textures:
check-presets.py, regeneratepresets.idx, consider the genre bundle. - UI, settings, remote keys: JVM tests plus a D-pad journey on a TV (open panel, sub-panels, Back/Menu, auto-hide); refresh setup screenshots with
-PsetupScreenshotTestwhen visuals change. - Audio and track titles: a TV with a verified music app (Spotify, SoundCloud, SmartTube, Milkbeat); include pause/resume.
- Public core API: build
:core:assembleReleaseand build Milkbeat against the new Native core (-PprojectmCoreRepo=<dir> -PprojectmCoreVersion=<version>, see docs/RELEASING.md). Verified consumer6802630c:githubDebugplus 28 focused tests against exact AAR SHA256c57c823d18e5e33c0fcb4c779e41eece0a3d613cf95092351a86a4332c864075; automatic release/update remains a separate gate. - After TV work: clear
debug.projectmtv.*properties and restore app settings.
Feature pushes and PRs targeting other branches do not start builds. Ready PRs targeting main need a clean completed review of the latest head from Codex, Claude or a qualified non-author human, no outstanding review requests, observable pending reviews or changes requested, and no unresolved threads (including outdated ones). Run both Claude and Codex on the initial ready PR, then choose one provider for each follow-up with @claude review or @codex review. The latest authorized manual code-review command or authenticated Claude workflow dispatch supersedes earlier code-provider requests; @codex security review remains a separate required scope. Follow-up pushes do not automatically run Claude. Codex's automatic opening review is configured in the repository integration outside Actions; turn that setting off for fully manual reviews. Never substitute self-review or a timeout for a clean final-head signoff.
Preserve trusted-main execution of the review controller, dedicated Claude reviewer and status reporter: those jobs must never check out or execute PR code, pass publishing/signing secrets to PR code, or let PR code write main caches. The Claude reviewer uses anthropics/claude-code-action@v1, CLAUDE_CODE_OAUTH_TOKEN and constrained source-reading commands. Its scoped App token is revoked by the action after inspection/inline feedback; the trusted publisher uses github.token. Enable repository Actions can_approve_pull_request_reviews=true for native publication while retaining default_workflow_permissions=read and all required review/build/thread protections; the trusted publication script validates the structured full SHA, approval decision and summary before posting a native current-head APPROVED review. Native Actions/Claude bot approval counts only with authenticated dedicated successful workflow provenance, matching start receipt and reviewed full head; general bot approvals do not qualify. Failed, empty or stale output cannot sign off. Findings produce a COMMENT review with a FINDINGS receipt and inline threads; require a subsequent clean current-head code review from either provider after that receipt and resolve all threads. Its initial RUNNING receipt keeps validation pending while its authenticated trusted-main workflow run is active. Completed or canceled runs stop that running block without supplying approval; preflight and the final reporter need actions: read to authenticate receipts. Keep the interactive Claude workflow separate: it retains content/PR/issue write permissions and Actions read access for replies and fixes, while exact PR @claude review commands route only to the reviewer. The interactive assistant can check out and edit PR code for fixes requested by an authenticated repository writer; this is separate from the read-only reviewer/controller trust boundary. See docs/RELEASING.md for reviewer setup, commands, authorization and permission details.
Codex approval is the connector's +1 reaction on the PR itself; completed comments and submitted review records supply revision/time metadata and cannot approve without that reaction. Resolve authenticated Codex 7–40-character identifiers through GitHub to the full current head; reject stale/ambiguous identifiers, malformed responses and hex-named branch/tag aliases. Require the reaction at or after the current review's completion. Qualified non-author human reviews remain a separate approval path; neither reserved provider bot qualifies as a human. Track authorized manual commands by their latest comment update time (creation time only when no update timestamp exists); only current-head completions strictly later at whole-second precision fulfill the selected provider request or separate security request. Private draft reviews are visible only to their author; request the reviewer on the PR to represent planned unfinished reviews.
Preserve full 40-character head/base/merge IDs in dispatch and preflight. Automatically rerun successful validation invalidated by review eligibility, a preflight error with no builds started, or a final reporter API error after successful builds, once eligibility recovers; actual build failures require an explicit retry. Read the live main ref and verify the immutable test merge parents; recheck head/base/review before reporting success. Require Reviewed PR builds from GitHub Actions in the main ruleset, with up-to-date-branch and thread-resolution enforcement. GitHub's approving-review count remains zero because Codex completion is allowed; the custom status enforces the completed-review minimum. See docs/RELEASING.md for completion signals, scheduled rechecks and manual retry.
Claude/Codex review cadence maintenance (2026-10-10): CLAUDE_CODE_OAUTH_TOKEN was verified present in repository Actions secrets; its value was not read. Repository Actions settings were enabled and verified as default_workflow_permissions=read and can_approve_pull_request_reviews=true. All 185 release/review tooling tests pass, including 89 gate and 10 Claude helper tests. A live source-reader check returns decoded UTF-8 source at the requested commit. The gate and reviewer share the same writer-permission check; PR authorship alone does not authorize Claude requests. Actionlint passes for both Claude workflows; checks for other changed workflows pass with only the existing queue/cache-mode keys ignored because the local validator lacks those newer schema keys. Strict MkDocs and the PR release-notes validator pass. A live Claude review through the trusted-main workflow is not yet verified; the earlier Action run explicitly skipped because the workflow did not match the default branch, and PR-only workflow changes are not active in trusted main execution before merge.
A successful tested merge to main triggers the versioned APK/single Native core AAR release and Milkbeat update through the canonical alias. Routine PRs must not manually bump the base version/code/commit. Follow docs/RELEASING.md for a planned new release line.
| Workflow (file) | Trigger | What it does |
|---|---|---|
Android CI/CD (android.yml) |
main push or main manual run | Calls Android build, Preset Lab and User guide; publishes only after the full suite passes, updates Milkbeat and deploys Pages. Main runs queue without cancellation |
Android build (android-build.yml) |
reusable call only | Native/tooling/assets and JVM tests; Native APK/AAR/mapping artifacts (30 days). Main receives signing secrets; PR calls receive none and disable Gradle cache access |
PR review gate (review-gate.yml) |
PR/comment/main-push events, trusted workflow_run relay from an unprivileged review signal, five-minute schedule, validation completion, manual | Trusted-main controller checks current Codex, Claude or qualified human review, outstanding review requests/changes and every thread; dispatches eligible PR validation and cancels obsolete runs. Schedules may be delayed by GitHub |
Claude Code Review (claude-code-review.yml) |
Initial ready same-repository PR by a repository writer targeting main; authorized exact PR @claude review comment created/edited; manual pr_number on main |
Trusted-main, read-only content review; validates structured current-head output before publishing native approval; forks require a trusted on-demand request |
Claude Code (claude.yml) |
General @claude mentions in issues and PR discussions |
Interactive replies/fixes with content/PR/issue write and Actions read permissions; exact PR review commands route to the dedicated reviewer |
PR review signal (review-signal.yml) |
submitted/edited/dismissed review events | Unprivileged relay with no checkout; default-branch controller follows its completion |
Reviewed PR validation (pr-builds.yml) |
controller/manual dispatch on main | Preflight validates immutable head/base/test-merge SHAs; runs all Android, Preset Lab and guide builds without secrets; required Reviewed PR builds status passes only after actual success and a final eligibility/revision check |
PR release notes (release-notes.yml) |
PR opened, synchronized, reopened, edited, ready for review | release_notes.py validate on the PR body |
User guide build (docs-build.yml) |
reusable call from main, reviewed PRs or the manual guide workflow | Read-only mkdocs build --strict and Pages artifact; no publishing token |
User guide (docs.yml) |
manual run on main | Calls guide build and the shared Pages deployer; other branches skip |
Publish user guide (pages-deploy.yml) |
reusable call from main Android or manual User guide | Shared projectm-tv-pages queue with no cancellation; publish only if caller SHA still equals current main; API failure stops publication |
Preset Lab (preset-lab.yml) |
reusable call from main/eligible PR validation, or main manual run | Native/source-analysis tests under xvfb; rejects tracked audio/raw captures |
- Versioning (
.github/scripts/release_version.py): publishing happens only forrefs/heads/mainonpushorworkflow_dispatch. Version =baseVersionNamepatch + (first-parent ordinal sincebaseVersionCommit− 1); the code advances in step frombaseVersionCode. Other builds get a-ci.<run>suffix and file names…-ci.<run>-<sha>.apk/.aar(the main/PR source SHA). Every build checks out a deterministic release commit (.github/scripts/release_commit.py) on top of its source whose only change appendsprojectmReleaseVersion/projectmReleaseVersionCodetogradle.properties; the release job rebuilds it, requires the build's SHA and pushes it only as thev<version>tag. Never push it to a branch or declare those properties onmain; version allocation and release notes map such tags to their parent source. The user-planned Native minor release line sets the base triple to 2.3.0 / code 49 /8b70620339018cfaf5ac05acdb4ea8104dc2eb59; this deliberate line change is the documented exception to routine no-bump rules. Publication remains contingent on successful main CI. - Signing secrets (names only):
SIGNING_KEYSTORE_BASE64,SIGNING_STORE_PASSWORD,SIGNING_KEY_ALIAS,SIGNING_KEY_PASSWORD. A publishing build without the keystore fails; PR artifacts use a temporary debug key. - Publication: GitHub Release
v<version>withprojectM-TV-<version>.apk,projectM-Android-<version>.apk,projectM-TV-core-<version>.aar(Native), stable aliasesprojectM-TV.apk/projectM-Android.apk/projectM-TV-core.aar, separate mappings, F-Droid TV/Android versioned and stable APKs, the verified collection ZIP,release-version.jsonandchecksums.txt. Notes come from merged PRs'## Release notessections plus a CI footer. - Downloader codes: read by
release_notes.pyfrom the README blockquote> **Install on your TV with the Downloader app: code `4821216`**(exact regex; changing its format breaks release-note generation). The TV code remains 4821216 and targetsreleases/latest/download/projectM-TV.apk. The separate canonical> **Install on your phone or tablet with the Downloader app: code `6565691`**supplies the Android footer and targetsprojectM-Android.apk. Keep the two blockquote formats distinct; the Android code is optional only for versions before 2.3.38 (v2.3.37 predates this code). From 2.3.38 onward the generator requires it even if the entire blockquote is removed. The user supplied code 6565691 and the Downloader Play Store link on 2026-10-11. - Milkbeat:
publish_release.py milkbeat(token secretMILKBEAT_TOKEN) sendsrepository_dispatchprojectm-core-releasewith the version tojohnneerdael/Milkbeat, unless Milkbeat's latest release already names this core version or newer. - Knowledge pack reader:
PresetKnowledgePack(core, additive) readspreset-knowledge-pack-v1packs pertools/milk-analyzer/export-contract/knowledge-pack/FORMAT.md: both policy strings, core file hash (verified before anything is decompressed), per-line hash over stored bytes, one valid gzip member with no trailing bytes (own RFC 1952 reader checks header CRC and trailer;GZIPInputStreamaccepts concatenated members), required presets array, LF-only line endings, strict UTF-8, JSON types of view fields, manifest order, unknown fields ignored. It holds the compressed core once in memory, decompresses it per pass and keeps per-preset views;RecipeVisitorreceives full recipes once, from the same verified bytes, only after the whole pack passed.GzipMemberends itsInflateron close and on header failure. Shared golden vectorsgolden-v1/(valid, unknown-fields, null-containers, twelve single-rule rejections; a missing or null container means empty because producers emit null estimates) run inPresetKnowledgePackTest; the producer, Python reference reader and vector generator (knowledge_pack.py,knowledge_pack_golden.py) live on the predictor branch. No pack is bundled in the app yet. - Mood facets: Settings › Preset mood opens the Mood panel (
MoodFacetsController,PresetFacetsin:core). Core API additions:PresetFacetsandProjectMJNI.setPresetPool(String[])(additive; legacysetMusicCategory("chill"|"normal"|"intense")is unchanged for Milkbeat). Preferences:mood_facets,mood_collection,music_category="facets", migration keymood_facets_migrated_1(an old Chill/Normal/Intense choice becomes the Energy option). Bundle (since 2026-10-16 static source analysis only; manifestsource: static-analysis):python3 tools/milk-analyzer/attribute_bundle.py --check(CI) checks it against the committed evidencedocs/superpowers/evidence/static-mood-table-2026-10-16/static-evidence.json.gz(two-way names vspresets.idx, each row rebuilt from its evidence row, preset bytes vs evidence sha256, recomputed manifest metadata, deflatedattributes.tsv≤ 450,000 bytes); rebuild with--build-static <evidence>, thenpython3 tools/preset_distribution.py lock-archive --root . --output tools/preset-distribution/official-archive.json. The evidence is exported on the never-merged research branchfeat/predictor-static-output-bounds(build/colour-gap-2026-10-15/static_evidence_export.py, not committed there); a changed or added preset needs a new export, or--checkfails. Definitiontools/milk-analyzer/facets/facets-v1.json: groups Energy, Brightness, Colour, Palette (red…pink, rulepalette), Main colour (main_red…main_pink, rulemain_colour), Movement, Style, Comfort; static options are not recomputed from the value columns. The earlier engine-ruler path (--ruler/--style/--ruler-manifest,--write-evidence-manifest,--freeze-activity-reference, ruler manifestdocs/superpowers/evidence/mood-facets-ruler-2026-10-12/ruler-results-manifest.json, frozenfacets/activity-reference-v1.json) is kept with its tests and checks bundles withoutsource, but no longer produces the shipped table. Selection semanticstools/milk-analyzer/preset_facets.pywith shared vectors (fixtures/facet-selection-vectors-v1.json, also read byPresetFacetsTest). Panel renders use oneMoodFacetsController.snapshot()(one skip-list read) for every chip count, dimming and the header;SkipListFilere-parsesskipped_presets.txtonly when its timestamp or length changes. Device case:-PsetupScreenshotTestbuild, thenadb -s SERIAL shell am instrument --user USER -w -e setup_case mood nl.neerdael.projectmtv.setuptest.test/com.example.projectm.visualizer.MusicCategoryInstrumentation. Verified 2026-10-16 with the static table (including the Palette step) on a task-owned API 36 Android TV emulator (1080p); on a fresh emulator grantandroid.permission.RECORD_AUDIOto the.setuptestpackage first, or the permission dialog keeps the engine from loading a preset and the case fails with "category not applied". Style options come from source structure and are the least certain; static precision/recall limits are indocs/user-guide/predictive-collections.md. User docs:docs/user-guide/predictive-collections.md. - Committed generated files:
core/src/main/assets/presets.idx,core/src/main/assets/preset-genres/,core/src/main/assets/preset-attributes/(facets.json,attributes.tsv,manifest.json). Not committed: APKs, AARs,build/, raw diagnostics.RELEASE_NOTES.mdis a manual archive (last entry 2.1.5) and is not used by CI.
| Source | Role |
|---|---|
README.md |
Product overview, settings tables, permissions, install (canonical Downloader blockquote), troubleshooting, developer build/test |
docs/user-guide/**/*.md + mkdocs.yml |
User guide source: Using ProjectM TV pages, authoring/ (source-level preset authoring, MilkDrop 2 semantics, no patch-specific claims), engine/ (patch catalog, 4K rendering, pipeline, validation) and the Predictor section (predictor.md, predictor/*.md: how the shipped moods are predicted from source, accuracy, predictions vs renders, JSON output, SDK, research journal, road ahead); built by the User guide build reusable workflow and published through the shared main/manual Pages deployer to https://johnneerdael.github.io/ProjectM-Android/ (docs/user-guide/development.md covers build/test and the docs site) |
docs/ARCHITECTURE.md |
Engine design, threading, transitions, resolution, device tiers, measurements |
docs/RELEASING.md |
CI publishing, versioning, signing, downloads, Milkbeat |
docs/UPSTREAM_PATCH_VALUE.md |
Current locked 15-patch assessment and GPU Android TV upstream/ablation image proof; linked archive preserves historical dispositions; update with pin/patch/evidence changes |
docs/THIRD_PARTY.md |
projectM pin, link to patch provenance, presets/textures sources and licences |
docs/PROFILING.md, docs/DIAGNOSTICS.md |
Profile build + simpleperf; tools/tv-diagnostics.sh |
docs/DANCE-COLLECTION.md |
Pointer to the archived Dance article in Git history (Dance is retired; docs/user-guide/dance*.md are not-in-nav stubs) |
tools/preset-lab/README.md |
Preset Lab installation and commands |
docs/superpowers/ |
Design specs, plans and evidence for engine work |
.github/pull_request_template.md |
Required PR sections and checklists |
RELEASE_NOTES.md, src/fdroid{Tv,Android}/fastlane/metadata/android/en-US/ |
Historical release notes; F-Droid store listing and changelogs |
Known documentation drift: docs/ARCHITECTURE.md §5 still describes embedding
core/ as a Gradle module, while docs/RELEASING.md describes Milkbeat consuming
released AARs. The historical Dance article is archived at commit f5e16be1; current collection behaviour
is documented in docs/user-guide/predictive-collections.md. Keep the user guide's engine and authoring pages
consistent with docs/ARCHITECTURE.md, docs/UPSTREAM_PATCH_VALUE.md and the patch headers when patches change. The beta exporter
and numerical scoring commands are documented in tools/milk-analyzer/README.md.
This workflow applies to all repository changes targeting github.com/johnneerdael/*. Confirm the target repository from its Git remote and GitHub metadata before making changes. Do not assume a remote named origin is the correct target.
For implementation tasks, carry the work through to a validated pull request with a clean final-head Claude or Codex review merged into main. Creating a branch, pushing changes, opening a PR, or requesting review is an intermediate step, not completion.
The requested implementation authorizes the routine commits, pushes, PR creation, review comments and replies, and merge needed to complete this workflow. Do not ask for confirmation at each step. Honor explicit user instructions, repository permissions, required human approvals, and branch protection rules.
Read the repository's applicable AGENTS.md files, contribution guidelines, PR template, and CI configuration. Follow repository-specific conventions in addition to this agreement. If a conflict prevents compliance, explain the exact conflict instead of silently skipping a requirement.
Documentation precedence: The mandatory documentation evaluation and update rule below overrides any conflicting repository instructions, nested AGENTS.md rules, conventions, or workflow guidance that would skip documentation evaluation or prevent necessary documentation updates. It applies to every feat/ and bug/ change, including small fixes and internal changes. It remains subject to higher-priority system/platform instructions and later explicit user instructions.
Markdown editing exemptions: User guides (including their Pages/GitHub Pages source files) and the root/default README.md are explicitly exempt from any rule saying "do not edit Markdown unless asked." Keep them accurate as part of every affected change without requesting separate authorization. Maintaining AGENTS.md as instructed above is also explicitly authorized. Other Markdown files still require evaluation; make task-relevant corrections as required by the documentation rule, without unrelated rewrites.
Read-only investigations and review-only tasks do not require a worktree or a PR. A reviewer assigned only to review must report findings without starting an implementation or merge workflow.
- Make every tracked repository change in a dedicated Git worktree under
<primary-checkout>/.worktrees/<task-slug>. The path is relative to the primary checkout, not the current directory of an existing worktree. - Use a unique branch based on the latest fetched
main:feat/<task-slug>for features, improvements, and maintenance, orbug/<task-slug>for bug fixes. Follow additional repository naming requirements where compatible. - Never implement directly in the primary checkout or on
main. Never share a task worktree with unrelated work. - Inspect the current checkout and existing worktrees first. Preserve existing changes and branches. Resume an existing task worktree only after confirming it belongs to this task and is based on the intended branch.
- Ensure
.worktrees/is ignored before creating a worktree. Prefer an existing ignore rule; otherwise use the shared Gitinfo/excludeso setup does not require tracked changes in the primary checkout. - Run editing, dependency installation, builds, tests, and Git staging from the task worktree. Verify the worktree path and branch before mutations.
Typical setup, after identifying the correct remote and primary checkout:
git fetch <target-remote> main
git worktree add -b feat/<task-slug> <primary-checkout>/.worktrees/<task-slug> <target-remote>/main
cd <primary-checkout>/.worktrees/<task-slug>Use bug/<task-slug> instead for a bug fix. Replace the placeholders with verified values.
- Keep changes focused on the requested outcome and follow existing repository patterns.
- Run an appropriate baseline check when useful, then the tests, lint, type checks, and builds required by the repository and affected code. Add meaningful regression coverage for behavior changes where appropriate.
- Commit coherent progress at regular milestones and push each checkpoint to the task branch. Do not keep all work uncommitted or unpushed until the end of a long task. Clearly identify incomplete checkpoints in commit messages.
- Inspect the diff and stage only task-related files. Exclude credentials, local configuration, generated clutter, and
.worktrees/contents. - Set the branch's upstream on its first push. Push only the task branch; never push directly to
main. - Prefer additional commits over rewriting published history. Do not force-push unless explicitly authorized and consistent with repository rules.
- Report useful progress while continuing work. Recover from routine failures autonomously and preserve checkpoints if interrupted.
For every feat/ and bug/ change, documentation evaluation is mandatory before the work can be considered ready for review or merge. Do not assume that a small change or an internal bug fix has no documentation impact.
- Identify and evaluate the user guide, if one exists, including any guide published on Pages or GitHub Pages and the source used to generate it. Also evaluate the README and inventory the repository's other Markdown files to identify affected documentation, then read the relevant files. Follow documentation links to the applicable user-facing guidance.
- Compare the final implementation with documented behavior, setup, configuration, examples, troubleshooting, compatibility, and limitations. Check whether the change makes existing guidance incomplete, misleading, or incorrect.
- Update all affected documentation in the same task worktree and PR. Follow the repository's documentation structure and publishing workflow, including required source changes for its Pages site. Ensure the README, user guide, and other affected Markdown files agree.
- Validate changed documentation using the repository's applicable build, link, example, or formatting checks. Reevaluate documentation after review fixes or scope changes that alter behavior.
- Include a documentation assessment in the PR: which sources were evaluated, which were updated, and why. If no documentation update is needed, state the specific reason; silence is not an assessment. If a user guide or Pages site does not exist, record that and evaluate the documentation that does exist.
Do not waive this requirement because another rule labels documentation optional, excludes it from bug fixes, or discourages editing Markdown. If an existing guide or Pages site cannot be inspected or updated because of a real access or publishing constraint, report the blocker and keep the task open until the requirement can be fulfilled or the user explicitly changes it.
In particular, an instruction requiring an explicit request before Markdown edits must not prevent updates to the user guide, its publishing sources, or the root/default README.md. These updates are a normal part of completing the feature or bug fix.
Once implementation is ready and local validation passes:
- Push all intended changes.
- Open a PR from the task branch to the target repository's
main, or update the existing PR for this task. - Follow
.github/pull_request_template.md, title conventions, linked-issue requirements, and other repository rules. Include substantive## Release notesand a separate## Validationsection as required above. Describe the problem, resulting behavior, validation performed, documentation assessment, and material limitations. Keep CI-owned artifact details out of authored release notes. - Ensure the PR is ready for review, not left as a draft when implementation is complete.
- Monitor CI and fix failures caused by the change. Investigate other failures and follow the repository's policy; do not silently treat failed or pending required checks as passing.
- Run Claude and Codex once when the PR first becomes ready for review. Claude's initial workflow skips draft, external/fork, untrusted-author and previously Claude-reviewed PRs. Codex's opening review is enabled in the external repository integration, not an Actions workflow. Check that each initial review actually started before requesting a duplicate.
- After follow-up pushes, choose one provider for the new revision. Post a PR comment containing exactly
@claude reviewor@codex review; the latest authorized manual code-review request supersedes earlier code-provider requests. A separate@codex security reviewrequest still requires a current-head security completion. Claude can also be selected through Claude Code Review onmainwithpr_number: its authenticatedRUNNINGreceipt uses the workflow run's creation time to supersede older Codex code requests, while a later authorized comment can select Codex again. Only an actor with repository write, maintain or admin permission can request Claude; external/fork PRs require such a trusted on-demand request. - Wait for the selected reviewer to finish. Monitor PR reviews, comments, review threads and review status. A submitted request, acknowledgement, eyes reaction, elapsed time or absence of findings does not prove completion.
- Verify that the completed clean review applies to the full latest PR head SHA. Codex requires its PR thumbs-up reaction plus authenticated current-head completion metadata; Claude requires the trusted publisher's native current-head
APPROVEDreview. Ambiguous, incomplete, failed or stale evidence does not satisfy the gate. - Read both initial reviews and every subsequent finding, including inline comments. Process every finding regardless of severity.
- Fix valid findings, add relevant coverage, run affected validation, commit and push the corrections to the same branch. Reply in the corresponding thread with the resolution and supporting evidence. Resolve a thread only after its finding has been addressed.
- If a finding is incorrect or inapplicable, provide a concrete explanation and evidence in its thread and obtain reviewer acceptance or explicit maintainer disposition. Do not silently dismiss findings or mark them resolved just to enable merging.
- After any review-driven changes, obtain a clean Claude or Codex review of the new head. Alternate providers if useful; neither provider is required on every follow-up. Repeat fix, validate, push and review until the final revision qualifies.
- Avoid duplicate requests while a review of the same revision is running. Additional focused passes can help complex changes; complexity never removes the final review requirement.
- Close the review cycle only when the final full head SHA has a clean completed signoff, every finding has a documented disposition, every thread (including outdated threads) is resolved, and there are no outstanding review requests, observable pending reviews or changes-requested decisions. Provider review completion does not replace separately required GitHub approvals.
If review cannot start or finish because of access, configuration, service failure or quota, investigate diagnostics and report the concrete blocker. Keep the PR open and preserve the branch. Never substitute self-review or a timeout for the required final-head review.
Merge autonomously once all of these conditions hold:
- The requested work is complete and the final diff contains only intended changes.
- The user guide/Pages, README, and other Markdown documentation have been evaluated, necessary updates are included and validated, and the PR records the documentation assessment.
- Repository-specific guidance in
AGENTS.mdhas been populated or maintained as required by the task, with unresolved facts identified honestly. - Local validation and all required CI checks pass for the final revision.
- A clean Claude or Codex review is complete for the current full PR head SHA, with all findings addressed and review threads resolved.
- All repository-required approvals and merge conditions are satisfied.
- PR release notes are factual and match the final diff; validation is recorded separately, and any required release-tooling tests pass. Routine changes have not manually bumped release versions.
- The PR targets
main, is mergeable, and meets any requirements to be current with its base branch.
Refresh the PR state immediately before merging and verify that its head SHA still matches the validated and reviewed SHA. Use the repository's permitted merge method through GitHub. Do not bypass protections, use an administrative override, or merge an unreviewed revision.
If updating from main or resolving conflicts changes the PR head, push the update, rerun appropriate validation, and obtain a clean Claude or Codex review of that new head before merging. Any further change reopens the validation and review gates.
If the repository requires a merge queue, enqueue the eligible PR and monitor until it actually merges. Continue responding to failures or new findings. Enabling auto-merge or entering a queue does not itself complete the task.
- Verify on GitHub that the PR is merged into
mainand record the merge commit or squash commit SHA. - Monitor the automatic APK/core AAR release and Milkbeat update workflow. Verify reported publication/update results before claiming success. If a job fails, investigate and address task-related failures through another isolated branch and reviewed PR where appropriate, or report the concrete release/update blocker. Do not manually bypass the release pipeline or push fixes directly to
main. - Preserve unrelated worktrees, branches and uncommitted changes. The user authorized removal only of this task's
.worktrees/native-feedback-recoveryafter the merged/pushed/clean and verified-external-archive conditions in Worktrees are satisfied. Do not generalize that authorization to another task or force-remove unchecked tracked changes. Delete a branch only when repository policy and explicit user constraints permit. - Report the outcome concisely: what changed, relevant validation, PR link, final reviewer and review disposition, and merge confirmation.
- If a real blocker prevents completion, report the current branch, worktree, PR, completed checks, exact blocker, and next required action. Describe the task as blocked, not completed.
- Historical 0042 host 253/253 and ASan/UBSan 253/253 pass; 42 patches apply; scoped review fixes viewport and UV-map state errors. Direct authored-state test covers 20 animated geometry-free frames; full preset fidelity remains separate. First-frame blur fixture can emit a macOS zero-texture warning; image/GL assertions pass.
- Automatic quality JVM suite 42 tests passes (FPS/memory/migration/budget). Release tooling 82 tests passes. Revalidate after final integration/review changes.
- Focused actual-AAR workers:
tools/native-trails/README.md; no full-corpus claim. Released 2.3.3 Native/capped AARs are local historical controls only. Native AAR Acid 4K smoke 480 frames passed. Instrumented comparison, live AM6, final CI/Codex/merge/release remain required evidence, not established by these host results. - Owner authorizes awake rooted AM6 at 192.168.50.80 for live debug/profile in this task. Verify current Android user and media-session state 3; never wake remotely; restore task properties/profile preferences. Initial link briefly connected then went offline before player/user queries.
- Freeze TimeKeeper constructor/init origins as well as frame updates in private Preset Lab snapshots. Public
SetFrameTimealone stores a requested value untilUpdateTimers; actual-core JNI workers set frame time before both render calls when the pinned source provides that API. The delayed-process time/progress control repeats six frames exactly with the corrected clock and reports zero GL errors. - Derive engine/evaluator gitlinks from each requested source commit through private checkouts. Never substitute the live engine HEAD for a historical comparison. Preserve historical 24/44-patch controls and original producer identities. Use
rebase-baseline/rebase-candidateworker roles with their distinctcorpusrebasebaseline/corpusrebasecandidatepackages; do not touch unrelated corpus packages or reinterpret their frozen protocols. - Run
python -m pytest tools/native-trails tools/core-corpus -q(CI): current combined run passes 75 tests plus 35 subtests. Runpython -m pytest tools/preset-lab/tests -qwith the standard native worker and its build identity: recorded full run is 164/164; the later focused Android-header guard suite is 7/7. The private GL hook includes desktop GLAD outside Android/GLES builds and omits the optional discard hint when its declarations lack GL4.3 (also on Apple OpenGL4.1). Twelve worker controls and the full 170-test Preset Lab suite pass; compiled desktop GL3.3/GL4.3 branches and real NDK GLES API guards cover both the analysis hook and separate host-control shim. All 329 host engine controls pass. Rebuild into a fresh role directory after an instrumentation identity changes. - Keep copied native CPU shader bodies and their source hashes intact; include production logging declarations rather than stubbing copied calls. Both source adapters build and the recorded milk-analyzer suite passes 199 tests.
- Treat AM6's reported GLES 3.2 runtime as that driver's coverage, not proof for a GLES 3.0-only driver. The completed 1080p single-preset pilot repeats its eight selected captures within each role, but cross-role RGB byte MAE reaches 2.66 at frame 479 with localized differences. It does not establish a coordinate-change cause, full-corpus equivalence or a speedup. The historical TV matrix is superseded for fidelity readiness by the completed release-bound API34 GPU validation under the user’s TV waiver. Preserve its original captures/timings; final CI/review/release gates remain separate.
The current-series reference is docs/UPSTREAM_PATCH_VALUE.md; historical
dispositions remain in its linked archive. tools/patch-proof/README.md documents
fresh source-bound preparation, repeated direct-libprojectM EGL captures and an
offline source/binary/image verifier. The verified 2026-10-07 checkpoint uses the
task-owned API36 Android TV emulator-5630 (host GPU/HVF, M4 Pro/GLES3.0), not
the API34 phone image used for historical migration fidelity. Baseline source
matches observed upstream master e98fca85 before common deterministic hooks.
Patched image workers disable binary-cache export due to the API36 guest error;
do not infer cache validation, production RNG parity or app/Windows appearance.
Use fresh work directories and explicit serial/user arguments; keep failures and
source identities separate from successful images. The new original lone-dot
witness is Stahlregen's funky Blur base preset (plus an unrendered nz+ source
match); isolated controls distinguish accepted code from tolerant omission.
Retained-component proof maintenance (2026-10-08): the main patch reference embeds
component comparisons on the same page. Resource-correctness and image-preserving
optimizations need actual healthy render frames plus measured diagnostics; do not
invent an appearance gain. --shader-failure-probe observes actual linked shader
lifetimes; --texture-history-probe uses a shared, explicitly labeled allocation
fixture and reads actual attachment pixels/state/storage counts. New native
harness revisions require freshly prepared workers. Keep pooling opt-in and the
API36 program-binary export limitation explicit. The 23-row component matrix in
docs/superpowers/plans/2026-10-08-retained-component-proof.md remains incomplete.
Historical 14-patch synchronization (2026-10-08): PR #57/main 41ec3fc1 adds
current 0014 legacy tint and mode-1 waveform compatibility. current14-series.json
preserves that inventory; original image-proof workers/series.json retain the
frozen 13-patch source 654815d8. The 0014 Hurricane, tint and mode-1 comparisons
provide matched upstream/current14 proof. Do not relabel earlier captures as
14-patch certification; affected-witness revalidation remains pending.
Proof snapshot selection: tools/patch-proof/{prepare,capture,verify}.py accept
--series; pass current-series.json consistently for the locked 15-patch endpoint
at 120547f3, or current14-series.json for historical 14-patch replay at 41ec3fc1.
Their default series.json preserves frozen 13-patch replay. Manifest digests bind
new workers and captures to a selected source/inventory; old unbound receipts are restricted
to the original frozen manifest. Never relabel preserved workers as a newer
endpoint merely because upstream pin/early patch bytes match.
Original proof inputs: before remaining component captures, read
docs/superpowers/evidence/current-patch-proof/original-evidence-inputs/README.md
and its machine map. It joins original PR/commit evidence to preset types, exact
verified assets and activation profiles. PR14 reference1024×768 must not silently
become a16:9 reference; PR34 needs first-use/resize/distinct FBO controls, and
PR40 needs explicit authored/native-off/Standard/Medium/High sequences. Historical
screenshots/timings remain provenance, not current4.2 certification. Early
optimization commits omit some benchmark names; preserve that uncertainty.
Patch review-page presentation: keep docs/UPSTREAM_PATCH_VALUE.md as one readable
Markdown page with upstream/our-library images, optional matching zooms and short
natural-language explanations of the difference and cause. Move audit tables,
hashes, repeated validation/caveats and the expanded working assessment to linked
evidence. Preserve real upstream versus single-patch-removal labels; never relabel
an ablation as upstream. Human-review figures must match retained raw frame hashes
and pixel crops without brightness changes.
Publication scope lock (user instruction 2026-10-08): PR55's engine inventory is main120547f3,15patches ending0015. Finish the readable single-page comparisons for that set and publish; deeper optimization/lifecycle comparisons move to a follow-up. Do not add engine patches or reopen this scope for later work. Preserve immutable 13/14-patch capture identities; current14-series.json retains the14-patch manifest while current-series.json identifies the locked15-patch publication source.
Proof review maintenance (2026-10-08): new captures retain uploaded inputs and record their owned remote workspace; verification binds job/event paths to that workspace. Native workers validate and install the job seed before RNG setup. Capture retains and uploads the validated worker bytes, checks the remote hash, and points receipt identities at that retained executable. Offline verification also requires the trusted deterministic PCM digest and supported 16:9 dimensions. Fresh locked15 power/rotation replays match all prior RGB frames and pass full source/binary/input/pixel verification. Historical records remain unchanged; non-staged workers require a fresh replay for current certification. See tools/patch-proof/README.md for input/workspace options and retained worker requirements.
Execution evidence maintenance (2026-10-08): new proof runs retain execution.json exit receipts. Verifier checks rejected jobs and retained diagnostics, rejecting contradictory success artifacts. Evaluator proof requires the complete upstream, without-0003 and patched role set; historical subsets are not current certification.
Applied-control proof maintenance (2026-10-08): rendered manifests record normalized line-reference dimensions, AA and feedback override from the setter helper. Verification binds these fields, including upstream’s ignored TV requests. Locked15 4K Geiss classic/reference captures replace the active line figure; original13-patch line images/receipts remain under components/lines.
Proof label portability (2026-10-08): capture/verifier use the committed bitmap font under tools/patch-proof/assets, without default-font/FreeType selection. Label-only comparison reconstruction records its font/image identity separately from the original producer; framebuffer payloads remain unchanged.
PR55 main60 synchronization (2026-10-08): main eb1e7c16 adds0016; preserve the 15-patch image checkpoint at120547f3 and link the separate AM6 performance evidence, as selected by the user. All16 patches apply in an isolated export. 137 proof controls, native engine/JNI checks,37 ASan/UBSan regression cases on macOS OpenGL and strict MkDocs pass. The separate EGL transition-overlay check was not run on macOS. These checks do not turn the frozen images into16-patch GPU certification.
Latest audit integration (2026-10-08): synchronize main af164a97 including released0016-cache-mesh-init-per-viewport, retaining its static upload policy and FinalComposite cache. Audit patches renumber to0017–0025;0025 only extends static key with aspect/compiled path and selects the legacy diagonal. Frozen source/artifact/PNG evidence still uses historical numbers and is not relabeled. Source, Native and final PR gates must be checked on this integrated head.
I12 candidate0026 (2026-10-08): actual legacy programs traverse physical bottom-to-top/X ascending, writing original slots; compiled custom and no-equation order unchanged. Preserve one per-frame Q copy, carried ordinary/reg/global-buffer/RNG effects, same-node inputs, CPU trig/negative power, released static uploads and replay. Production node-placement/two-frame/stateless/custom/fallback/replay RED→GREEN;50 normal controls and26-patch application pass. Native original/finite/cost and integration pending. See I12.
I02/M01 candidate0027 (2026-10-08): shared GetBool uses parsed integer nonzero, preserving prefix/default/case/first-occurrence behavior. Local analyzer scalar mirrors it. Actual production parser/wave/frame/point/Native replay RED→GREEN;53 normal renderer and21 focused analyzer controls pass. No recognized negative first-occurrence stock flags among9,606 files. Negative external enables activate authored work; Native4K/sampler/cost/final integration remain open. See boolean evidence. I12 original/finite/custom final-output runs now repeat exactly; isolated timing and final integration remain open.
Boolean source followup: known-gradient displaced-UV production sampler RED→GREEN proves negative=positive repeat versus zero clamp, including prepared replay. Current54/54 normal renderer controls pass; ARM64 source-instrumented AAR/worker630a6a4b built. Android4K finite invert/wave controls remain active, not accepted yet.
I20 candidate0028 (2026-10-08): source-unblended circle denominator239 and explicit closure before smoothing,241raw/481smoothed/480segments, strip topology. Preserve Native styles/density/opacity and replay; do not introduce original geometry-blending state into TV independent-preset composition. Actual production geometry andCGL draw/replay RED167→GREEN;55 normal controls and28-patch application pass. Original/finite Native4K and cost remain pending. See I20.
Boolean disposition (2026-10-08): I02/M01 Native4K24finite runs repeat exactly; negativeafter=positive,negativebefore=zero,zero/positiveunchanged. Clean12ABBA negative-wave cost+.102019ms/+7.503% (all3cyclespositive). Shared proposal withdrawn from shipping patch series and local analyzer restored to positive-only current policy; owner packet includes actual before/source-corrected images, sampler/source proofs and cost. Frozen historical candidate0027/source630a6a4b identities are unchanged. Circle live patch renumbers0028→0027; frozen source55 controls retain historical context. Fresh51 shipping-series controls and27-patch application must be checked.
Current audit checkpoint5d721fba (2026-10-08): shipping-series27-patch application and51 normal+51 ASan/UBSan renderer controls pass, strict docs pass. CircleARM64 source-instrumented AAR/worker built; original Native4K and cost gates remain active. No claim about both-ABI shipping artifacts, full analyzer/JVM/CI or full33 audit completion.
I20 disposition (2026-10-08):20 Native4K runs(two originals+thin/thick/dots) repeat exactly. Isolated12ABBA Royal137 mean1.712315→1.770416ms,+.058101/+3.393%,cycles+7.069/+5.715/−2.504%; no-performance-impact gate unproven. Proposal withdrawn outside shipping series; exact source/GL/sanitizer/Native/cost owner packet retained. Current series returns to26 patches;50 normal/sanitizer reruns pending. Historical0027/0028 artifact identities are unchanged.
Post-deferral shipping checks (2026-10-08):50/50 normal and50/50 ASan/UBSan renderer controls, fresh26-patch application pass. I13 is source-stage only; I14 separates bilinear/triangle/storage at actual UV dimensions. I16 has no production GetUV API: motion shader queries the previous published texture, which can be stale on re-enable. Its proposed UV-only replay needs retained previous uniforms/path/generations and a discard policy; existing DrawAgain is unsafe across frames. No new motion policy is accepted.
I13 candidate0027 (2026-10-08): exact legacy/default/fallback left-axis equations consume signed cached angle; custom and off-axis negation unchanged. Raw radius/angle shader attributes, CPU float trig, power/traversal/cache/replay remain intact. Actual EEL→CPU trig→GPU UV CGL RED→GREEN,51 normal controls and27-patch application pass. Odd-grid activity test uses a covered point because existing quadrant indexing leaves a center gap; no odd-grid repair. Native4K/cost/final integration pending.
I13 focused acceptance (2026-10-08):51 normal/51 ASan/UBSan,27-patch application;16 Native4K original/finite/custom jobs repeat exactly with custom unchanged;12 isolated Liquido ABBA mean3.217472→3.086516ms, mixed cycles/no consistent slowdown. Raw shader angles/custom behavior and previous fixes remain intact. Final integration/CI and physical-TV performance remain unverified.
I18 candidate0028 (2026-10-08): ordered local float RGB clamp before nonzero brightening, raw frame RGB preserved. Actual constant/endpoint RGBA, primitive/count/style/geometry, additive, opacity thresholds and replay controls RED→GREEN;52 normal and28-patch application pass. Negative darken is separate outside shipping patches, including extra draws/NaN predicate boundaries. Native4K/cost/current sanitizer/final integration pending.
I18 focused acceptance (2026-10-08):52 normal/52 ASan/UBSan and28-patch application; unchanged Geiss BlurMix3 brightening palette and finite positive-HDR/negative-dim/compiled-blue/inrange Native4K repeats pass. Unbrightened HDR/suksma captures remain identical on this RGBA8 backend; prior source-only blending prediction is superseded. Twelve isolated BlurMix3 ABBA mean2.036141→1.923487ms, mixed cycles/no consistent slowdown. Preserve raw frame RGB, geometry, alpha, sample density and replay. Negative darken remains outside shipping patches.
I22 disposition (2026-10-08): fresh final-read0 exact-source mosaic12ABBA mean2.081861→2.186736ms,+.104875/+5.038%,all3cyclesincrease. Defer complete discrete/single-dot proposal with original/finite final4K images and all older timing evidence; withdraw canonical0019 anddot-specific tests. Later live0020–0028renumber0019–0027; frozen artifacts/helpers keep original numbers. Current shipping source normal50/sanitizer50/27patch checks pending. Preserve remaining custom-window/read-only tests and all earlier fixes.
Post-I22 shipping verification (2026-10-08):50 normal/50 ASan/UBSan,27-patch application pass after rollback/renumber. LiveI13angle=0026, I18RGB=0027. Frozen prior 0027/0028/source artifacts remain unchanged; use ledgercurrent_patch_number for live mapping. Strict docs/final CI/currentbothABI/JVM/host/whole-audit completion remain separate gates.
I01 retained casing policy (2026-10-08): current actual parser controls and40 Native4K/matched256x144 runs pass;20 selected-RGB repeat groups exact. Keep case-insensitive key/code-prefix loading and first normalized occurrence. Finite ZOOM2 border differs from explicit original-source defaultzoom1; both supplied unchanged PyroCybin originals match strict-default surrogates in selected frames. No engine change or broad affected-preset claim. Sourceac3 current27/evidence identity frozen; see I01 policy.
Capture audio provenance (2026-10-08): FeedAudio queries projectm_pcm_get_max_samples(); upstream4.2 returns AudioBufferSamples576, separate from SpectrumSamples512. Frozen Android manifests containing latest512 are stale descriptive prose, preserved byte-for-byte with correction. Future worker manifests name the queried API boundary. Do not reconstruct JNI numerical input from the old512 label.
I28 retained blur policy (2026-10-08): keep0005 expansion/coherentfallback. Current numeric/storage/decode normal+sanitizer controls pass;24Native4K runs/12selectedRGBrepeatgroups pass. Three collapsed-bound fixtures match finiteexpanded siblings and unsupportedfinite matchesdefault. Originalzero-denominator storage is not a pixeloracle; blackreference is decodercoefficient-only. See I28.
I07 retained stereo policy (2026-10-08): actual currentPCM576-tail mono controls over480frames showexactaveraged/left-onlycombinationinvariance; actualstereostep/swap/right-only differs. SixNative4Kruns repeat; stereoimages are explicitly frozen executed-stage coefficients, not stereoJNI. Retain upstreambothchannelanalysis, preserve monoTV bridge andfresh/fullringhistory caveat. See I07.
I21 retained16-modepolicy (2026-10-08): productionCGLfactory/alias/invalid-recovery/Native replay controls pass;38Native4K runs(includingblue compilemarker) repeat. Exact frozenac3 ARM64 archive sourceproducer uses queried576 tails/labdt0 then frame/30 deltas/.75default smoothing, all7680factory/frameoutputs finite. Initialsmoothing0source-onlytrace preserved; no hiddenJNIarray claim. No selectorpatch; see I21.
I25/I30 retainedprecisionpolicy (2026-10-08): productionshape/wave/displayRGBA/pass/replay and completed-grid pre-interpolationbyte replay pass;48Native4Kruns/24repeatgroups pass. Keep floats/subbyte alpha andpriorstyles. Gammaoraclespixelidentical;echo uniformwhite255vsgainsurrogate254; compositebyte replay isCGL64x64. No Androidbyte-grid/dynamicstockdouble claim; futurecompatibility gates in owner decision.
I26/I27 retainedshaderinputpolicies (2026-10-08): compiledproductionuniform/source/readback controls pass;34Native4K runs repeat. Keep immutableallbandvol/att andheightmip/Native reference reporting; equalbandsarenotcomma-invariant. Cope1shaderline sourcecomma surrogate differs(maxMAE30.550332);miplive matches1280x720oracle. No hiddenJNI/Windows/census claim. See owner decision.
I23 retainedoffsetpolicy (2026-10-08): actualshaderposition/count/replay controls andshared44Native4Kruns pass; bothlivewave/shape match explicitcurrent-offsetsiblings inall8selectedframes. Originalone-reference-pixel siblingsdiffer; noWindowsphysical4Kclaim. KeepNative/referencehalf-pixelpolicy andcommonshapebias; I15minimumremainsseparate. See I23.
I03 deferred recovery (2026-10-08): real combined198 host/ARM controls and12Nativefinite jobs pass;36runtimeQABBAjobs/allselectedRGB verified. Tiny-basepow mean1.468158→1.578336ms,+7.50%/allcyclesincrease; ordinarycombined+8.22%. Literaloperator benchmarks foldatloadtime and are retainedseparately. Do notshipI03under no-costgate; I04qualification remainsindependent. See owner packet.
I15 retained minimum (2026-10-08): unchanged Rovastar Parallelogram Bin2/minimum-only source oracle Native4K runs repeat exactly;32 selected PNGs verified, max selected RGB MAE55.869. Retain current aspect/diffusion visibility minimum; original undefined domains unchanged. See I15 owner packet.
M02 focused acceptance (2026-10-08): current0028 changes only border indices to original fan topology. Baseline4/46 GL failures become46/46;51 normal/51 ASan/UBSan and28-patch application pass.24 Native4K finite runs repeat, four unaffected control profiles match;12 isolated ABBA mean1.679363→1.680356ms, mixed cycles/no consistent slowdown. Retain float colour/alpha and prior Native policies. Predictor contract is versioned; external predictor worktree untouched. Stock impact/physical-TV and final full integration remain unverified. See M02 evidence.
I14 deferred reconstruction (2026-10-08): production layered interpolation/storage/filter/endpoint controls pass; corrected nMotionVectorsX/Y finiteNative48x32 fixture has4 Native4K jobs/2exact repeats/32PNGs. Frame239 analytic-bilerp oracle changes67,636pixels/MAE.298407; oracle is invalid for arbitrary presets and must never ship. V1 used ignored grid filekeys/default grid and is preserved separately. No general original-node producer or cost-qualified path exists; keep prior GPU/Native policies. See owner decision.
I16 deferred freshness (2026-10-08): actual full-pipeline8sequences pass;12cadenceNative4K jobs/6repeats/96PNGs show bounded custom stale-field geometry. Defaultzero-decay images are black and not visible-impact proof.12isolated all-disabled ABBA mean1.403056→1.401440ms/mixedcycles shows no consistent slowdown, but eager publication removes existing disabled-write optimization; extra logical1280x720UV writes3.515625MiB/frame are not extra resident allocation. Generic lazy producer/resource contract remains unqualified. See owner decision.
I24 deferred live thickness (2026-10-08): actual per-instance/source49/replay CGL controls pass;20 Native4K original/finite jobs/10exact repeats/160PNGs verify.24 qualified ABBA jobs/192PNGs: city-c mean6.081853→6.171351ms,+1.472%/mixed(-1.461,-1.461,+7.080); alternate-2.552%. No-loss gate across both originals remains unproven; grouped refinement adds bounded scratch/upload/raster qualification. ENOSPC/cleanup-overlap partial datasets are retained and excluded. No I24 shipping patch. See owner packet.
I04 deferred bounded remainder (2026-10-09): v2/v3 preserve flags/other math and20baseline boundary observations;76+12 controls passhost/ARM64, extended92remainder cases cover signed OR-missed fallback. Eachvariant4Native sign jobs repeat; v3matchesv2allselectedRGB perrole.96independent ABBAjobs/768PNGs: v2remainder+1.537%; v3remainder+.623%,ordinary+.837%,preservedpow+2.871% withmixedcycles. No consistent universal regression claim, but no-loss gate unproven. No canonical arithmetic patch. All33 audit IDs now classified(13repaired/10retained/10deferred); final combined shipping integration/CI/review remains separate. See owner packet.
All33 audit disposition checkpoint (2026-10-09):13repaired IDs/12new shipping patches0017–0028,10retained policies and10completed deferred owner packets. Fresh329host,51ASan/UBSanrenderer+nativeengine/JNI,137releaseJVM,53capture/helper+24subtests,28-patch application, bothABIreleaseAAR/debugAPK andstrictMkDocs pass. Final Border.cpp bytes match frozenM02sourceproof; artifact hashes separate. macOS standaloneEGLtransition skipped; Dockerdaemon unavailable, so Linux/Mesa/explicitGL_LIBS route is not locally certified. FinalCI/externalreview andPRready/merge/publication remain separate; no corpusrestart/versionbump. See combined evidence.
Authored fidelity followup (2026-10-09): user accepts necessary authored-work cost for I19/I22/I20 and requests production I24/I16 through a new merge-ready PR. Candidate0029–0033 restore line sample caps, discrete custom dots/single-dot NaN inputs, circle closure, per-instance evaluated thickness and the latest previous real-warp field. Preserve earlier0001–0028, raw equation values, prepared replay, fragment semantics and ownership; no API/version/dependency/preset changes. Current source checks pass329 host,63 normal/sanitizer renderer,137 JVM and60 capture/helper tests plus24 subtests; both ARM builds pass.76 focused Native jobs verify608 selected final-output captures and repeats. Four active-canvas Native4K JNI lifecycle jobs verify120 boundary captures/repeats; original zero-reference jobs remain fallback-only evidence. Owned API34 emulator5640 only; Standard1280x720 reference, Native3840x2160, frozen audio/seed/clock and exact identities. Source-instrumented artifacts are distinct from shipping builds. Combined isolated cost, recursive/analyzer/docs checks, final main integration and new PR CI/review remain open. No physical-TV headroom, whole-corpus or Windows-pixel claim. See active evidence; the historical33-finding ledger is not relabelled.
Predictor accuracy documentation (2026-10-09): docs/user-guide/authoring/testing.md
section5 separates20-claim behavioural grades, full-sample versus conditional
pass rates (which do not replace average agreement), numerical tolerances, visual judgment and independent reference
qualification. Its worked97.5example and100-case totals cite immutable predictor
commit b3737a564f4b937bd33959e17bb61dbe4eb11304. Keep historical unknowns in the
original denominator; do not describe the47-field source export or a completed
corpus simulation as a fresh visual-accuracy audit. The predictor overview links
to this explanation. No predictor/app/AAR behavior is changed by this guide update.
Predictor guide (2026-10-16): the Predictor section (docs/user-guide/predictor.md, docs/user-guide/predictor/*.md, predictor-export.md) documents the static analysis behind the shipped moods; the earlier numerical feature-record contract and its assets/predictor-export/ downloads (2026-10-08 checkpoint, predictor commit fdfd09e2) were removed and are referenced only through the repository-history permalink on predictor-export.md. Gallery images, clips and frame strips live in docs/user-guide/images/predictor/; clips are omitted for presets with 10 or more ruler jumps per 10 s (flashing). Keep guide accuracy figures tied to the shipped model's out-of-fold scoreboard (research branch docs/superpowers/evidence/facet-readiness-2026-10-15/facet_scoreboard2.py) and regenerate counts from the static evidence when the table changes. Validate with the strict MkDocs build.
Authored followup final local acceptance (2026-10-09): integrated docs-only mainaf4b520f and retained all33 patch bytes. Isolated seven-workload84-job ABBA verifies672 selected final-output captures against focused witnesses. Largest overall measured added timeRoyal103 +.343ms/+6.96%; other workloads-.190 to+.135ms, with varied cycle deltas. User-approved necessary authored work keeps all five fixes; no physical-TV headroom or summed-percent claim. Fresh clean recursive coreDebug/releaseAPK build at6a016447,206 source-analyzer tests, both production warp GLSL ES3 links and strict MkDocs pass. New PR/current-head GitHub review/CI remain open; do not merge/publish as part of this task. See combined cost.
PR65 P1 compatibility correction: patch0034 gates optional RG16F motion-map allocation before clear/publication, using core/extension capability and a mixed-MRT probe. Rejected GLES backends use required RG16UI half words, typed clear and decoded bilinear consumers; capable RG16F shader paths remain unchanged.70 Linux normal controls and63 macOS normal/sanitizer controls pass, including targeted fractional/nonfinite/edge/capability cases. User explicitly limits further review-fix validation to bug-specific checks; do not restart broad local suites or capture matrices. Existing33-patch evidence remains a historical checkpoint. Corrected Native worker builds; required current-head external review/CI remains separate.
I31 benefit proof (2026-10-09): exact0019 ablation on native Apple M4 Pro/GL4.1 at3840×2160 verifies3→2 gamma draws,80 timed jobs/28,800 measured frames and zero GL errors. Standard-profile completed-frame mean2.704→2.241ms and GPU elapsed2.307→1.847ms; all8paired blocks favor the patch. Gamma2.0 control GPU/complete intervals include zero. Separate3-selected-frame/two-repeat comparisons are RGB-identical in Classic and Standard profiles. Retain0019 on this measured backend/boundary evidence, without universal/physical-TV FPS claims. See benefit evidence.
PR66 review closeout (2026-10-09): gallery verification follows committed inputs through all104 full original requests and independent RGB repeats to actual guide figures/role labels/crop CSS. I31 custody reconstructs frozen source plus exact0019 reversal and regenerates all means/intervals from unchanged timed records. Corrected selected-frame observers preserve read framebuffer/read buffer/pack alignment; eight scoped replays/24 states retain identical RGB hashes. Use the evidence verifiers for bounded checks; do not restart the gallery or80-job timing experiment for metadata/guard changes. Final-head external review and required CI remain separate readiness gates.
Patch-proof presentation (2026-10-09): the causal close-ups in docs/superpowers/evidence/patch-proof-clarity/ use unchanged real-preset Native3840×2160 RGB frames and two independent repeats per role. Keep whole-master context separate from one-repair ablations; preserve matching crops and brightness. 0023 disables only its compile-time oscillator branch to retain later packed-motion guards; other displayed roles reverse their target patch exactly. Cropped 16:9 source images reserve their aspect ratio before lazy loading so positioned zooms do not remain blank. Validate retained RGB/source/request/guide bindings with verify.py, and preserve the earlier catalog and I31 cost evidence. This is desktop witness evidence, not a physical-TV performance claim.
Field confirmation (2026-10-18): the reporter confirms the released fix resolves the original AFTKRT / Fire TV Stick 4K Max 2nd-generation GE9215 crashes. Minimum versions are ProjectM 2.3.56 and Milkbeat 1.0.13. The predicate covers all supported Imagination/PowerVR renderers, not a model whitelist. Keep confirmed field results separate from same-GPU model matches and unrelated blit errors on other vendors; see coverage/research. ProjectM Android's phone/tablet F-Droid listing is live at https://f-droid.org/en/packages/nl.neerdael.projectm/; verified catalog version 2.3.49/code 98 on 2026-10-18 predates the PowerVR fix. The TV API/catalog entry is not live at this checkpoint. Document catalog availability separately from GitHub F-Droid-flavor APK publication; do not imply every catalog version contains the fix.
The reporter's 2.3.55 ARM native report identifies glBlitFramebuffer from Renderer::Framebuffer::SetSize, including crashes during unchanged-preset playback. The released module build ID matches; the rebuild's complete .text bytes and addresses match the released library, supporting source binding despite different complete build IDs. Patch0038 uses existing Shader/Sampler classes for cached textured copies on Imagination/PowerVR contexts in resizing, retained-detail recombination and host-scaled presentation. Preserve primary colour formats, auxiliary integer UV data, copy state and the context-forget generation. Resize all attachments before preserving colour zero. On the ordinary blit path, detach the read source before releasing its old texture: the Android GLES translator otherwise detached an unrelated draw image in the control. Native host helpers are projectm_opengl_needs_framebuffer_copy_workaround and projectm_opengl_copy_texture_to_framebuffer. The focused powervr-blit real-GL control covers pixels through4K, MRT routing, cached reuse, touched state, actual C API/default GLES presentation, Native trails and replacement contexts. See source/validation evidence; no affected-device success claim follows from host/emulator vendor selection. Update the archive lock after editing docs/THIRD_PARTY.md.
Reporter feedback: AFTKRT crashes after the first real preset, including Lightweight/0s and with both diagnostic switches disabled. Those UI switches are retired; app startup restores normal enabled compilation/cache behavior even for old saved Off preferences. Keep JNI setters for existing AAR consumers and historical trail parsing. Patch0036 implements PowerVR/Imagination framebuffer deletion protection based on Chromium/Skia's complete-guard-FBO workaround: one 1x1 guard per rendering thread/context, retained until context destruction; preserve surviving split read/draw bindings. Reset before stale-instance destruction on replacement context (projectm_opengl_forget_framebuffer_guard; the existing texture-pool-forget hook also forgets it). Route host FBO deletion through projectm_opengl_delete_framebuffers. No unverified Fire TV fix claim: real-Mac-GL interception proves 124 unsafe deletions without protection versus zero with it; all65 sanitizer renderer controls pass.
ProjectMCore.init installs source-built xCrash once; readiness requires the supported native-loader callback to run for this initialization and the private directory to match. A prior xCrash initialization, including a failed one, is reported as unavailable; foreign settings/reports are not adopted. NativeCrashReports reads at most five bounded native tombstones from private no_backup/projectm-crashes, formats actual signal/module-relative backtrace/build IDs, and labels missing stacks. No telemetry or upload. Consumers must retain extractNativeLibs=true and set packagingOptions.jniLibs.useLegacyPackaging=true for the packaged dumper executable. Pinned source provenance, age and licenses are in third_party/xcrash/PROVENANCE.md; its classes and both ARM native outputs are built directly into the self-contained AAR. Optional logcat/FD/network/map/other-thread collection and Java/ANR handlers are disabled; raw stack/register fragments still exist in private files. Unit reader controls pass; an isolated API34 ARM64 emulator confirms delivered SIGSEGV process death, saved native report and UI display after restart. This does not validate the affected PowerVR device. Reproduce only in a separate setup build with setup_case native_crash_capture then native_crash_report.
Historical 2026-10-09 checkpoint follows:
A Fire TV Stick 4K Max (2nd gen; MediaTek MT8696T, PowerVR GE9215, Fire OS 8 = API 30, 2 GB) closes at most preset changes, sometimes with Instant transitions, while the background music app survives. No adb or log is available; no PowerVR device has been tested here. The unconfirmed suspects are (1) the background prewarm thread's second, non-shared EGL context compiling concurrently and its program binaries being restored with glProgramBinary in the render context (patch 0001 ProgramCache), and (2) GPU-driver memory exhaustion during blends. Shipped default-on troubleshooting switches through the normal release: Advanced › Background compile (background_compile, ProjectMJNI.setBackgroundCompile; stops/starts PresetPrewarmer and its EGL context on the GL thread) and Advanced › Shader binary cache (shader_binary_cache, ProjectMJNI.setShaderBinaryCache → patch 0035 projectm_opengl_set_program_cache_enabled). Advanced › Last exit combines ActivityManager.getHistoricalProcessExitReasons (API 30+) with the native trail, each line attached only to the exit whose setProcessStateSummary session ID it carries (fallback when an exit recorded none: earliest same-PID exit at or after the line; PIDs are reused and listener-only processes never rewrite the trail) and carrying that process's switch states (cache= / compile=), so reports never use today's settings for a past exit; the Diagnostics card shows GL_RENDERER (VisualizerRenderer.getGlRenderer). Core API additions are additive. If the reporter's results isolate the cause, a follow-up should fix it for that GPU family rather than ship a permanent user switch; a staged (prepare/activate) load on the render thread is the fallback when concurrent compiling itself is unsafe. Validation: program-cache-switch real-GL control (binary loads exercised only on GLES contexts that report binary formats), engine trail/switch checks in engine_test.cpp, ExitDiagnosticsTest; no Fire TV or PowerVR run.