Files
pluriwave/openspec/changes/archive/2026-07-11-bt-device-identity/archive-report.md
T
FreeTLab 159334f997
Build & Deploy PluriWave / Análisis de código (push) Successful in 35s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 1m44s
docs(openspec): archive bt-device-identity and promote its spec
Move the change folder to changes/archive/2026-07-11-bt-device-identity
with the verified artifact set (verdict: pass with warnings, 0 critical,
102/102 targeted tests) and create the bt-device-identity capability
spec under openspec/specs/. Phase 7 on-device QA remains the pending
human gate before release.
2026-07-11 01:15:06 +02:00

14 KiB
Raw Blame History

Archive Report: Canonical Bluetooth Device Identity

Change: bt-device-identity
Archived: 2026-07-11
Status: ARCHIVED AND CLOSED
Archive Path: openspec/changes/archive/2026-07-11-bt-device-identity/
Spec Merged To: openspec/specs/bt-device-identity/spec.md (new capability domain)


Executive Summary

The bt-device-identity change is now archived with a PASS WITH WARNINGS verification verdict. All code-gated implementation work is complete across Phases 15 (41 of 57 tasks done, 6 N/A by design). Phase 7 (manual/on-device QA, 10 tasks, human sign-off gate) remains pending as the mandatory pre-shipping gate. The change restores stable Bluetooth device identity by acquiring BLUETOOTH_CONNECT at point-of-intent, guarding against the OS placeholder MAC, caching per-device names, and migrating corrupted placeholder-keyed entries. All test suites pass (102/102 tests), flutter analyze reports 0 issues, and the re-verify phase closed the single CRITICAL finding (CRITICAL-1: untested "rename survives re-pair" scenario — now covered by new task 3.10 composed regression test that passes against existing code).


Artifact Traceability

Artifact Engram Observation ID Location Notes
Proposal #2299 sdd/bt-device-identity/proposal Root cause analysis, scope, approach, risks
Spec #2304 sdd/bt-device-identity/spec; merged to openspec/specs/bt-device-identity/spec.md 7 requirement groups, 21 scenarios; on-disk version (openspec/changes/bt-device-identity/spec.md) reconciled and merged to new domain
Design #2305 sdd/bt-device-identity/design 6 ADRs, technical approach, file changes, testing strategy
Explore (embedded) sdd/bt-device-identity/proposal (no separate observe) Root cause evidence, bug analysis
Tasks #2314 sdd/bt-device-identity/tasks 57 checkboxes: 41 complete (Phase 15), 6 N/A (Phase 6), 10 pending (Phase 7 human QA)
Apply-Progress #2328 sdd/bt-device-identity/apply-progress Batches 1/2/2b complete (Phases 15); Batch 3 (Phase 7) pending; full merged evidence of all 41 tasks
Verify-Report #2329 sdd/bt-device-identity/verify-report Initial FAIL (CRITICAL-1 untested scenario); re-verify PASS WITH WARNINGS (CRITICAL-1 closed via task 3.10); 15/21 scenarios COMPLIANT, 4/21 DEFERRED (manual QA), 2/21 N/A (l10n justified), 0/21 still UNTESTED

Implementation Completion

Phases 15: Code-Gated Work — COMPLETE

