Skip to content

fix: a transient decoder loss no longer drops the learnt capacity to the floor (#189) - #190

Merged
compot-dev merged 3 commits into
v2from
fix/planner-transient-189
Oct 15, 2026
Merged

compot-dev merged 3 commits into
v2from
fix/planner-transient-189

Conversation

@compot-dev

@compot-dev compot-dev commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #189.

Changes (DecodePlanner)

  • Transient losses: losses at a load below proven (a load that played PROBE_QUIET_MS without losses) demote the lowest full cell as before, but proven, failedLoad and the capacity (kept ≥ proven) stay. Repeated transients: a transient is forgiven once. The next below-proven demotion means proven itself is wrong and is handled as before (failed load set, proven cut, capacity lowered; floor of Planner: learnt decoder capacity can be poisoned by a system stall and blocks full rate for an hour #126 kept), unless a load at least as high as proven has played PROBE_QUIET_MS without losses again in between (Qodo on fix: a transient decoder loss no longer drops the learnt capacity to the floor (#189) #190: no time window, so losses spaced far apart still count).
  • Failed-load retry: FAILED_RETRY_MS (1 h) is timed from the last demotion (new lastFailMs, also moved by the retry itself), no longer from quietSinceMs, which any lossy sample resets.
  • Promotion: when the highest-priority key-frame cell is held back (≥ failedLoad × FAILED_MARGIN, or no fit and no probe due), the first lower-priority cell that fits within the capacity and stays below the failed-load margin is promoted. Probes beyond the capacity and the failed-load retry stay with the highest-priority cell; the UNKNOWN_WAIT_MS wait is unchanged.
  • Decoder.java unchanged: it persists capacity / failed load as before.
  • Version 2.10 (35).

Tests

New: transient keeps capacity and failed load (the #189 timeline 312 338 → 239 719), repeated transient falls back (also 15 min later), a transient is forgiven again after the proven load plays quietly again, a light cell is promoted in the #189 state (capacity 244 800, failed 239 719 in the session, nine key-frame cells), probes keep priority order, retry an hour after the demotion despite sparse losses. Existing tests unchanged.

Verify on device (.144, AG-216)

…the floor (#189)

- Losses at a load below a proven one (played PROBE_QUIET_MS without losses) count as a
  transient: the lowest full cell is demoted as before, but proven, failedLoad and the
  capacity (at least proven) stay. A second such demotion within TRANSIENT_WINDOW_MS
  (10 min) means the proven load was wrong: handled as any failure, as before.
- The retry of a failed load (FAILED_RETRY_MS) is timed from the last demotion (or the last
  retry), not from the last lossy sample, which key-frame cells keep resetting.
- When the highest-priority key-frame cell is held back (a failed load, or it does not fit
  and no probe is due), the first lower-priority cell that fits within the capacity and
  stays below the failed load is promoted. Probes and retries stay with the first cell.

Unit tests for each case, including the #189 timeline and stored values. No existing test
changed: none relied on the changed behaviour. versionName 2.10, versionCode 35.
@compot-dev compot-dev added the awaiting device test PR ready, merge only after testing on a real device label Oct 15, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Preserve learned decoder capacity after transient losses

🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Preserve proven decoder capacity after isolated losses; treat repeated losses as a failed load.
• Promote safe lower-priority cells when the first is blocked, while preserving probe and retry
 priority.
• Time failed-load retries from demotions and release the fix as version 2.10.
Diagram

graph TD
  A["Cell samples"] --> B{"Loss threshold?"} -->|Yes| C["Demote cell"] --> D["Protect or revise limits"]
  B -->|No losses| E["Scan priorities"] --> F{"Safe fit?"} -->|Yes| G["Promote cell"]
  F -->|Priority probe or retry| G
Loading
High-Level Assessment

The targeted planner change is appropriate: it retains the existing learned-capacity and persistence model while bounding transient tolerance to one demotion per ten-minute window. Broader capacity-smoothing or persistence changes would add tuning and migration risk without addressing the blocked lower-priority cells as directly.

Files changed (3) +184 / -13

Bug fix (1) +53 / -11
DecodePlanner.javaHandle transient losses and unblock safe promotions +53/-11

Handle transient losses and unblock safe promotions

• An isolated below-proven loss demotes a cell without discarding proven capacity or the failed-load threshold; another within ten minutes follows the normal failure path. Failed-load retries now run from the last demotion or retry, and lower-priority cells may be promoted when they fit safely while probes and retries remain first-cell decisions.

android/app/src/main/java/com/openipc/decoder/DecodePlanner.java

Tests (1) +129 / -0
DecodePlannerTest.javaCover transient, promotion, probe, and retry scenarios +129/-0

Cover transient, promotion, probe, and retry scenarios

• Adds regression tests for the reported load timeline and stored values, repeated below-proven losses, safe lower-priority promotions, priority-ordered probes, and retries despite intermittent losses.

android/app/src/test/java/com/openipc/decoder/DecodePlannerTest.java

Other (1) +2 / -2
build.gradleBump Android release to 2.10 (35) +2/-2

Bump Android release to 2.10 (35)

• Increments the application version name and code for the decoder-planning fix.

android/app/build.gradle

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Recurring losses never lower capacity ✓ Resolved
Description
step() treats each below-proven demotion as a fresh transient once TRANSIENT_WINDOW_MS has
elapsed, leaving proven and failedLoad unchanged and keeping capacity at least as high as
proven. If a full-rate load repeatedly loses frames at intervals just over ten minutes, the
planner can promote it again after each demotion without ever learning that the load fails.
Code

android/app/src/main/java/com/openipc/decoder/DecodePlanner.java[R224-227]

+                    boolean repeated = lastTransientMs >= 0
+                            && nowMs - lastTransientMs < TRANSIENT_WINDOW_MS;
+                    if (load < proven && !repeated) {
+                        lastTransientMs = nowMs;
Evidence
The new repeat check uses only the previous transient timestamp. Once ten minutes pass, the branch
refreshes that timestamp while preserving the values that would otherwise be reduced by the ordinary
failure path; the new tests exercise only a second loss within three minutes.

android/app/src/main/java/com/openipc/decoder/DecodePlanner.java[221-244]
android/app/src/main/java/com/openipc/decoder/DecodePlanner.java[292-308]
android/app/src/test/java/com/openipc/decoder/DecodePlannerTest.java[454-473]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Below-proven demotions spaced more than ten minutes apart are each treated as a first transient, so a repeatedly failing load can retain its proven capacity indefinitely.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/DecodePlanner.java[224-231]
- android/app/src/test/java/com/openipc/decoder/DecodePlannerTest.java[454-473]
## Recommended Fix
Keep the first isolated loss transient, but track recurring below-proven demotions across retry cycles and eventually use the ordinary failure path unless the load has been successfully reproven. Add a test with repeated losses spaced beyond the transient window.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread android/app/src/main/java/com/openipc/decoder/DecodePlanner.java Outdated
…ad plays quietly again, not once per 10 minutes (Qodo on #190)
@compot-dev

Copy link
Copy Markdown
Collaborator Author

Device test 2026-10-15 on Aceline-1 (AG-216, .144): passed.

  • Before (v2.09, the same app process for about an hour after a loss at 239 719): capacity 244800, failed_load 239719, 0 of 9 cells at full rate.
  • After installing this PR (d659389), within 2 min: cells 1, 2, 8 and U at full rate, capacity 452913. There was one short demotion on the way; it did not drop the capacity.
  • Stress, the same process: 60 cycles of grid → slot 4 single → grid (the ANR on AG-216: RenderThread blocked in eglSwapBuffers with 8 multi-view SurfaceViews #76 script). 0 ANR, longest Davey! 0.95 s, none ≥ 2 s.
  • Afterwards, 4 min in the grid: cells 1, 2, 8 and U still at full rate, capacity 467 608–483 299, failed_load 706517 (above the capacity, so it holds nothing back).

@compot-dev compot-dev removed the awaiting device test PR ready, merge only after testing on a real device label Oct 15, 2026
@compot-dev
compot-dev merged commit c9f678c into v2 Oct 15, 2026
4 checks passed
@compot-dev
compot-dev deleted the fix/planner-transient-189 branch October 15, 2026 16:03
compot-dev pushed a commit that referenced this pull request Oct 15, 2026
compot-dev pushed a commit that referenced this pull request Oct 15, 2026
…ad plays quietly again, not once per 10 minutes (Qodo on #190)
compot-dev added a commit that referenced this pull request Oct 15, 2026
fix: a transient decoder loss no longer drops the learnt capacity to the floor (#189)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-view: one transient loss drops the learnt capacity to the floor and every cell stays on key frames

2 participants