Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
162 commits
Select commit Hold shift + click to select a range
79ac5bb
docs: establish Symphonia SDD foundation
efraespada Sep 20, 2026
aa62b33
feat: add copy planning and durable operation core
efraespada Sep 20, 2026
8ee9941
feat: add runtime health and readiness surface
efraespada Sep 20, 2026
d2a55a2
fix: expose SQLite readiness check
efraespada Sep 20, 2026
4a06d12
feat: add normalized provider import contracts
efraespada Sep 20, 2026
adf8ca9
fix: enforce retry timing and lease renewal
efraespada Sep 20, 2026
19eea50
feat: persist digest-bound copy plans
efraespada Sep 20, 2026
7030d17
feat: persist explainable identity decisions
efraespada Sep 20, 2026
961c656
feat: enqueue only accepted copy plans
efraespada Sep 20, 2026
a532a2b
build: add non-root local runtime container
efraespada Sep 20, 2026
5ca264b
feat: execute accepted copies with reconciliation
efraespada Sep 20, 2026
55c0c5b
feat: support cooperative copy cancellation
efraespada Sep 20, 2026
f4b2c31
fix: migrate operation store schema forward
efraespada Sep 20, 2026
10f224c
feat: persist complete playlist projections
efraespada Sep 20, 2026
10c6624
feat: publish imports through application service
efraespada Sep 20, 2026
8d93d82
feat: add durable operation audit events
efraespada Sep 20, 2026
4af9f51
feat: add atomic next-operation claiming
efraespada Sep 20, 2026
50dbd87
feat: add redacted operation diagnostics
efraespada Sep 20, 2026
f769b8c
feat: persist provider rate-limit waits
efraespada Sep 20, 2026
1bd90a5
fix: version operation event schema
efraespada Sep 20, 2026
96b5a73
feat: make playlist snapshot publication idempotent
efraespada Sep 20, 2026
9fbe2db
feat: persist provider connections safely
efraespada Sep 20, 2026
c743b94
feat: persist single-use authorization attempts
efraespada Sep 20, 2026
ed21aee
feat: orchestrate adapter-backed playlist imports
efraespada Sep 20, 2026
73376b5
feat: add deterministic provider adapter registry
efraespada Sep 20, 2026
4628818
feat: add safe Spotify playlist adapter
efraespada Sep 20, 2026
ff703ee
feat: orchestrate authorization state boundary
efraespada Sep 20, 2026
af60f47
fix: forbid secrets on disconnected connections
efraespada Sep 20, 2026
81e31ca
feat: bind copy plans to target capabilities
efraespada Sep 20, 2026
0d87203
feat: probe and classify provider connections
efraespada Sep 20, 2026
f98eb9a
feat: add scoped YouTube Data playlist reader
efraespada Sep 20, 2026
93d9449
feat: add experimental Home Assistant App metadata
efraespada Sep 20, 2026
8bc52e8
feat: support safe Ingress base paths
efraespada Sep 20, 2026
969652d
feat: intersect layered provider capabilities
efraespada Sep 20, 2026
c0effbd
feat: add durable operation runner
efraespada Sep 20, 2026
3165d6a
feat: dispatch copy execution through operation runner
efraespada Sep 20, 2026
4a71aa9
feat: add experimental Apple Music playlist reader
efraespada Sep 20, 2026
1966a72
fix: fail durable operations on handler exceptions
efraespada Sep 20, 2026
18639a4
feat: add cooperative durable operation worker
efraespada Sep 20, 2026
f160aaa
fix: preserve provider object types in snapshots
efraespada Sep 20, 2026
8a03add
feat: run playlist imports as durable operations
efraespada Sep 20, 2026
c98250e
feat: add lossless identity normalization
efraespada Sep 20, 2026
20c8d4f
docs: align implementation baseline with durable workers
efraespada Sep 20, 2026
fd4168a
feat: add runtime resource composition
efraespada Sep 20, 2026
6e0cd0f
feat: wire runtime resources into HTTP startup
efraespada Sep 20, 2026
32be279
docs: document current provider adapter boundaries
efraespada Sep 20, 2026
ca226d9
fix: reject repeated provider pagination cursors
efraespada Sep 20, 2026
7038936
feat: gate Spotify write capabilities explicitly
efraespada Sep 20, 2026
5ff5536
fix: bound transient operation retries
efraespada Sep 20, 2026
31fbee0
fix: bind copy plans to source object types
efraespada Sep 20, 2026
f4739a0
feat: retain provider metadata in projections
efraespada Sep 20, 2026
7c72be5
fix: bound provider pagination reads
efraespada Sep 20, 2026
e941e67
feat: add bounded operation diagnostics listing
efraespada Sep 20, 2026
afdbd26
fix: audit recovered operation leases
efraespada Sep 20, 2026
11670ce
fix: make readiness cover all durable stores
efraespada Sep 20, 2026
71cc06f
fix: validate OAuth redirect URIs before persistence
efraespada Sep 20, 2026
9124728
fix: redact secrets from provider errors
efraespada Sep 20, 2026
90e09ff
fix: reject credential keys in operation payloads
efraespada Sep 20, 2026
82386ae
feat: add consistent runtime SQLite backups
efraespada Sep 20, 2026
05d8d93
feat: add aggregate operation queue summaries
efraespada Sep 20, 2026
abd84ab
feat: validate runtime configuration before startup
efraespada Sep 20, 2026
f6baebd
fix: encapsulate durable store readiness checks
efraespada Sep 20, 2026
1178f9b
fix: fail cleanly on invalid ingress configuration
efraespada Sep 20, 2026
b744405
feat: expose queue age and expired lease diagnostics
efraespada Sep 20, 2026
b3ba9dc
feat: add safe runtime diagnostics view
efraespada Sep 20, 2026
426190d
feat: add safe provider connection health summaries
efraespada Sep 20, 2026
1f89c14
fix: fail closed during runtime diagnostics races
efraespada Sep 20, 2026
5d754b7
test: guard Home Assistant container contract
efraespada Sep 20, 2026
6090035
feat: add safe playlist projection summaries
efraespada Sep 20, 2026
343fea9
feat: add safe resolution decision summaries
efraespada Sep 20, 2026
83df573
fix: redact credentials at the write boundary
efraespada Sep 20, 2026
f92900f
feat: preflight SQLite backups before restore
efraespada Sep 20, 2026
5110af5
fix: refuse backups from unhealthy runtimes
efraespada Sep 20, 2026
34ba9cf
fix: publish SQLite backups atomically
efraespada Sep 20, 2026
f1db298
fix: reject nested credential payloads
efraespada Sep 20, 2026
ede5b9f
fix: redact provider error codes
efraespada Sep 20, 2026
e9bd960
feat: report expired provider connections
efraespada Sep 20, 2026
59063d6
docs: define Home Assistant-native UI contract
efraespada Sep 20, 2026
946b313
fix: protect durable checkpoints from credentials
efraespada Sep 20, 2026
3f486e9
fix: validate backup schema and foreign keys
efraespada Sep 20, 2026
8147c2b
fix: cap support diagnostic sizes
efraespada Sep 20, 2026
0c9de28
feat: support runtime resource context management
efraespada Sep 20, 2026
ddd1e34
docs: trace foundation evidence without advancing readiness
efraespada Sep 20, 2026
30f2afe
test: enforce dependency-free architecture boundaries
efraespada Sep 20, 2026
9c5b680
fix: harden runtime configuration boundaries
efraespada Sep 20, 2026
1f11b0a
fix: make runtime shutdown idempotent
efraespada Sep 21, 2026
a03ddb8
fix: enforce foreign keys across sqlite stores
efraespada Sep 21, 2026
fc4803e
test: verify sqlite orphan rejection
efraespada Sep 21, 2026
3c0a495
refactor: centralize sqlite connection policy
efraespada Sep 21, 2026
55b9c7f
fix: require object-shaped durable payloads
efraespada Sep 21, 2026
52880af
fix: reject non-string durable payload keys
efraespada Sep 21, 2026
b7a6378
fix: bound diagnostic key lists
efraespada Sep 21, 2026
2150fed
fix: bound diagnostic event reads
efraespada Sep 21, 2026
c1788a3
fix: migrate operation schema transactionally
efraespada Sep 21, 2026
39b0c87
fix: reject future operation schemas in backups
efraespada Sep 21, 2026
185b012
fix: validate operation handler results
efraespada Sep 21, 2026
1ad205e
fix: harden runtime json response headers
efraespada Sep 22, 2026
64f4f61
feat: harden runtime and durable recovery foundation
efraespada Sep 22, 2026
bd00868
fix: close all runtime resources on shutdown errors
efraespada Sep 24, 2026
e091285
fix: reject non-origin runtime request targets
efraespada Sep 24, 2026
e6aac03
fix: unwind partial runtime startup safely
efraespada Sep 24, 2026
18faaa6
fix: close sqlite connections when migrations fail
efraespada Sep 24, 2026
c49ca76
fix: reject symlinked backup destinations
efraespada Sep 24, 2026
774f49f
fix: centralize ingress path validation
efraespada Sep 24, 2026
788b136
refactor: encapsulate sqlite backup in repository
efraespada Sep 25, 2026
93982b4
fix: preserve cleanup on interrupted startup
efraespada Sep 25, 2026
d4e502b
fix: validate runtime config before opening stores
efraespada Sep 25, 2026
666a39d
fix: shut down runtime cleanly on sigterm
efraespada Sep 25, 2026
5fd7ef4
fix: sanitize default runtime http errors
efraespada Sep 25, 2026
17c7c2c
fix: align container probe with runtime config
efraespada Sep 25, 2026
3be58f6
ci: check whitespace in pushed patch range
efraespada Sep 25, 2026
fd8b636
fix: bound runtime http connection time
efraespada Sep 25, 2026
cf9521b
docs: refresh implementation baseline review date
efraespada Sep 25, 2026
bd3bb7d
fix: align container probe with bind host
efraespada Sep 26, 2026
acf3f35
ci: handle first-push whitespace checks
efraespada Sep 26, 2026
bf21a5b
fix: validate container readiness response
efraespada Sep 26, 2026
1a72be5
fix: preserve backup failure during cleanup
efraespada Sep 26, 2026
fe352e7
fix: handle dropped runtime http clients quietly
efraespada Sep 26, 2026
2cbb2f8
fix: reject lossy runtime port coercions
efraespada Sep 26, 2026
7e23f8f
fix: reject unicode controls in runtime config
efraespada Sep 26, 2026
7bbaf25
fix: normalize health probe listener host
efraespada Sep 26, 2026
2d4eb7d
fix: share sqlite path validation across runtime entrypoints
efraespada Sep 26, 2026
6f2151e
fix: normalize sqlite home paths consistently
efraespada Sep 26, 2026
8f5cdaf
style: remove trailing blank lines for initial CI check
efraespada Sep 26, 2026
7345fec
fix: unwind interrupted operation transactions safely
efraespada Sep 26, 2026
ff9cd39
fix: reject api keys in durable operation payloads
efraespada Sep 26, 2026
33e410e
fix: validate credentials in durable audit events
efraespada Sep 26, 2026
c607eda
fix: avoid echoing sensitive operation keys
efraespada Sep 26, 2026
427ab8e
fix: reject naive operation timestamps
efraespada Sep 26, 2026
a902f90
fix: validate durable lease durations strictly
efraespada Sep 26, 2026
6fa7628
perf: stream durable operation readiness checks
efraespada Sep 26, 2026
c19d733
fix: serialize shared operation repository access
efraespada Sep 26, 2026
94f94ba
fix: reject timestamps without a defined offset
efraespada Sep 26, 2026
f26c3f4
fix: return persisted operation after handler dispatch
efraespada Sep 26, 2026
14d19c0
fix: validate operation ids at repository boundaries
efraespada Sep 26, 2026
251014a
fix: fail readiness closed on pathological records
efraespada Sep 26, 2026
2b51a1a
fix: validate checkpoint state types
efraespada Sep 26, 2026
fb89788
fix: validate diagnostic query limits
efraespada Sep 26, 2026
f086eb8
perf: bound diagnostic key selection memory
efraespada Sep 26, 2026
2c3b10f
docs: track cancellation recovery decision
efraespada Sep 26, 2026
bc0e57c
feat: report storage spike phase timings
efraespada Sep 26, 2026
c1e11d9
feat: parameterize synthetic storage spike payloads
efraespada Sep 26, 2026
0c1ad66
feat: quarantine cancelled operations with unknown outcomes
efraespada Sep 26, 2026
8b860d0
feat: expose safe cancellation recovery diagnostics
efraespada Sep 26, 2026
003e57c
feat: cover cancelled lease recovery in storage spike
efraespada Sep 26, 2026
09a9744
fix: preserve uncertain cancellation across retry releases
efraespada Sep 26, 2026
684ae0a
fix: stop before side effects after cancellation request
efraespada Sep 27, 2026
dd84566
fix: reconcile in-flight playlist writes before retry
efraespada Sep 27, 2026
2b0e7a2
docs: define provider write reconciliation evidence
efraespada Sep 27, 2026
6e477a0
docs: align import SDD with foundation evidence
efraespada Sep 27, 2026
c1c2ce4
docs: record authorization foundation boundaries
efraespada Sep 27, 2026
8156a32
test: keep inconclusive copy writes pending on resume
efraespada Sep 27, 2026
64c5cae
fix: preserve permanent copy failures across reconciliation
efraespada Sep 27, 2026
eb3fb5e
test: align runtime and storage checks with safety contracts
efraespada Sep 27, 2026
5131e45
test: require target reconciliation before resumed creation
efraespada Sep 27, 2026
8566d37
fix: reject inconsistent copy progress before provider writes
efraespada Sep 27, 2026
8430cd8
fix: validate persisted import identity before adapter reads
efraespada Sep 27, 2026
1ce6881
refactor: share provider JSON transport and guard architecture
efraespada Sep 27, 2026
1112a6a
refactor: centralize copy confirmations and constant-time replay checks
efraespada Sep 27, 2026
ccd476b
docs: add isolated Home Assistant-adjacent UI review fixture
efraespada Sep 27, 2026
f2ff581
test: characterize copy recovery after lost provider responses
efraespada Sep 27, 2026
a2871fd
docs: record Lit UI decision and verify isolated RG-006 spike
efraespada Sep 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
.gitignore
.pytest_cache
__pycache__
*.py[cod]
*.sqlite3
*.db
docs
specs
tests
73 changes: 73 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Verify

