docs(openspec): archive android-auto-local-music-phase3
Merges its delta requirements into the android-auto-media base spec. Completes the 3-phase local-music-in-Android-Auto feature. Standing pre-release gate: on-device/DHU validation of every native surface built across all 4 phases (SAF picking, metadata extraction, art cache, queue/shuffle handler wiring) is still outstanding.
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
||||
# Archive Report: android-auto-local-music-phase3
|
||||
|
||||
**Archived**: 2026-07-20 (completion of entire 3-phase local-music feature)
|
||||
|
||||
**Change Status**: CLOSED — Verified PASS WITH WARNINGS, committed to main (git commit dfd40ca), implementation complete.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Phase 3 of the local-music-in-Android-Auto feature — folder-scoped queue playback with shuffle and transport control wiring — is COMPLETE, verified, and archived. This change **completes the entire planned 3-phase local-music feature**. No further phases are planned. All 4 archived changes (Phase 1 + Paging Fast-Follow + Phase 2 + Phase 3, all dated 2026-07-19 and 2026-07-20) are now closed; the feature is ready for pre-release gate validation and eventual production release.
|
||||
|
||||
## Phase 3 Implementation Outcome
|
||||
|
||||
### Status
|
||||
- **Verification Verdict**: PASS WITH WARNINGS (0 CRITICAL issues, 3 WARNINGS, 1 SUGGESTION)
|
||||
- **Test Results**: 181/181 tests passed (0 failures)
|
||||
- **Regression Gate**: 4 pre-existing radio/reconnect test suites (21 total tests) BYTE-FOR-BYTE UNTOUCHED — identical pass counts (8/5/3/5 = 21) before and after, zero regression
|
||||
- **Commit**: dfd40ca on main, tagged [size:exception] per user's explicit informed choice (highest-risk change of session)
|
||||
- **Delivered**: Single PR, ~1400+ lines (prod 470, tests 950)
|
||||
|
||||
### Key Achievement — Regression Isolation Proven
|
||||
|
||||
The highest-risk aspect of this change — modifying `PluriWaveAudioHandler`'s event listeners and adding a queue auto-advance mechanism alongside a live-stream reconnection state machine — was isolated via a strict mode boundary (`_colaLocal != null` is the ONLY gate). The proof:
|
||||
|
||||
1. **Static proof**: `git diff --stat` shows ZERO lines changed in `_cambiarFuente`, `ControladorReconexion` usage, `_esErrorDeRed`, `_gestionarErrorReproduccion`, `_intentarReconexion`, `_reintentarFuente`.
|
||||
2. **Test proof**: The 4 protected regression test suites (servicio_audio_reconnect_test.dart, servicio_audio_session_test.dart, servicio_audio_source_switch_test.dart, servicio_audio_eq_reapply_test.dart) are BYTE-FOR-BYTE UNTOUCHED — not merely "same pass count", but identical file contents and identical test results.
|
||||
3. **Functional proof**: `playMediaItem`, the public entry point for all external play actions (radio stations, groups, single tracks), unconditionally clears `_colaLocal = null; _avanzandoCola = false` FIRST before any handler mutation, guaranteeing zero queue leakage into radio playback.
|
||||
|
||||
This is the single strongest assurance available in this environment (static-review-only, no DHU/on-device testing) that radio playback is provably untouched.
|
||||
|
||||
## Feature Completion — All 3 Phases Archived
|
||||
|
||||
### Timeline of Archived Changes
|
||||
1. **Phase 1 (2026-07-19)**: SAF folder pick, lazy per-level traversal, placeholder art, single-track browse + play
|
||||
2. **Paging Fast-Follow (2026-07-19)**: On-demand pagination (replacing 50-item silent truncation)
|
||||
3. **Phase 2 (2026-07-19)**: Embedded metadata, LRU cache, quality sort, name buckets, album art
|
||||
4. **Phase 3 (2026-07-20)**: Folder-scoped queue, sequential/shuffled play, auto-advance, transport skip, mode isolation
|
||||
|
||||
**Result**: The local-music-in-Android-Auto feature is FEATURE-COMPLETE. Drivers can:
|
||||
- Browse a local folder hierarchy in the car
|
||||
- See real track metadata (title, album art) via embedded extraction
|
||||
- Sort/filter tracks by name and quality
|
||||
- Play a folder's tracks sequentially or shuffled
|
||||
- Use car next/previous controls to navigate the queue
|
||||
- Auto-switch between local queue and live radio with clean mode transitions
|
||||
|
||||
**Out of scope by design** (not promised, explicitly deferred):
|
||||
- Recursive whole-subtree queueing (direct children only per folder)
|
||||
- OS shuffle toggle (browsable action only, not AudioService.setShuffleMode)
|
||||
- Repeat modes, persisted/resumable queues, queue-reordering UI
|
||||
|
||||
## Standing Pre-Release Gate — On-Device / DHU Validation Required
|
||||
|
||||
**CRITICAL STANDING RISK applying to ALL FOUR archived changes:**
|
||||
|
||||
None of the native Android Auto surfaces built in this session have been runtime-verified on a real Android Auto head unit or Desktop Head Unit (DHU). All of it is static-review-only or unit-tested-via-extraction:
|
||||
|
||||
1. **SAF folder picking** (Phase 1): SAF API usage, permission persistence, DocumentFile traversal — no runtime test
|
||||
2. **Native metadata extraction** (Phase 2): `MediaMetadataRetriever.getMetadata()`, `readAudioMetadataBatch` — extraction logic unit-tested locally, but device behavior untested
|
||||
3. **FileProvider art cache** (Phase 2): Static cache file serving, no device test
|
||||
4. **Queue/shuffle handler wiring + transport controls** (Phase 3): `ProcessingState.completed` firing, car next/previous button presses, queue advance behavior — static-review-only, no device test
|
||||
|
||||
**Pre-release sign-off MUST include**:
|
||||
- Dedicated on-device QA pass on a real head unit or DHU
|
||||
- Validation of all four surfaces (SAF pick, metadata display, album art, queue advance/skip)
|
||||
- End-to-end testing of: single-track play, folder sequential play, folder shuffled play, auto-advance to next track, skip via car controls, radio-to-queue and queue-to-radio mode transitions
|
||||
- Confirmation that no regressions to radio playback, reconnect, or EQ signal chain occur
|
||||
|
||||
This is non-negotiable before production release. The environment constraint (no DHU/on-device testing available during SDD execution) is documented and accepted; the risk is real and does not diminish with code review alone.
|
||||
|
||||
## Artifacts Merged into Base Specification
|
||||
|
||||
The base Android Auto Media specification (`openspec/specs/android-auto-media/spec.md`) now includes 6 new requirements from Phase 3's delta spec:
|
||||
|
||||
1. **Folder-Scoped Sequential Play Action** — "Reproducir carpeta" prepended on page 0 of folders with audio files
|
||||
2. **Folder-Scoped Shuffled Play Action** — "Reproducir aleatorio" alongside sequential, deterministically seeded
|
||||
3. **Local Queue Auto-Advance and Transport Skip** — handler auto-advances on track completion, skipToNext/skipToPrevious move within queue
|
||||
4. **Local Queue Mode Isolation From Radio Playback (Regression Guard — CRITICAL)** — `_colaLocal` nullability is the sole gate; mode boundaries prevent leakage
|
||||
5. **Local Queue End-of-Queue Behavior** — clean stop with no wraparound on last track
|
||||
6. **New Action Media-IDs Are Collision-Free** — `carpeta_local_reproducir:` and `carpeta_local_aleatorio:` collision-proven against all 8 existing prefixes
|
||||
|
||||
## Change Artifacts
|
||||
|
||||
| Artifact | Location | Notes |
|
||||
|---|---|---|
|
||||
| Proposal | `archive/2026-07-20-android-auto-local-music-phase3/proposal.md` | Honest scope reconciliation, single queue foundation feature, rationale for avoiding ConcatenatingAudioSource |
|
||||
| Design | `archive/2026-07-20-android-auto-local-music-phase3/design.md` | 6 ADRs, mode isolation via `_colaLocal` nullability, Fisher-Yates shuffle, file changes |
|
||||
| Tasks | `archive/2026-07-20-android-auto-local-music-phase3/tasks.md` | 26-item executable checklist (24 completed, 6.1 manual follow-up, 6.2 N/A), phases 0-5 |
|
||||
| Apply Progress | `archive/2026-07-20-android-auto-local-music-phase3/apply-progress.md` | 181/181 tests passed, 4 protected suites byte-identical, 4 deviations disclosed, risks for verify phase |
|
||||
| Verify Report | `archive/2026-07-20-android-auto-local-music-phase3/verify-report.md` | Verdict PASS WITH WARNINGS, priority investigations confirmed, 3 WARNINGs (static-review-only handler wiring, flutter analyze deferred, single-PR size:exception, DHU validation gate), 1 SUGGESTION (const list) |
|
||||
| Base Spec (merged) | `openspec/specs/android-auto-media/spec.md` | 6 new requirements appended; feature-complete specification |
|
||||
|
||||
## Test Coverage & Regression Data
|
||||
|
||||
### New Tests (181 total passing)
|
||||
- `cola_local_test.dart` (18 tests): ColaLocal navigation, decidirAvanceCola state machine, identical() guard for race condition
|
||||
- `navegacion_auto_test.dart` (139 tests, was ~120): Fisher-Yates determinism, collision-freedom exhaustive, page-0 action prepend, 4 pre-existing assertions updated for new behavior
|
||||
- `controlador_reconexion_local_test.dart` (3 tests, new): Bounded retry (5 failures → agotado, no 6th retry, no hang) with default maxReintentos:5, separate from protected suite to preserve byte-identical pass count
|
||||
|
||||
### Protected Regression Suites (21 tests, ZERO regression)
|
||||
- `servicio_audio_reconnect_test.dart`: 8 / 8 passing (UNCHANGED)
|
||||
- `servicio_audio_session_test.dart`: 5 / 5 passing (UNCHANGED)
|
||||
- `servicio_audio_source_switch_test.dart`: 3 / 3 passing (UNCHANGED)
|
||||
- `servicio_audio_eq_reapply_test.dart`: 5 / 5 passing (UNCHANGED)
|
||||
|
||||
**Baseline (Phase 0.1)**: 21 total, 0 failures
|
||||
**Final (Phase 5.4)**: 21 total, 0 failures
|
||||
**Diff on protected files**: Zero lines changed (verified via `git diff --stat`)
|
||||
|
||||
## Code Changes Summary
|
||||
|
||||
### New Files
|
||||
- `lib/servicios/cola_local.dart` (103 lines): Pure Dart queue holder, decision logic, identical() race guard
|
||||
- `test/servicios/cola_local_test.dart` (181 lines): Full unit coverage of ColaLocal and decidirAvanceCola
|
||||
- `test/servicios/controlador_reconexion_local_test.dart` (151 lines): Open-question regression test, bounded retry proof
|
||||
|
||||
### Modified Files
|
||||
- `lib/servicios/navegacion_auto.dart` (~180 lines added): Two new prefixes, Fisher-Yates, page-0 action prepend, queue orchestration
|
||||
- `lib/servicios/servicio_audio.dart` (~190 lines added/changed): Queue state fields, playMediaItem split, auto-advance wiring, skip overrides, control gating
|
||||
- `openspec/specs/android-auto-media/spec.md`: 6 new requirements appended (delta merged)
|
||||
|
||||
### Total Impact
|
||||
~1400+ lines delivered as single PR with user's explicit informed choice (highest-risk change of session, offered chain option, user chose single-PR anyway).
|
||||
|
||||
## Deviations from Design (All Disclosed & Justified)
|
||||
|
||||
1. **avanceEsValido folding**: Guard placed inside `_reproducirEntradaCola` as optional parameter instead of external gate; functionally identical, avoids redundant URI resolve
|
||||
2. **Resolve-failure defense**: Auto-advance that hits dead local file deactivates + stops (not left with stuck latch); defensive, consistent with ADR-4's no-inconsistent-state principle
|
||||
3. **androidCompactActionIndices dynamic**: Compact-view play/pause index shifts from [0] to [1] when queue active (skip-previous prepended); necessary for correctness, negligible perf cost
|
||||
4. **Test placement**: Open-question regression (5-failures-default-maxReintentos:5) placed in new file to preserve protected suite's byte-identical pass count
|
||||
|
||||
## Issues & Risks
|
||||
|
||||
### CRITICAL
|
||||
- None in this change alone; however, see standing pre-release gate below
|
||||
|
||||
### WARNINGS (3)
|
||||
1. **Handler wiring static-review-only**: ProcessingState.completed, car transport buttons, race-guard runtime behavior never executed in unit tests (environment constraint — no DHU/on-device testing). Risk mitigated by (a) pure cola_local.dart fully unit-tested, (b) careful manual diff review, confirmed by verify phase. **Requires on-device QA before release.**
|
||||
2. **flutter analyze/coverage/gen-l10n deferred**: Phase 6.1 explicitly out of scope (strict-TDD, only target test files run). **Must run manually before merge.**
|
||||
3. **Single-PR delivery size:exception**: ~1400+ lines over estimated 850-950. User was informed this was highest-risk change of day and offered chain option; chose single-PR anyway. **Review workload note for PR reviewers.**
|
||||
|
||||
### STANDING PRE-RELEASE GATE (APPLIES TO ALL 4 ARCHIVED PHASES)
|
||||
- **On-device / DHU validation of ALL native surfaces**: SAF folder pick, MediaMetadataRetriever extraction, FileProvider art cache, queue/shuffle handler + car transport. **Mandatory before production release** — none runtime-tested; all static-review-only or unit-tested-locally.
|
||||
|
||||
## Skill Compliance
|
||||
|
||||
- ✓ `literal-encoding` scan: 0 corruption matches in all touched/new .dart files
|
||||
- ✓ `work-unit-commits`: No commits by apply/verify agents; orchestrator handles final commit/tag
|
||||
|
||||
## Deferred / Out of Scope (Accepted)
|
||||
|
||||
- [ ] Phase 6.1: `flutter analyze`, `flutter test --coverage`, `flutter gen-l10n` — manual follow-up
|
||||
- [ ] On-device / DHU QA — scheduled separately as standing pre-release gate
|
||||
- [ ] Recursive subtree queueing — explicitly out of scope by design
|
||||
- [ ] OS shuffle toggle — browsable action only
|
||||
- [ ] Repeat modes, persisted queues, queue-reordering UI — not promised
|
||||
|
||||
## Traceability: All SDD Artifacts
|
||||
|
||||
| Phase | Artifact | Topic Key | Observation ID |
|
||||
|---|---|---|---|
|
||||
| Proposal | `sdd/android-auto-local-music-phase3/proposal` | — | (captured in engram) |
|
||||
| Spec | `sdd/android-auto-local-music-phase3/spec` | — | (captured in engram) |
|
||||
| Design | `sdd/android-auto-local-music-phase3/design` | — | (captured in engram) |
|
||||
| Tasks | `sdd/android-auto-local-music-phase3/tasks` | — | (captured in engram) |
|
||||
| Apply Progress | `sdd/android-auto-local-music-phase3/apply-progress` | — | (captured in engram) |
|
||||
| Verify Report | `sdd/android-auto-local-music-phase3/verify-report` | — | (captured in engram) |
|
||||
| Archive Report | `sdd/android-auto-local-music-phase3/archive-report` | — | (this document, saved to engram) |
|
||||
|
||||
## Closure
|
||||
|
||||
**Change is CLOSED and ARCHIVED.**
|
||||
|
||||
The entire 3-phase local-music-in-Android-Auto feature is now feature-complete, verified, and ready for:
|
||||
1. **Pre-release gate**: On-device/DHU validation (mandatory, covers all 4 archived phases)
|
||||
2. **Production release**: Once pre-release QA confirms all native surfaces on a real head unit
|
||||
|
||||
No further work on the local-music feature is planned. Subsequent PRs, if any, will be bug fixes or targeted enhancements (out of scope for SDD).
|
||||
+2
-1
@@ -41,7 +41,7 @@ controlador_reconexion_local_test.dart genuinely exercises `ControladorReconexio
|
||||
## Normal Verification Checklist
|
||||
|
||||
6. Media-id collision-freedom: both new prefixes (`carpeta_local_reproducir:`, `carpeta_local_aleatorio:`) confirmed `playable: true` in `_itemReproducirCarpeta`/`_itemReproducirAleatorio` (navegacion_auto.dart:517-535). An exhaustive collision test (navegacion_auto_test.dart:358-430) checks both directions against all 8 existing prefixes/ids (emisora:, grupo:, eq_preset:, carpeta_local:, carpeta_local_pag:, carpeta_local_ord:, carpeta_local_bucket:, pista:).
|
||||
7. Fisher-Yates shuffle: `mezclarFisherYates` confirmed to run over `pistasEnOrdenNombre`'s canonical name-sorted order (not native enumeration order), with an injectable Random. Determinism test (`Random(42)` twice yields identical order) and distribution sanity test (1000 runs, every position occupied by every item at least once) both present, real, and passing.
|
||||
7. Fisher-Yates shuffle: `mezclarFisherYates` confirmed to run over `pistasEnOrdenNombre`'s canonical name-sorted order (not native enumeration order, which is not guaranteed stable), with an injectable Random. Determinism test (`Random(42)` twice yields identical order) and distribution sanity test (1000 runs, every position occupied by every item at least once) both present, real, and passing.
|
||||
8. End-of-queue: STOP + deactivate (no loop) implemented in `_manejarFinPista`'s `desactivar` case and `skipToNext`'s end-of-queue branch; `skipToPrevious` clamps at index 0 (no wraparound). This wiring is static-review-only, PluriWaveAudioHandler cannot be instantiated in unit tests (confirmed: no `PluriWaveAudioHandler()` construction anywhere in the test suite), a pre-existing, disclosed testability gap from tasks.md, not a new finding.
|
||||
9. Browsable action items: the empty-folder-shows-no-action-item edge case (folder with only subfolders yields neither playable action prepended) is genuinely tested (navegacion_auto_test.dart:1681-1709).
|
||||
10. No AI attribution, no debug prints, no dead code, no real leftover TODOs found in changed files (one grep false-positive matched the Spanish word TODOS inside a test description string).
|
||||
@@ -58,6 +58,7 @@ None found.
|
||||
- Phase 3 handler wiring (highest-risk part of this change) is static-review-only by unavoidable environment constraint. PluriWaveAudioHandler cannot be instantiated in unit tests because its constructor builds a real just_audio.AudioPlayer requiring platform MethodChannels. This means a genuine ProcessingState.completed firing, real car transport button presses (skipToNext/skipToPrevious), and the actual runtime behavior of the mid-await avanceEsValido race guard have never been exercised by an executable test in this environment, correctness rests entirely on (1) the pure, fully-unit-tested cola_local.dart decision functions and (2) careful manual diff review, both of which this verify pass independently re-confirmed. This is a genuine residual risk requiring human on-device/DHU (Desktop Head Unit) confirmation before shipping, not a formality. The design and apply-progress both already flag this gap; this verify pass confirms it is real and has not been concealed or downplayed.
|
||||
- Phase 6.1 deferred: flutter analyze, flutter test --coverage, flutter gen-l10n were explicitly not run per Strict TDD scope (only target test files were run). Must be run manually before merge.
|
||||
- Single-PR delivery with size:exception (~1400+ lines, over the original ~850-950 estimate in tasks.md's Review Workload Forecast), an informed, disclosed choice by the user this session (told this was the highest-risk change of the day, chose to proceed as one PR anyway), not a new finding, restated here as a review-workload note for whoever reviews the PR.
|
||||
- **Pre-release gate for the entire 3-phase local-music feature (CRITICAL STANDING RISK)**: On-device / DHU (Desktop Head Unit) validation of EVERY native Android Auto surface built this session has never been runtime-verified. This includes: SAF folder picking (Phase 1), native metadata extraction via MediaMetadataRetriever (Phase 2), the FileProvider art cache (Phase 2), and NOW the queue/shuffle handler wiring with real car transport buttons (Phase 3). All of it is static-review-only or unit-tested-via-extraction; none of it has run on a real Android Auto head unit. This is the single most important open risk across the entire local-music feature and applies to ALL FOUR archived changes (Phase 1 2026-07-19, paging 2026-07-19, Phase 2 2026-07-19, Phase 3 2026-07-20). A pre-release sign-off must include a dedicated on-device QA pass on a real head unit or DHU covering all four surfaces before any production release.
|
||||
|
||||
### SUGGESTION
|
||||
- androidCompactActionIndices is no longer a compile-time const list (was const [0], now [colaActiva ? 1 : 0]), allocates a new 1-element list on every playbackState.add call. Zero functional impact, negligible GC pressure; not worth blocking on.
|
||||
Reference in New Issue
Block a user