From 6ed75920df3b584ccf3921f15aea1cf0dac45c44 Mon Sep 17 00:00:00 2001 From: luisguzman-adfa Date: Wed, 24 Jun 2026 01:26:43 +0000 Subject: [PATCH 1/2] fix(lint): disable hanging androidx.fragment lifecycle detector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :app:lintDebug hangs for hours inside androidx.fragment's UnsafeFragmentLifecycleObserverDetector — its recursive call-graph walk degrades to near-exponential cost on our oversized Fragment/Activity methods (DeployFragment ~2.7k LOC, MainActivity). CI's lint step ran ~50 min, up to 3 h on heavy branches. Disable the detector's 3 issue IDs (FragmentBackPressedCallback, FragmentLiveDataObserve, FragmentAddMenuProvider) — the only ones it emits — so lint completes in minutes. The other 450+ checks (and abortOnError + baseline) stay active. Re-enable once the god classes are split (TECH_DEBT_PLAN.md). --- controller/app/build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/controller/app/build.gradle b/controller/app/build.gradle index e6128b1..fd7316a 100644 --- a/controller/app/build.gradle +++ b/controller/app/build.gradle @@ -111,6 +111,15 @@ android { // the build — en/es are primary and translations land incrementally. Still // reported as a warning so they stay visible. warning 'MissingTranslation' + + // Workaround: androidx.fragment's UnsafeFragmentLifecycleObserverDetector + // walks the call graph recursively per Fragment method; on our oversized + // Fragment/Activity methods (DeployFragment/MainActivity) this degrades to + // near-exponential cost and HANGS :app:lintDebug for hours (CI ~50 min -> 3 h). + // Disable its 3 issue IDs (the only ones that detector emits) so lint finishes + // in minutes; the other 450+ checks stay on. Re-enable after the god classes + // are split (see controller/docs/TECH_DEBT_PLAN.md). + disable 'FragmentBackPressedCallback', 'FragmentLiveDataObserve', 'FragmentAddMenuProvider' } testOptions { From 0f9abbc36f27507400819b161a706055def0246a Mon Sep 17 00:00:00 2001 From: luisguzman-adfa Date: Wed, 24 Jun 2026 01:34:36 +0000 Subject: [PATCH 2/2] docs(tech-debt): record the disabled Fragment* lint checks as active debt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logs the lintOptions workaround in TECH_DEBT_PLAN.md (Progress log + Phase 3 cross-ref) with the explicit re-enable condition: drop the disable once DeployFragment/MainActivity are carved up. Keeps the comment in build.gradle honest — the debt is now tracked, not just referenced. --- controller/docs/TECH_DEBT_PLAN.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/controller/docs/TECH_DEBT_PLAN.md b/controller/docs/TECH_DEBT_PLAN.md index eabc6a5..66bb295 100644 --- a/controller/docs/TECH_DEBT_PLAN.md +++ b/controller/docs/TECH_DEBT_PLAN.md @@ -4,7 +4,12 @@ ## Progress log -_Last updated: 2026-06-17. Tracks remediation work against the findings below. IDs map to the register in this file (F/D/S/M) and to `FORK_DELTA_ANALYSIS.md` (K)._ +_Last updated: 2026-06-23. Tracks remediation work against the findings below. IDs map to the register in this file (F/D/S/M) and to `FORK_DELTA_ANALYSIS.md` (K)._ + +**Lint — `androidx.fragment` lifecycle detector disabled (ACTIVE DEBT)** (PR `fix/lint-fragment-detector-hang`) +- `androidx.fragment`'s `UnsafeFragmentLifecycleObserverDetector` **hangs** `:app:lintDebug` for hours — its recursive call-graph walk degrades to near-exponential cost on the oversized `DeployFragment`/`MainActivity` methods (CI lint ~50 min, up to ~3 h on heavy branches). +- **Workaround:** `lintOptions` disables that detector's 3 issue IDs — `FragmentBackPressedCallback`, `FragmentLiveDataObserve`, `FragmentAddMenuProvider` (the only IDs it emits). All other 450+ checks (and `abortOnError true` + baseline) stay active. +- **Re-enable condition:** once `DeployFragment`/`MainActivity` are carved into smaller collaborators (Phase 3 — `D1`/`F1`/`S14`), remove the `disable` and confirm lint completes. This is the trigger to retire the workaround. **Phase 0 — Guardrails: DONE** (PR `chore/phase0-guardrails`, merged as #4) - Extracted `SystemStatsUtil` and added the first JVM unit tests (`SystemStatsUtilTest`, `SyncHandshakeHelperTest`); added unit-test infra (`returnDefaultValues` + real `org.json`). Addresses **M10**. @@ -165,7 +170,7 @@ The plan is designed to run **alongside feature work**, lowest-risk-first. **Phase 2 — Concurrency & lifecycle stabilization (2–3 sprints).** Introduce a single shared executor + lifecycle-scoped cancellation; replace raw threads and convert recurring `Handler` loops to be torn down in `onDestroy`/`onPause` (`F4`, `M1`, `M7`, `D8`, `S8`, `M2`); make shared process handles `volatile`/synchronized and add `waitFor()` after `destroy()` (`D9`, `D19`, `S10`); drain process pipes and stop swallowing exec errors (`D12`, `S9`); replace `MODE_MULTI_PROCESS` with a real IPC/state mechanism (`F12`). -**Phase 3 — Architecture decomposition (ongoing, behind the net).** Extract config constants into a single `DeployConfig`/`Endpoints` class (`D3`, `F10`, `S7`). Introduce ViewModels + a thin repository layer so state leaves `MainActivity`'s public fields (`F8`, `M17`). Carve `DeployFragment` into download/extract/provision services and `MainActivity` into updater/terminal/server-controller collaborators (`D1`, `F1`, `S14`). De-duplicate the download and PRoot exec paths (`D4`, `D10`). +**Phase 3 — Architecture decomposition (ongoing, behind the net).** Extract config constants into a single `DeployConfig`/`Endpoints` class (`D3`, `F10`, `S7`). Introduce ViewModels + a thin repository layer so state leaves `MainActivity`'s public fields (`F8`, `M17`). Carve `DeployFragment` into download/extract/provision services and `MainActivity` into updater/terminal/server-controller collaborators (`D1`, `F1`, `S14`). Once these methods shrink, **remove the `lintOptions` disable of the `Fragment*` checks** (see Progress log). De-duplicate the download and PRoot exec paths (`D4`, `D10`). **Phase 4 — Build, distribution & polish.** Plan the `targetSdk 28 → 34+` epic (`M9`, `M3`) once Phases 1–2 remove the patterns that depend on the SDK exemption. Remove `jcenter()` (`M8`), align `abiFilters`/`splits` (`M12`), decouple native-artifact fetch from every build (`M15`), enable `minifyEnabled`, translate residual Spanish comments (`F16`, `D20`), delete dead code (`M13`, `D18`), and write the deploy runbook (`M20`).