on:
push:
pull_request:

permissions:
contents: read

jobs:
verify:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Check specification consistency
run: PYTHONPATH=. python tools/validate_specs.py
- name: Check patch whitespace
env:
PATCH_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
if [[ "$PATCH_BASE" =~ ^0+$ ]]; then
DEFAULT_REF="refs/remotes/origin/$DEFAULT_BRANCH"
if git show-ref --verify --quiet "$DEFAULT_REF"; then
if PATCH_BASE="$(git merge-base HEAD "$DEFAULT_REF")"; then
git diff --check "${PATCH_BASE}...HEAD"
exit 0
fi
fi
EMPTY_TREE="$(git hash-object -t tree /dev/null)"
git diff --check "$EMPTY_TREE" HEAD
else
git diff --check "${PATCH_BASE}...HEAD"
fi
- name: Run offline test suite
run: PYTHONPATH=src:. python -m unittest discover -s tests -v

ui-lit-spike:
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs/development/ui-lit-spike
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: docs/development/ui-lit-spike/package-lock.json
- name: Install pinned spike dependencies
run: npm ci --ignore-scripts
- name: Check types
run: npm run check
- name: Check presentation boundary
run: npm run check-boundary
- name: Run pure fallback tests
run: npm test
- name: Build isolated UI spike
run: npm run build
- name: Inspect relative assets
run: npm run verify-output
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.coverage
coverage.xml
dist/
build/
node_modules/
.venv/
*.sqlite3
*.db
.repowise/
graphify-out/
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Product specifications and SDDs

