From 9bfa9ac408ba44fda8f93bbb9c47fa9981eff992 Mon Sep 17 00:00:00 2001 From: freetlab Date: Sun, 19 Jul 2026 14:19:11 +0200 Subject: [PATCH] docs(openspec): archive android-auto-eq-presets Merges its delta requirements into the android-auto-media base spec. --- .../apply-progress.md | 0 .../archive-report.md | 95 +++++++++++++++++++ .../design.md | 0 .../proposal.md | 0 .../specs/android-auto-media/spec.md | 0 .../tasks.md | 0 .../verify-report.md | 0 openspec/specs/android-auto-media/spec.md | 74 ++++++++++++++- 8 files changed, 166 insertions(+), 3 deletions(-) rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/apply-progress.md (100%) create mode 100644 openspec/changes/archive/2026-07-19-android-auto-eq-presets/archive-report.md rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/design.md (100%) rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/proposal.md (100%) rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/specs/android-auto-media/spec.md (100%) rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/tasks.md (100%) rename openspec/changes/{android-auto-eq-presets => archive/2026-07-19-android-auto-eq-presets}/verify-report.md (100%) diff --git a/openspec/changes/android-auto-eq-presets/apply-progress.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/apply-progress.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/apply-progress.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/apply-progress.md diff --git a/openspec/changes/archive/2026-07-19-android-auto-eq-presets/archive-report.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/archive-report.md new file mode 100644 index 0000000..41b8f47 --- /dev/null +++ b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/archive-report.md @@ -0,0 +1,95 @@ +# Archive Report: android-auto-eq-presets + +**Date**: 2026-07-19 +**Change**: android-auto-eq-presets +**Status**: ARCHIVED - VERIFIED PASS +**Commit**: 90cd232 on main + +## Summary + +The android-auto-eq-presets change has completed all phases (proposal → spec → design → tasks → apply → verify) with a PASS verdict (0 CRITICAL, 0 WARNING, 0 SUGGESTION). The change adds a browsable `Ecualizador` folder to the Android Auto media tree, exposing the 6 fixed EQ presets as selectable items without interrupting playback. Implementation spans 3 files with 346 changed lines (339 insertions + 7 deletions), all covered by strict TDD (46/46 tests passing). Delta spec requirements have been merged into the base `openspec/specs/android-auto-media/spec.md`. The original change folder has been archived to `openspec/changes/archive/2026-07-19-android-auto-eq-presets/` and the source folder deleted. + +## Artifacts Preserved + +| Artifact | Location | Type | +|----------|----------|------| +| Proposal | `archive/2026-07-19-android-auto-eq-presets/proposal.md` | Requirements intent and scope | +| Design | `archive/2026-07-19-android-auto-eq-presets/design.md` | Architecture, 6 ADRs, integration points | +| Tasks | `archive/2026-07-19-android-auto-eq-presets/tasks.md` | 5 phases, 21 tasks (all complete) | +| Apply Progress | `archive/2026-07-19-android-auto-eq-presets/apply-progress.md` | TDD cycle evidence, 3 files modified | +| Verify Report | `archive/2026-07-19-android-auto-eq-presets/verify-report.md` | Test execution, spec compliance, 0 issues | +| Delta Spec | `archive/2026-07-19-android-auto-eq-presets/specs/android-auto-media/spec.md` | Original change-scoped spec delta | + +## Base Spec Merge + +**File**: `openspec/specs/android-auto-media/spec.md` + +**Changes**: +1. **Modified "Browsable Media Tree" requirement** (line 63): + - Root folder count updated from 3 to 4 (added `Ecualizador`) + - Updated scenario "Car requests the root" to return 4 folders instead of 3 + - Updated requirement description to reference EQ folder and preset behavior + +2. **Added "EQ Preset Browsable Folder" requirement** (new): + - 6 fixed presets exposed as playable items in `Ecualizador` folder + - Distinct `eq_preset:` media-id scheme + +3. **Added "EQ Preset Selection Applies Without Disturbing Playback" requirement** (new): + - Branch in `playFromMediaId` intercepted BEFORE `emisora:`/`grupo:` routing + - No `playMediaItem` call, no `mediaItem` mutation + - Three scenarios: preset while playing, preset while stopped, unknown/stale id + +4. **Added "EQ Preset Persistence and Phone/Car Parity" requirement** (new): + - Preset applied from car persists as principal preset + - Eventual phone/car parity via SharedPreferences + - Two scenarios: car-to-phone, phone-to-car parity + +5. **Added "Active Preset Indication (Optional)" requirement** (new): + - Optional visual marker via title-text convention + - Explicitly allows out-of-scope scoping (ADR-6) + - Two scenarios: with marker, without marker + +## Implementation Evidence + +**Files Modified**: +- `lib/servicios/navegacion_auto.dart` — 94 insertions (const, functions, tree builder) +- `lib/servicios/servicio_audio.dart` — 26 insertions (handler wiring) +- `test/servicios/navegacion_auto_test.dart` — 226 insertions (15 new test cases, 46/46 passing) + +**Test Results**: +- `navegacion_auto_test.dart`: 46/46 passing (34 pre-existing + 12 new test blocks) +- Regression check: `estado_radio_test.dart` (favorite-groups shared surface): 22/22 passing +- Load-bearing invariant verified: preset tap structurally cannot reach `playMediaItem` + +**Design Adherence**: +- All 6 ADRs (ADR-1 through ADR-6) verified in live code +- Headless-safe persistence via `ServicioEcualizador.guardarPrincipal` (SharedPreferences) +- Per-station preset override gate mirrored from `cambiarPresetPrincipal` +- Active-preset marker scoped out per ADR-6 (no partial/stale implementation) + +## Verification + +**Verdict**: PASS (0 CRITICAL, 0 WARNING, 0 SUGGESTION) + +**Coverage**: +- Completeness: All 21 tasks verified complete +- Test execution: Independently re-run (46/46 navegacion_auto, 22/22 regression) +- Structural invariants: Signature-enforced non-playback guarantee +- Spec compliance: All 5 spec requirements mapped to code +- Design coherence: All ADRs traced against implementation + +**Deviations identified and verified harmless**: +1. `servicio_ecualizador.dart` import added (required for instantiation, task-description omission only) +2. `playFromMediaId` restructured to single outer try/catch (functionally inert for pre-existing paths, required for spec exception safety) + +## Archive Integrity + +**Source deletion**: Original `openspec/changes/android-auto-eq-presets/` directory has been deleted. No files remain in the change folder; all artifacts are archived in `openspec/changes/archive/2026-07-19-android-auto-eq-presets/`. + +**Engram memory**: Archive report saved to engram topic `sdd/android-auto-eq-presets/archive-report` with full observation ID for traceability. + +## Next Steps + +- Change is complete and closed. +- No follow-up work required (ADR-6 scoping decision on active-preset marker is intentional; revisit only if reliable tree-refresh trigger is added). +- Merged base spec is ready for team use and future changes. diff --git a/openspec/changes/android-auto-eq-presets/design.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/design.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/design.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/design.md diff --git a/openspec/changes/android-auto-eq-presets/proposal.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/proposal.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/proposal.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/proposal.md diff --git a/openspec/changes/android-auto-eq-presets/specs/android-auto-media/spec.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/specs/android-auto-media/spec.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/specs/android-auto-media/spec.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/specs/android-auto-media/spec.md diff --git a/openspec/changes/android-auto-eq-presets/tasks.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/tasks.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/tasks.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/tasks.md diff --git a/openspec/changes/android-auto-eq-presets/verify-report.md b/openspec/changes/archive/2026-07-19-android-auto-eq-presets/verify-report.md similarity index 100% rename from openspec/changes/android-auto-eq-presets/verify-report.md rename to openspec/changes/archive/2026-07-19-android-auto-eq-presets/verify-report.md diff --git a/openspec/specs/android-auto-media/spec.md b/openspec/specs/android-auto-media/spec.md index 848b31e..def4b52 100644 --- a/openspec/specs/android-auto-media/spec.md +++ b/openspec/specs/android-auto-media/spec.md @@ -62,14 +62,14 @@ The system MUST produce a browsable tree that never presents a user-selectable f ### Requirement: Browsable Media Tree -`getChildren` MUST return a browsable tree rooted at `AudioService.browsableRootId`, organized into non-playable folders (Favoritos, Todas las emisoras, Mis emisoras) containing playable station items. Playable station items SHOULD carry an audio-quality subtitle when known. The `Favoritos` folder additionally MAY contain non-playable favorite-group sub-folders (see "Favorite Group Sub-Folders"); `Todas las emisoras` and `Mis emisoras` remain flat, unchanged by this capability. -(Previously: `Favoritos` was a flat folder of playable station items only, with no sub-folder nesting.) +`getChildren` MUST return a browsable tree rooted at `AudioService.browsableRootId`, organized into non-playable folders (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador) containing playable items. Playable station items SHOULD carry an audio-quality subtitle when known. The `Favoritos` folder additionally MAY contain non-playable favorite-group sub-folders (see "Favorite Group Sub-Folders"); `Todas las emisoras` and `Mis emisoras` remain flat. The `Ecualizador` folder is flat, non-playable, and contains only the 6 fixed EQ preset items (see "EQ Preset Browsable Folder"). +(Previously: root contained exactly 3 folders — Favoritos, Todas las emisoras, Mis emisoras — with no EQ folder; Favoritos was a flat folder of playable station items only, with no sub-folder nesting.) #### Scenario: Car requests the root - GIVEN the car head unit connects and requests the root (`AudioService.browsableRootId`) - WHEN `getChildren` is called with the root id -- THEN it returns three folder `MediaItem`s (Favoritos, Todas las emisoras, Mis emisoras), each with `playable: false` +- THEN it returns four folder `MediaItem`s (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador), each with `playable: false` #### Scenario: Car requests a folder with no stations @@ -103,6 +103,74 @@ The system MUST produce a browsable tree that never presents a user-selectable f - THEN that station appears as a playable `emisora:` item in exactly the same folder(s), position (subject to existing sort rules), title, art, and subtitle as it did before favorite-group folders were introduced - AND its presence and shape are unaffected by the existence, emptiness, or content of any favorite group +### Requirement: EQ Preset Browsable Folder + +The Android Auto browse tree MUST expose the 6 fixed EQ presets (`PresetEcualizador.presets`) as playable items inside the `Ecualizador` root folder, each using a distinct `eq_preset:` media-id scheme, separate from `emisora:` and `grupo:`. + +#### Scenario: Car requests the Ecualizador folder + +- GIVEN the `Ecualizador` folder id was returned under the root +- WHEN `getChildren` is called with the `Ecualizador` folder id +- THEN it returns exactly 6 playable `MediaItem`s, one per `PresetEcualizador.presets` entry, titled with the preset's name +- AND each item's id is `eq_preset:`, where `` is that preset's unique name + +### Requirement: EQ Preset Selection Applies Without Disturbing Playback + +Selecting an `eq_preset:` item MUST apply that preset immediately through the headless-safe EQ seam (e.g. `PluriWaveAudioHandler.aplicarPreset`), via a branch in `playFromMediaId` intercepted BEFORE the `emisora:`/`grupo:` routing. It MUST NOT call `playMediaItem`, MUST NOT mutate the now-playing `mediaItem`, and MUST NOT start, stop, restart, or otherwise alter current playback or playback position. + +#### Scenario: User selects a preset while a station is playing + +- GIVEN a station is currently playing and projected to the car +- WHEN the user taps an `eq_preset:` item, resolved via `playFromMediaId` +- THEN the matching preset is applied via the headless-safe apply seam +- AND the currently playing station continues uninterrupted, with no change to playback position or now-playing metadata + +#### Scenario: User selects a preset while nothing is playing + +- GIVEN no station is currently playing +- WHEN the user taps an `eq_preset:` item +- THEN the matching preset is applied via the headless-safe apply seam +- AND no playback starts as a result of the selection + +#### Scenario: Unknown or stale preset id + +- GIVEN `playFromMediaId` receives an `eq_preset:` id whose `` matches no entry in the current `PresetEcualizador.presets` +- WHEN resolution fails to find a matching preset +- THEN the selection is a no-op: no preset is applied, no playback state changes, and no unhandled exception propagates from the handler + +### Requirement: EQ Preset Persistence and Phone/Car Parity + +An EQ preset applied from the car MUST persist as the principal preset, with the same observable outcome as the phone's existing `ServicioEcualizador.guardarPrincipal`/`cambiarPresetPrincipal` path, and MUST be reflected on the phone. A principal preset changed on the phone MUST be observable from the car. The exact code path used to reach the headless-safe persistence seam from an Android Auto bind is deferred to `sdd-design`. + +#### Scenario: Preset applied from the car persists for the phone + +- GIVEN the user selects `eq_preset:` from the car head unit +- WHEN the selection is processed +- THEN the preset is persisted as the principal preset +- AND opening the phone's EQ UI afterward shows that preset as the active/principal preset + +#### Scenario: Preset applied from the phone is reflected for the car + +- GIVEN the user changes the principal preset from the phone UI +- WHEN the car head unit subsequently observes EQ-related state through the browse/selection surface +- THEN the car-observable state reflects the phone's most recently applied principal preset + +### Requirement: Active Preset Indication (Optional) + +The system MAY indicate the active/principal preset within the `Ecualizador` folder's rows. Because the legacy `MediaBrowserService` browsable-item model has no native "selected item" affordance, any indication, if implemented, MUST be conveyed via a row `title` text convention (e.g. a marker prefix/suffix) rather than assuming a checkmark or selection icon exists. If a reliable title-text convention is too costly to maintain, this requirement MAY instead be satisfied by explicitly not implementing active-preset indication. + +#### Scenario: Active preset is indicated via title convention (if implemented) + +- GIVEN the currently applied principal preset is known when `Ecualizador`'s children are built +- WHEN the design's chosen title-text convention is applied +- THEN exactly one of the 6 preset rows' `title` carries the active-preset marker, uniquely identifying that preset among the 6 + +#### Scenario: Active preset indication is out of scope (if not implemented) + +- GIVEN the design opts not to implement an active-preset marker +- WHEN the `Ecualizador` folder's children are built +- THEN all 6 preset rows are returned with their plain preset names, with no partial or inconsistent marking applied + ### Requirement: Playable Item Metadata Every playable `MediaItem` (station) MUST include a non-empty `title` and a loadable `artUri`. Stations without a logo MUST fall back to on-brand artwork, and stations whose logo URL cannot actually be loaded MUST degrade to the same on-brand fallback instead of rendering broken or blank art. The fallback MUST be visually consistent with the phone UI's per-station rotation rather than a generic launcher-icon copy.