Phase Description Tasks Status Evidence
1 Kotlin permission plumbing (manifest, placeholder guard, composite fallback, permission request, re-emit) 1.11.8 (8) 8/8 [x] Code-inspection verified; flutter analyze 0 issues
2 Dart permission contract (abstract method, fakes, interface tests) 2.12.5 (5) 5/5 [x] All 3 fake implementers updated; contract tests pass
3 Dart platform-name cache + duplicate-entry guard (in-memory map, composites don't auto-create, regression tests) 3.13.10 (10) 10/10 [x] 49/49 estado_ecualizador_test.dart pass (incl. post-verify task 3.10)
4 Dart display fix + permission trigger (call sites updated, permission fires on toggle-ON) 4.14.8 (8) 8/8 [x] 11/11 pantalla_ajustes_test.dart pass; provider wiring resolved via passthrough
5 Dart one-time migration purge (guarded flag, exact-literal placeholder removal, idempotent) 5.15.10 (10) 10/10 [x] 20/20 servicio_ecualizador_test.dart pass; migration isolation verified

Phase 6: Localization — N/A (By Design)

Phase Description Tasks Status Rationale
6 Localization (l10n keys for all 13 locales IF UI copy is shown) 6.16.6 (6) N/A No in-app rationale sheet or migration-notice UI exists; OS BLUETOOTH_CONNECT dialog only; migration purge entirely silent; Phase 6 condition ("IF UI copy is shown") not met

Phase 7: Manual/On-Device QA — PENDING (Human Gate)

Phase Description Tasks Status Notes
7 Manual/on-device QA (permission dialog flow, real MAC after grant, rename survives re-pair, no duplicates, migration idempotency, locale rendering if Phase 6 ships) 7.17.10 (10) 0/10 [ ] Mandatory human sign-off gate; requires real/emulated Android 12+ device; Kotlin permission path not unit-testable (code-inspected only); these 10 items are the ONLY remaining work for this change

Task Summary:

  • 41 tasks complete ([x]) across Phases 15
  • 6 tasks N/A ([ ] with justification) in Phase 6
  • 10 tasks pending ([ ]) in Phase 7
  • Total: 57 checkboxes, 0 unaccounted

Test Results

Unit/Widget Tests

Suite Tests Result Notes
estado_ecualizador_test.dart 49/49 PASS +1 from task 3.10 (was 48/48); Phase D regression group (D.1-D.5) confirmed green
pantalla_ajustes_test.dart 11/11 PASS Display fix + permission trigger scenarios
servicio_ecualizador_test.dart 20/20 PASS Migration tests including matrix-key and near-miss scenarios
servicio_dispositivo_audio_real_test.dart 10/10 PASS Permission contract (MethodChannel invokeMethod)
servicio_dispositivo_audio_test.dart 7/7 PASS Abstract contract tests
servicio_dispositivo_audio_toggle_test.dart 5/5 PASS Ripple-fix (NullServicioDispositivoAudio) included
Total 102/102 PASS

Static Analysis

Tool Result Coverage
flutter analyze 0 issues Whole project
rg (placeholder string checks) No dead code (00:00:00:00:00:00 removed completely) Kotlin, Dart

Code Coverage

  • Exact-line coverage metrics not collected (per verification scope).
  • Scenario-level mapping: 15/21 spec scenarios COMPLIANT (Dart-testable, all pass); 4/21 DEFERRED (manual QA); 2/21 N/A (l10n justified).
  • CRITICAL-1 closed by new task 3.10 composed test (spec.md L95-100, rename survives re-pair).

Verification Summary

Verdict Evolution

Pass Result Finding
Initial (2026-07-11 00:44) FAIL CRITICAL-1: Untested Dart-testable scenario "rename persists after re-pair" (spec.md L95-100) — constituent behaviors tested individually (reconnect-dedup D.4, rename-priority 4.4), composition untested
Re-verify (2026-07-11 post-verify) PASS WITH WARNINGS CRITICAL-1 CLOSED: task 3.10 adds composed regression test; passes against existing code (coverage gap confirmed, not functional bug). WARNING-1 spec wording reconciled (composite shape documented as bt_a2dp:name:<name> per ADR-3). WARNING-2 cosmetic (arithmetic drift in apply-progress narrative, no code impact) accepted as-is.

Finding Breakdown

Severity Count Status
CRITICAL 1 CLOSED (task 3.10 composed regression test)
WARNING 2 CLOSED (WARNING-1, re-verify wording fix); OPEN/ACCEPTED (WARNING-2, cosmetic)
SUGGESTION 3 OPEN (non-blocking: wording nuance SUGGESTION-1, mock call-count pattern SUGGESTION-2, Engram housekeeping SUGGESTION-3)

Compliance Matrix Snapshot

Req. Scenarios Compliant Deferred N/A Untested
BLUETOOTH_CONNECT at point-of-intent 2 0 2 0 0
Placeholder MAC guarded 5 4 1 0 0
Platform-name cache feeds display 3 3 0 0 0
Rename survives re-pair 2 1 (via 3.10) 1 0 0
No duplicate entries on transient 2 2 0 0 0
One-time guarded migration 6 6 0 0 0
Localization (conditional on UI copy) 2 0 0 2 0
Total 21 15 4 2 0

File Changes Summary

Estimated Changed Lines: ~450 (Kotlin ~90, Dart prod ~140, Dart tests ~58 post-verify, manifest ~1)
400-Line Budget Risk: High (mitigated by chained PR strategy; PR 1: Kotlin, PR 2: Dart, PR 3: l10n + manual QA)
Build Execution: Not run (per verification scope — flutter build explicitly excluded)

Kotlin (Code-Inspection Verified)

  • android/app/src/main/AndroidManifest.xml: +1 permission declaration
  • android/app/.../MainActivity.kt: ~90 lines (permission request code, placeholder guard, composite fallback, re-emit logic, doc comment updates)

Dart Production

  • lib/servicios/servicio_dispositivo_audio.dart: Abstract + real impl of solicitarPermisoBluetooth()
  • lib/estado/estado_ecualizador.dart: In-memory _nombresPlataforma cache, nombrePlataforma() getter, duplicate-entry guard update
  • lib/pantallas/pantalla_ajustes.dart: Two nombreVisible() call sites updated, permission trigger added
  • lib/servicios/servicio_ecualizador.dart: Migration constants and migrarClavesPlaceholder() function

Dart Tests

  • test/estado/estado_ecualizador_test.dart: +58 lines (task 3.10 post-verify regression test)
  • test/pantallas/pantalla_ajustes_test.dart: Display fix + permission trigger tests
  • test/servicios/servicio_ecualizador_test.dart: Migration test suite (exact placeholder, matrix suffix, near-miss, idempotency)
  • test/servicios/servicio_dispositivo_audio_real_test.dart: Permission contract tests
  • test/helpers/fakes.dart: Fake implementers updated (ripple-fix: NullServicioDispositivoAudio also patched)

Localization

  • lib/l10n/*.arb (13 files): 0 changes (Phase 6 N/A — no in-app copy shipped)

Deviations from Design

Documented and Signed Off:

  1. Trigger-Point Deviation (Phase 4, Task 4.7): Design specified "on opening device-management UI" (StatefulWidget on-build hook). Actual: permission fires on toggle-turn-ON tap in _SeccionEcualizadorAvanzado (StatelessWidget). Rationale: avoids unnecessary StatelessWidget → StatefulWidget conversion; toggle-turn-ON satisfies both spec scenarios. Signed off by orchestrator.

  2. Provider Wiring Resolution (Phase 4, Task 4.8): Design assumed ServicioDispositivoAudio would be a top-level Provider. Actual: not registered in app.dart. Solution: added solicitarPermisoBluetooth() passthrough on EstadoEcualizador (delegates to private field). Maintains test isolation, avoids ProviderNotFoundException.

No design deviations were introduced in Batch 2b (task 3.10 is test-only, zero production-code changes).


Risks and Mitigations

Risk Likelihood Status
Migration deletes legitimate data Low Exact-literal bt_a2dp:02:00:00:00:00:00 match only; all other keys preserved; Task 5.3 near-miss test confirms 02:00:00:00:00:01 is NOT purged
BT permission denied → unstable ids Med Composite bt_a2dp:name:<productName> fallback keeps app functional; re-prompt on next settings open; no crash path
Colon-delimiter key safety broken Low Single leading bt_a2dp: segment preserved; productName colons sanitized to -; eq_presets_matriz_v1 split-on-first-: remains valid (ADR-3, multi-device-eq ADR-3 RFC4122 rationale)
Kotlin permission flow has no precedent Med Copied verbatim from existing requestPostNotificationsPermission pattern (L306-318 struct); request codes distinct (4821/4822/4823); human verification in Phase 7
No Kotlin test harness Med Code-inspection verified against proven pattern; Phase 7 manual QA is the runtime gate

Next Steps

Mandatory Before Shipping

  1. Phase 7 Manual/On-Device QA (pending, 10 tasks): Requires real or emulated Android 12+ device.

    • Permission dialog flow (grant/deny, no dialog at launch)
    • Real MAC after grant (7.3)
    • Rename survives re-pair without duplicates (7.5, the original bug report symptom)
    • Migration purge on affected install + idempotency (7.77.8)
    • Locale rendering if Phase 6 ships (7.9; N/A this change)
    • Sign-off (7.10) before merge
  2. Chain Strategy (pending, from tasks.md): Orchestrator should confirm/name the chain strategy if chained PRs are used:

    • stacked-to-main: Each PR merges to main in order (fast, fix on the go)
    • feature-branch-chain: Tracker branch accumulates; PR #1→PR 2→PR 3 (rollback control)

Follow-Up Opportunities (Out of Scope)

  • WARNING-1 (now closed): Spec wording reconciled during re-verify. Composite shape (bt_a2dp:name:<name>) is now documented in both spec and design.
  • WARNING-2 (cosmetic, accepted): Arithmetic drift in apply-progress narrative (pre-existing test counts off by 3/2 respectively) — no functional impact. Accepted as-is per task brief.
  • SUGGESTION-1/2/3 (non-blocking): Wording nuance (composite not single-entry), mock-call-count pattern, Engram conflict housekeeping — all flagged for future review.
  • Broader transient-churn dedup (design Open Question): Deferred; acceptable now that MAC is canonical. Revisit if duplicates persist post-fix.

Archive Integrity

Moved From: openspec/changes/bt-device-identity/openspec/changes/archive/2026-07-11-bt-device-identity/
Verified Empty: Source folder no longer exists under changes/ (not a residual copy, true move)
Spec Merged: openspec/specs/bt-device-identity/spec.md created as new domain (not merged into multi-device-eq, which focuses on EQ preset resolution; bt-device-identity is about device identity + persistence)
Artifacts Preserved: All 6 openspec files (design, explore, proposal, spec, tasks, verify-report) + new state.yaml and this archive-report


Sign-Off

Archive Ready For: Production shipping (pending Phase 7 human QA sign-off)
Blocking Issues: None; Phase 7 is a known human gate, not a blocker
Recommendation: Proceed to Phase 7 manual QA; no code changes expected; expect 10 pass/fail items to be recorded against real device access


Appendix: Observation IDs for Traceability

  • #2299: sdd/bt-device-identity/proposal (root cause, scope, approach)
  • #2304: sdd/bt-device-identity/spec (21 scenarios, 7 requirement groups)
  • #2305: sdd/bt-device-identity/design (6 ADRs, technical approach, testing strategy)
  • #2314: sdd/bt-device-identity/tasks (57 checkboxes, 3 batches, phase status)
  • #2328: sdd/bt-device-identity/apply-progress (Batches 1/2/2b merged evidence, 41 tasks complete)
  • #2329: sdd/bt-device-identity/verify-report (PASS WITH WARNINGS, re-verify, CRITICAL-1 closed)

All artifacts archived and accessible at openspec/changes/archive/2026-07-11-bt-device-identity/ with full change history preserved for audit trail.