When creating, revising, reviewing, or implementing product behavior:

1. Read [`specs/README.md`](specs/README.md) and the applicable SDD in full.
2. Consult [`specs/catalog.json`](specs/catalog.json) for capability status, blockers, and evidence.
3. Start new SDDs from [`specs/_template.md`](specs/_template.md). Adapt sections proportionally, but mark non-applicable concerns explicitly instead of deleting them silently.
4. Keep global product/domain/architecture requirements in `docs/`; SDDs assemble those requirements into one vertical, implementation-driving capability contract. Resolve conflicts rather than choosing one document silently.
5. Do not implement a capability whose SDD is not `Ready for implementation`, and do not begin production implementation without explicit owner approval even when the SDD is ready.
6. Update the SDD, catalog evidence, tests, user/operator documentation, and relevant ADRs together when an observable or architectural contract changes.
7. Include concrete user flows, states, failure/recovery behavior, security boundaries, a numeric test budget, acceptance scenarios, and requirement traceability.
8. Treat provider documentation and community implementations as dated evidence, not permanent guarantees. Keep official API research separate from community implementation evidence.

Until a repository-native specification validator is selected, every SDD change must at minimum pass the manual checks listed in `specs/README.md`.
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app/src \
SYMPHONIA_HOST=0.0.0.0 \
SYMPHONIA_PORT=8099 \
SYMPHONIA_DATABASE=/data/symphonia.sqlite3

WORKDIR /app
COPY src /app/src

RUN addgroup --system symphonia \
&& adduser --system --ingroup symphonia symphonia \
&& mkdir -p /data \
&& chown -R symphonia:symphonia /app /data

USER symphonia
VOLUME ["/data"]
EXPOSE 8099

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["python", "-c", "import http.client,json,os,sys; host=os.getenv('SYMPHONIA_HOST','127.0.0.1').strip(); host={'0.0.0.0':'127.0.0.1','::':'::1'}.get(host,host); port=int(os.getenv('SYMPHONIA_PORT','8099')); base=os.getenv('SYMPHONIA_INGRESS_PATH','/').rstrip('/'); connection=http.client.HTTPConnection(host,port,timeout=2); connection.request('GET',f'{base}/ready'); response=connection.getresponse(); payload=json.loads(response.read()); sys.exit(response.status != 200 or payload.get('service') != 'symphonia' or payload.get('status') != 'ready')"]

ENTRYPOINT ["python", "-m", "symphonia"]
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.PHONY: verify specs test spike-storage

specs:
PYTHONPATH=. python3 tools/validate_specs.py

test:
PYTHONPATH=src:. python3 -m unittest discover -s tests -v

spike-storage:
PYTHONPATH=src:. python3 tools/storage_recovery_spike.py --operations 1000

verify: specs
git diff --check
$(MAKE) test
71 changes: 69 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,69 @@
# symphonia
HomeAssistant App/Service to manage and enjoy your music
# Symphonia

Symphonia is a self-hosted, provider-independent music library hub, distributed primarily as a Supervisor-managed Home Assistant App. It is intended to help a person understand, move, and eventually synchronize their music across services such as Spotify and YouTube without treating any one provider as the source of the domain model.

> Your music library belongs to you. Providers are places where representations of that music happen to exist.

Symphonia is a library-management and interoperability project, not a new music player. Playback, recommendations, social features, transcoding, and multi-room audio are outside the initial scope. Its domain and application core remain independent of Home Assistant so the service can also run and be tested standalone; the deployment and integration pattern follows `vypdev/homeassistant-gateway`.

## Project status

**Implementation foundation stage. Provider adapters, durable import/copy execution, and an experimental Home Assistant App metadata scaffold exist; no published App image, complete UI, or OAuth flow is available yet.**

The current work combines a reviewable source of truth with the first owner-approved, dependency-free domain/persistence slice. Official provider feasibility still needs validation: Google's public YouTube Data API can manage YouTube video playlists, but the research performed for this specification did not identify an official API exposing the complete YouTube Music library model.

### Current adapter boundaries

- Spotify: official playlist reads plus confirmed playlist creation and entry append operations, subject to connection capabilities and provider policy.
- YouTube Data API: official video-playlist reads only; this is deliberately not represented as YouTube Music.
- Apple Music: experimental official library-playlist reads using separate developer and Music User Token inputs; writes and OAuth wiring remain out of scope until their feasibility gates are closed.

All adapters expose normalized identities and preserve unavailable/duplicate occurrences. Provider credentials are injected behind ports and are not accepted through App options, logs, or ordinary operation payloads.

## Documentation

Start with the [documentation map](docs/README.md). The horizontal specifications define shared product and architecture rules; the [SDD standard](specs/README.md) and [capability catalog](specs/CATALOG.md) turn those rules into reviewable vertical implementation contracts.

| Area | Source of truth |
| --- | --- |
| Capability readiness and implementation contracts | [SDD catalog](specs/CATALOG.md) |
| SDD lifecycle, template, and readiness gate | [SDD standard](specs/README.md) |
| Vision, scope, journeys, requirements | [Product specification](docs/product/product-specification.md) |
| Home Assistant-native UI and component contract | [UI specification](docs/product/home-assistant-ui-specification.md) and [UI foundation SDD](specs/home-assistant-native-ui.md) |
| Non-production Home Assistant-adjacent UI review | [Synthetic UI fixture](docs/development/ui-spike/README.md) |
| Non-production Lit/TypeScript/Vite feasibility | [Isolated build spike](docs/development/ui-lit-spike/README.md) |
| Vocabulary, entities, identity, playlists | [Domain model](docs/domain/domain-model.md) |
| System boundaries and operational qualities | [Architecture](docs/architecture/system-architecture.md) |
| Provider contract and capability semantics | [Provider specification](docs/providers/provider-specification.md) |
| Current official-provider evidence | [Provider research](docs/providers/provider-research.md) |
| Existing Home Assistant music projects and design lessons | [Ecosystem review](docs/providers/home-assistant-ecosystem-review.md) |
| Engineering and testing workflow | [Development specification](docs/development/development-specification.md) |
| Decisions already accepted | [Architecture decisions](docs/decisions/README.md) |
| Risks and unresolved choices | [Open questions](docs/open-questions.md) |

## Product shape

The intended first useful workflow is:

1. A local user connects provider accounts.
2. Symphonia imports provider playlists and track representations without losing provenance.
3. It resolves provider tracks to provider-independent recordings, surfacing uncertainty rather than hiding it.
4. The user reviews unresolved items and records durable manual decisions.
5. The user previews and executes a one-time playlist copy.
6. The operation history explains every match, omission, provider write, retry, and failure.

Persistent synchronization follows only after copy semantics and official provider feasibility are understood.

## Contributing during specification

Use requirement identifiers in issues, SDDs, tests, and future commits. Material implementation work starts from the applicable cataloged SDD and its numeric verification budget. A change to accepted behavior must update the relevant horizontal specification and, when it changes an architectural decision, add or supersede an ADR. Do not infer a decision from an open question, and do not start production implementation until the SDD is ready and the owner explicitly approves it.

Before opening a change, run `make verify`. It checks the SDD/catalog links and
traceability, patch whitespace, and the offline test suite without provider
accounts, Home Assistant, or network access.

The two current evidence spikes can be run independently: `make spike-storage`
exercises restart/lease recovery and SQLite backup validation, while the
[OAuth callback spike](docs/development/oauth-callback-spike.md) documents the
direct App-owned callback boundary. Both are research evidence, not published
production endpoints.
16 changes: 16 additions & 0 deletions addon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Symphonia Home Assistant App metadata

This directory is an experimental Supervisor App metadata scaffold. It follows
the App/service boundary used by `vypdev/homeassistant-gateway`:

- management traffic is Ingress-only (`8099` is not mapped as a direct port);
- only `/data` is persistent;
- the App requests no Home Assistant or Supervisor API access; and
- the service image runs the repository root `Dockerfile` as a non-root user.

The metadata is not a published release yet. The image reference, supported
architecture matrix, OAuth callback contract, Home Assistant-native UI,
backup/restore behavior and release signing still require their SDD gates
before stable publication. The future UI is governed by the
[Home Assistant-native UI foundation](../specs/home-assistant-native-ui.md); the
metadata scaffold does not imply that UI has been implemented.
23 changes: 23 additions & 0 deletions addon/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Symphonia
version: "0.1.0"
slug: symphonia
description: Provider-independent music library hub and playlist migration service
url: https://github.com/vypdev/symphonia
image: ghcr.io/vypdev/symphonia
arch:
- amd64
- aarch64
startup: application
boot: auto
stage: experimental
init: false
ingress: true
ingress_port: 8099
panel_icon: mdi:music-box-multiple
panel_title: Symphonia
map:
- data:rw
options:
log_level: info
schema:
log_level: list(debug|info|warning|error)
67 changes: 67 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Documentation map

**Status:** baseline for review
**Last reviewed:** 2026-09-20

This documentation is the horizontal implementation contract for Symphonia. It deliberately separates product intent, domain rules, architecture, provider facts, accepted decisions, and unresolved choices. The vertical, capability-level contracts live in the [SDD catalog](../specs/CATALOG.md) and select from these shared rules without overriding them.

## Sources of truth

| Document | Owns | Does not own |
| --- | --- | --- |
| [SDD standard and catalog](../specs/README.md) | Capability boundaries, readiness, end-to-end design, numeric test budgets, acceptance and evidence | Shared product policy or silent overrides of horizontal specifications |
| [Product specification](product/product-specification.md) | Outcomes, scope, journeys, product requirements | Entity design or technology choices |
| [Home Assistant-native UI specification](product/home-assistant-ui-specification.md) | Cross-cutting UI direction, component families, host context, accessibility, responsive and visual compatibility requirements | Feature-specific content/state or frontend framework selection |
| [Domain model](domain/domain-model.md) | Ubiquitous language, invariants, identity, copy and sync semantics | Provider API facts |
| [System architecture](architecture/system-architecture.md) | Boundaries, execution model, security and operations | Final implementation stack |
| [Provider specification](providers/provider-specification.md) | Provider port, capabilities, normalized errors | Claims about a specific API |
| [Provider research](providers/provider-research.md) | Dated, sourced facts about provider APIs | Product policy or permanent architecture |
| [Home Assistant music ecosystem review](providers/home-assistant-ecosystem-review.md) | Reusable patterns and cautions from existing HA music projects | Dependency selection or provider guarantees |
| [Development specification](development/development-specification.md) | Specification workflow, testing and delivery gates | Product scope |
| [Local quality audit](development/quality-audit.md) | Reproducible offline RepoWise/Graphify review and current architecture/test debt | A release or SDD readiness claim |
| [ADRs](decisions/README.md) | Decisions that have actually been accepted | Proposals and guesses |
| [Open questions](open-questions.md) | Decisions needed, assumptions, risk register, next design work | Accepted requirements |

## Normative language

`MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are normative. Lowercase uses are explanatory. Each requirement has a stable identifier:

| Prefix | Area |
| --- | --- |
| `SYM-PROD` | Product and user experience |
| `SYM-UI` | Home Assistant-native UI and component compatibility |
| `SYM-ACC` | Local and provider accounts |
| `SYM-LIB` | Unified library |
| `SYM-MATCH` | Identity resolution |
| `SYM-PL` | Playlist copy |
| `SYM-SYNC` | Persistent synchronization |
| `SYM-PROV` | Provider abstraction |
| `SYM-ARCH` | Architecture and persistence |
| `SYM-JOB` | Background execution |
| `SYM-SEC` | Security and credentials |
| `SYM-OBS` | Observability |
| `SYM-HA` | Home Assistant |
| `SYM-TEST` | Testing |
| `SYM-DEP` | Deployment |

Requirement identifiers are never reused. Removed requirements remain in history and should be marked superseded rather than silently renumbered.

## Status model

- **Accepted**: an explicit product constraint or recorded ADR; implementation may rely on it.
- **Proposed**: a reviewable direction; implementation must not treat it as settled.
- **Open**: a choice or fact still requiring evidence or owner input.
- **Research snapshot**: a dated external fact that must be revalidated before implementation.

The current baseline is documentation for review, not approval to implement. The repository must remain free of production application code until the owner explicitly approves implementation.

## Change workflow

1. Link a change to one or more requirement identifiers.
2. Select or create the capability SDD from the [catalog](../specs/CATALOG.md); use the [mandatory template](../specs/_template.md).
3. Update product/domain specifications before or with the SDD when shared behavior changes.
4. Record a durable, consequential decision as an ADR; do not use ADRs for routine coding choices.
5. Keep provider facts in dated research and link to official sources.
6. Move answered questions into requirements or ADRs and leave a pointer to the resolution.
7. Do not implement until the SDD passes its readiness gate and the owner explicitly approves implementation.
8. Add acceptance tests traceable to the affected requirements and update catalog evidence with the implementation.
Loading
Loading