# Ampelos Media System Planning

## Purpose

Ampelos is intended to become a single media control plane that combines the useful automation pieces of the *arr ecosystem with the existing `tv-manager` archive workflows.

The goal is not to expose Sonarr and Radarr as the source of truth. Ampelos should compute desired media state from user demand, media age/status, storage policy, and archive availability, then drive downstream systems to make that state real.

## Current Starting Point

The existing `tv-manager` project lives at `/srv/Ampelos/tv-manager`.

It currently manages local filesystem state across:

- Main/live media paths served by Plex.
- Backup media paths intended to be a 1:1 copy of main/live.
- Archive media paths that are not normally exposed in Plex.
- TV and movie media types.

Current capabilities include:

- Scanning primary, backup, and archive locations.
- Comparing inventory between locations.
- Restoring missing media from backup to primary.
- Moving shows or selected seasons into archive.
- Deduplicating archive files.
- Normalizing season and episode names.
- Transcoding archive media to 720p h265.
- A Flask web UI and JSON/SSE endpoints for the above workflows.

The current SQLite cache is only a scan cache. It should not become the authoritative database for the new system.

## High-Level Product Vision

Ampelos should provide one unified control panel for media requests, watch intent, library policy, and backend automation.

Users should be able to:

- Log in through Authentik at `charon.sticknife.com`.
- Browse/search media in an Ombi-like interface.
- Add shows and movies they are interested in.
- Maintain a limited "Watching Now" list.
- See request and availability status without managing automation details directly.

Admins should be able to:

- Define media retention and quality rules.
- See why each show/movie is in its current state.
- Override computed state when needed.
- Monitor archive sync, restore, import, dedup, and transcode-agent jobs.
- Keep Plex-facing storage small while preserving broad availability in archive.

## Core Principle

Ampelos computes desired state. Plex, trackers, download clients, and archive tools are implementation details.

Users should not manually manage monitor status, quality targets, storage routing, or retention decisions. Ampelos derives all of those from policy.

## Stack Decisions

Ampelos should use a Node.js web application for the main frontend and product surface.

Rationale:

- The user-facing site will be exposed to `sticknife_users`, so the frontend experience matters.
- Node.js gives a stronger path to a polished request/search/watchlist UI.
- The interface should feel closer to Ombi's user experience than to the current `tv-manager` admin table.

Backend and agent work can still use Python where it is a better fit.

Expected split:

- Node.js frontend and web application for users/admins.
- PostgreSQL database on `mimir`.
- Python workers/agents where useful for filesystem, hardware, scanning, transcode coordination, and reuse of current `tv-manager` logic.
- Native Ampelos TV/movie automation modules, built from scratch and inspired by *arr concepts but not derived from their code.

Open implementation choice:

- Exact Python worker shape: service process, job runner, or agent package.

## Storage Topology

Ampelos should explicitly model three storage tiers.

### Main / Live

Main/live storage is the Plex-facing active library.

Expected contents:

- Mostly full-resolution content.
- Actively airing TV.
- Recently ended TV according to policy.
- Recent movies according to policy.
- User-requested and Watching Now content.

Main/live is optimized for availability and normal playback. It is not the most trusted copy.

### Backup

Backup storage should be a 1:1 copy of main/live.

The backup server uses raidz, consumes more power, and is trusted more than main/live's snapraid storage. Because of that, it should be treated as the safer active copy, but not necessarily always online.

Expected behavior:

- Every file that belongs in main/live should eventually exist in backup.
- Backup drift should be tracked in the database.
- Backup sync work should queue while the backup server is unavailable.
- Ampelos should surface anything missing from backup as a protection gap.

### Archive

Archive storage holds content no longer in active circulation.

Expected contents:

- Older TV seasons not currently promoted.
- Older movies outside the active movie retention policy.
- Plex watchlist items that are wanted but not actively promoted.
- Compressed 720p archive copies where possible.

Archive is optimized for low data footprint and broad recoverability, not immediate full-quality playback.

## File Inventory and Deduplication

Ampelos should improve on the current `tv-manager` dedup behavior by making file inventory and dedup jobs database-backed and cross-storage-aware.

### File Inventory

Ampelos should track observed files across main/live, backup, and archive in the database.

For each file, the inventory should eventually capture:

- Storage tier: main/live, backup, or archive.
- Media association: series, season, episode, or movie.
- Path and normalized relative path.
- Filename.
- Size.
- Modified time.
- Optional content hash or partial hash.
- Parsed quality, codec, resolution, and release metadata where available.
- Whether the file is expected, extra, missing, duplicate, or queued for action.

The database should not blindly trust stale scan records. Each record needs freshness metadata tied to the storage availability and scan time.

### Dedup Role

Deduplication should live on Ampelos.

Ampelos should:

- Compare duplicate candidates across all three storage tiers.
- Apply quality/selection concepts inspired by *arr (codec, resolution, release group preference) to choose which duplicate to keep.
- Prefer keeping files based on storage policy, media state, quality, codec, resolution, release metadata, and trust level.
- Detect duplicate episodes or movies even when filenames differ.
- Support manual overrides that exclude specific files/releases from dedup consideration.
- Track rename/delete decisions as durable jobs instead of immediately mutating offline storage.
- Execute or dispatch those jobs when the relevant storage is available.
- Keep audit history for every deletion or rename decision.

Deletion should be conservative. Ampelos should only delete a candidate when the retained copy is verified in the correct tier and the policy reason is clear.

Dedup should start in review-only mode during development and debugging, but the intended production model is not permanently review-only. Once rules are proven, Ampelos should execute safe dedup actions automatically while still allowing review/override for ambiguous cases.

Example override case:

- A show may intentionally have multiple variants, such as black-and-white and color cuts of the same episode. Ampelos needs a way to mark one or both files as intentionally retained so dedup does not collapse them into a single copy.

### Deferred Storage Actions

Because backup/archive storage may be offline, dedup should create pending actions rather than assuming immediate access.

Potential action types:

- Delete duplicate file.
- Rename file.
- Move file between tiers.
- Restore archive file to main/live.
- Copy main/live file to backup.
- Mark missing expected copy.
- Reprobe file metadata.

Each action should have:

- Target storage tier.
- Required availability condition.
- Reason code.
- Safety preconditions.
- Dry-run output.
- Execution result.
- Audit events.

### Transcoding Role

Transcoding should move out of Ampelos into a dedicated transcode agent.

Expected behavior:

- The transcode server boots only when backup/archive storage is online.
- Ampelos queues or flags archive transcode candidates.
- The transcode agent claims work from the database or receives jobs from Ampelos.
- The agent compresses eligible archive media to 720p h265.
- Ampelos records the result and updates observed file inventory.

Ampelos should own the policy and job state. The transcode agent should own the CPU/GPU-heavy execution.

## Major External Systems

### Authentik

Authentication should be delegated to Authentik at `charon.sticknife.com`.

Expected model:

- OIDC login.
- Ampelos trusts Authentik identity and group claims.
- Local user records map external identities to preferences, watch lists, permissions, and quotas.
- `sticknife_users` maps to normal user access.
- `sticknife_admins` maps to full admin access.

Open decisions:

- Exact OIDC issuer URL.
- Whether regular users can request all media or only manage their own lists.

### Mimir Database

The new system should use the database on `mimir` as its authoritative store.

Expected model:

- PostgreSQL.
- Single database named `ampelos`.
- Separate tables or schemas can model TV, movies, music, users, jobs, file inventory, and policy data.
- No new feature should depend on SQLite as source of truth.
- Local caches are acceptable for expensive scans or external API responses, but must be rebuildable.

Open decisions:

- Connection method and credentials.
- Migration tool.
- Backup expectations.

### Sonarr and Radarr

Ampelos should not fork, adapt, or treat Sonarr and Radarr as API backends. The *arr data model is fundamentally incompatible with Ampelos's requirements:

- Sonarr enforces one canonical file per episode. Intentional variants (e.g. black-and-white and color cuts of the same episode) cannot be represented.
- Sonarr and Radarr have no model for multi-tier storage with different quality targets per tier.
- Both services derive "missing" state from live filesystem visibility. Offline archive storage would be misread as missing content, triggering unwanted re-downloads.
- Quality profiles are per-series, not per-storage-tier.

Ampelos should instead build its own native TV and movie automation, taking *arr's concepts as reference and inspiration rather than as source material.

Ampelos-native responsibilities:

- Series and movie metadata integration (TVDB and TMDB APIs directly).
- Release name parsing using `guessit` (Python) or equivalent.
- Release search via the indexer backend (Jackett or Prowlarr).
- Release selection and quality decision logic driven by Ampelos policy and desired state.
- Download client integration (qBittorrent, Transmission).
- Import pipeline with full storage-tier awareness.
- File organization and naming.
- Upgrade logic driven by the desired-state model rather than quality profiles.
- Multi-tier inventory: intentional variants, offline archive state, and cross-tier dedup handled natively.

Sonarr and Radarr should be retired from the stack as Ampelos automation reaches feature parity. No Sonarr/Radarr API compatibility layer is needed.

Open decisions:

- Whether TV and movie automation share one internal policy engine with media-type adapters, or use parallel but separate modules.
- Exact TVDB/TMDB integration approach and fallback behavior when metadata sources disagree.

### Plex

Plex remains the consumption surface.

Ampelos should reason about what belongs in Plex-facing storage versus archive.

Expected Plex-facing media:

- "Watching Now" TV shows.
- High-definition currently airing or recently ended shows according to policy.
- Movies that are actively requested or within the configured freshness window.

Archive-only media should not normally be visible in Plex unless restored or promoted.

Open decisions:

- Whether Ampelos should read Plex watch history, active sessions, or user libraries directly.
- Whether existing Plex watchlists remain an input signal.
- Whether Plex availability should be polled or inferred from filesystem and Ampelos inventory state.

## Existing Compose Services

The current Docker Compose stack at `/home/odin/docker/compose/docker-compose.yml` is a useful map of what Ampelos may eventually fold into one control panel.

The near-term goal should not be to rewrite every service. The practical path is to absorb the UX, policy, queueing, and explanation layers first, while keeping specialized services as backend adapters where they are already good at their jobs.

### Ombi

Current role:

- User-facing request portal.
- Media discovery and request workflow.

Ampelos direction:

- Absorb into Ampelos.
- Replace with the Ampelos user-facing request, browse, and Watching Now UI.
- Preserve the useful product behavior: simple user requests without exposing automation internals.
- Ombi's UX is a strong reference point for the normal user interface.
- Existing Ombi code may be reused where licensing and architecture make sense, with Ampelos adding Watching Now and the new policy model.

Likely future:

- Retire once Ampelos has request UX and Authentik login.

### Sonarr

Current role:

- TV series metadata.
- Monitoring.
- Release search.
- Download/import coordination.
- TV file organization.

Ampelos direction:

- Fork or extract the useful TV automation internals.
- Ampelos should own the policy that decides monitor status, quality profile, tags, root folder, storage tier, and dedup behavior.
- Sonarr's existing mechanics are useful source material, but the long-term UI and policy should live in Ampelos.

Likely future:

- Fork-first path for the TV automation layer.
- API compatibility may still be useful temporarily during migration.

### Radarr

Current role:

- Movie metadata.
- Monitoring.
- Release search.
- Download/import coordination.
- Movie file organization.

Ampelos direction:

- Same as Sonarr: retire in favor of native Ampelos movie automation.
- Ampelos computes movie retention, quality target, and Plex-facing/archive state natively.

Likely future:

- Retired once the `automation.movies` module reaches feature parity.

### Jackett

Current role:

- Indexer bridge supporting a wide range of public and private trackers.

Ampelos direction:

- Likely primary indexer backend. Jackett has better compatibility with certain indexers than Prowlarr and may be the better fit depending on which trackers are in use.
- Ampelos queries Jackett's API for release search and should surface indexer health.
- Ampelos does not reimplement indexer protocol handling.

Likely future:

- Durable backend dependency for release search.

Open decision:

- Confirm whether Jackett or Prowlarr better covers the active indexer set. One should be chosen as the primary; running both long-term adds maintenance overhead for no clear gain.

### Prowlarr

Current role:

- Indexer management for *arr apps.

Ampelos direction:

- Secondary indexer backend option. May be preferred if its indexer coverage and management UI prove more useful than Jackett for the active tracker set.
- Ampelos can query Prowlarr's API for release search in the same way as Jackett.

Likely future:

- Either replaces Jackett as the primary indexer backend, or is retired once a single indexer backend is chosen.

### FlareSolverr

Current role:

- Cloudflare/challenge solving helper for indexers.
- Compose service key is `flaresolver`, while the container/image use `flaresolverr`.

Ampelos direction:

- Keep as backend infrastructure.
- Ampelos should only surface health/config dependency where indexers require it.

Likely future:

- Remains external unless indexer handling is deeply folded into Ampelos.

### Transmission OpenVPN

Current role:

- Torrent download client behind VPN.
- Writes downloads under `/Niflheim/Downloads`.

Ampelos direction:

- Keep as backend infrastructure.
- Ampelos should track download-client health, queue state, completed imports, and failure reasons directly via the client API.
- VPN credentials and network policy should remain outside normal user workflows.

Likely future:

- External download backend.
- Admin status surfaced in Ampelos.

Security note:

- The compose file currently contains VPN credentials in plaintext. When this stack is revised, move secrets into an environment file or secret manager and avoid copying them into Ampelos config.

### qBittorrent PIA Container

Current role:

- Commented-out alternate VPN torrent client.

Ampelos direction:

- Treat as an alternate download backend option, not core Ampelos scope.

Likely future:

- Decide on one torrent backend and expose it through a generic download-client adapter.

### Bazarr

Current role:

- Subtitle management for movies and TV.

Ampelos direction:

- Keep as a backend initially.
- Ampelos should surface subtitle availability and allow admin-level subtitle policy, but not immediately reimplement subtitle provider/search logic.

Likely future:

- Backend adapter.
- Potentially absorbed later if subtitle policy becomes central.

### Lidarr

Current role:

- Music library automation.

Ampelos direction:

- Defer from the first TV/movie scope.
- The architecture should leave room for media-type adapters beyond TV and movies.

Likely future:

- Possible later module once TV/movie policy is stable.

### slskd

Current role:

- Soulseek daemon for music downloads.

Ampelos direction:

- Keep as backend infrastructure for music workflows.
- Defer until music support is intentionally designed.

Likely future:

- Backend adapter under a future music module.

### Soularr

Current role:

- Bridge between Lidarr and slskd.
- Periodic script-driven matching/download workflow.

Ampelos direction:

- Defer from first scope.
- If music is added, Ampelos can absorb the dashboard/status/policy layer while leaving slskd as backend.

Likely future:

- Candidate for folding into a future music automation module.

### Soularr Dashboard

Current role:

- Custom dashboard for Soularr logs and failures.

Ampelos direction:

- Absorb dashboard/status behavior if music support is added.
- The broader pattern is relevant now: Ampelos should centralize job logs, failures, and retry workflows.

Likely future:

- Replace with Ampelos admin job views.

### Chaptarr

Current role:

- Book/audiobook automation.
- Can use PostgreSQL but currently appears self-contained.

Ampelos direction:

- Defer from first TV/movie scope.
- Keep in mind as another future media-type adapter.

Likely future:

- Possible books/audiobooks module after TV/movie foundations.

### Readarr

Current role:

- Commented-out book automation.

Ampelos direction:

- Treat as deferred/legacy book automation.
- Do not include in first Ampelos build unless book scope is pulled forward.

Likely future:

- Reevaluate alongside Chaptarr.

### Watchtower

Current role:

- Commented-out automatic container updater.

Ampelos direction:

- Not a media workflow dependency.
- Ampelos may eventually expose deployment/version status, but should not own automatic updates initially.

Likely future:

- Keep outside Ampelos.

## Service Folding Strategy

Initial Ampelos scope should focus on TV and movies:

- Replace Ombi as the user-facing request and Watching Now interface.
- Build native TV and movie automation inspired by *arr concepts: metadata integration, release parsing, release search via Jackett, download client orchestration, and a tier-aware import pipeline.
- Retire Sonarr and Radarr as Ampelos automation reaches feature parity.
- Use Jackett/FlareSolverr/download clients as backend infrastructure.
- Integrate the current `tv-manager` storage/archive workflows.
- Add durable database-backed jobs and audit trails.

Secondary scope:

- Subtitle visibility and policy through Bazarr.
- Better indexer/download health visibility.
- Music support through Lidarr/slskd/Soularr concepts.
- Books/audiobooks through Chaptarr or Readarr concepts.

This produces a layered system: one Node.js product shell and policy brain, native Ampelos TV/movie automation inspired by *arr, backend infrastructure adapters (Jackett, download clients, Plex, Authentik), and Python agents where hardware and filesystem work is better handled outside the web app.

## Demand Sources

Ampelos should aggregate media demand from multiple sources.

### Plex Watchlists

Existing watchlist scraping should continue.

Default behavior:

- Watchlisted TV should sync on a slower weekly cycle.
- Default watchlist quality should be archive-oriented 720p.
- Default watchlist items should go straight to archive, not Plex-facing primary storage.

Rationale:

This preserves broad user interest without letting every casual watchlist entry consume high-quality Plex-facing storage.

### Watching Now

"Watching Now" is a short, intentional list managed through Ampelos.

Expected behavior:

- Each user starts with 5 TV show slots and 10 movie slots.
- Any show on any user's Watching Now list is promoted.
- The whole show should become available quickly in at least archive-quality 720p.
- Ampelos should restore existing 720p copies from archive to Plex-facing storage when available.
- Ampelos should return the show to Sonarr/tracker management so higher quality can be acquired or upgraded when policy allows.
- Ampelos should notify users when they add something already covered by automatic live rules, such as currently airing TV or recent movies.
- Users should still be allowed to add automatically covered items because Watching Now can imply broader availability than the automatic rule. Example: season 11 may be airing and live, but a user may want season 2 restored from archive.

Open decisions:

- Whether admins can grant larger limits.
- Whether a user can pin a single season instead of the whole show.

### Currently Airing

"Currently Airing" should override default archival behavior for TV.

Expected behavior:

- Any show that is still running, or ended less than one year ago, is treated as currently relevant.
- The currently airing season should be live at the highest available quality as episodes release.
- The most recent completed season should be live at the highest available quality.
- For a show that stopped airing within the last year, the final season is retained in live.
- Older seasons default to archive unless also promoted by Watching Now.

Worked example of the three live-retention cases:

| Show state | Retained live |
| --- | --- |
| Still airing | Current season + most recent completed season |
| Ended < 1 year ago | Final season |
| Ended >= 1 year ago | Nothing, unless Watching Now or admin override |

Open decisions:

- Metadata source for show status and end date.
- Exact definition of "most recent completed season".
- Whether the one-year cutoff is configurable globally.

### Admin Overrides

Admins should be able to pin or force state when policy is insufficient.

Potential override types:

- Force Plex availability.
- Force archive-only.
- Force quality profile.
- Force monitor/unmonitor.
- Force ignore.
- Temporary promotion with expiry.

Overrides should be auditable and explainable in the UI.

## Desired State Model

Ampelos should represent media state as computed desired state plus observed actual state.

### Desired State

For each series, season, episode, movie, and file group where applicable, Ampelos should compute:

- Wanted or unwanted.
- Plex-facing or archive-only.
- Minimum acceptable quality.
- Preferred quality.
- Whether Ampelos automation should actively seek or upgrade it.
- Target root folder.
- Archive restore requirement.
- Archive eviction requirement.
- Reason codes explaining the decision.

Example TV reason codes:

- `watching_now`
- `currently_airing`
- `plex_watchlist_archive`
- `recently_ended`
- `admin_override`
- `stale_archive_only`
- `quota_limited`

### Actual State

Actual state should be observed from:

- Database records.
- Filesystem scans.
- Existing `tv-manager` inventory logic.
- Download client state.
- Plex availability where needed.
- Archive worker job results.

Observed state should be refreshable and repairable. If an external system drifts, Ampelos should detect and reconcile it.

## TV Policy Draft

Default policy:

- Plex watchlist TV: wanted in archive at 720p.
- Not in any demand source: archive-only if already present; otherwise not wanted.
- Watching Now: wanted in Plex-facing storage, full show restored at 720p minimum, monitored for upgrades.
- Currently airing or ended less than one year ago: most recent completed season available in high definition, older seasons archive-only unless promoted.
- Admin override: wins over automated policy unless explicitly expired.

Quality draft:

- Archive default: 720p h265.
- Plex active default: highest available quality according to policy.
- 4K should be preferred for media marked live where available, with storage policy still able to constrain exceptional cases.

Storage movement draft:

- Archive restore happens before tracker upgrades when an acceptable archive copy exists.
- Demotion from live happens only when the item is not Watching Now, not recent/current, not admin-pinned, and has been safely archived.

Terminology, because the earlier wording ("archive eviction") read as deleting
from the archive when it meant the opposite:

- **Restore** — archive to live.
- **Demotion** — live to archive, once a verified archive copy exists. This is
  what the rule above governs.
- **Eviction** — deleting a redundant copy. Only ever applied to a duplicate,
  never to the canonical copy of an item.
- Existing `tv-manager` move/restore workflows are good candidates for the first archive adapter implementation.
- Existing `tv-manager` dedup logic should be evolved into database-backed cross-storage dedup planning.
- Existing `tv-manager` transcode logic should be moved behind a dedicated transcode-agent interface rather than kept as an Ampelos in-process worker.

## Movie Policy Draft

Movies likely need looser retention rules because they consume less space relative to TV.

Initial draft:

- Keep movies in Plex-facing storage if they are on any user's list.
- Keep movies in Plex-facing storage if they are newer than a configurable age threshold, probably 5 years.
- Keep movies in Plex-facing storage permanently if they are on the admin-managed classics list.
- Archive older, lower-demand movies where useful.
- Be more liberal than TV with Plex-facing availability.
- Prefer 4K/highest available quality for movies marked live.

Open decisions:

- Whether movie watchlists should always remain Plex-facing.
- What movie age threshold should apply.

## Archive Sync Model

Bragi/archive storage may not be continuously online.

Expected behavior:

- Weekly sync windows are acceptable for low-priority watchlist archival.
- Bragi can power on for a bounded sync cycle, exchange data, then shut down.
- Users cannot trigger Bragi wake/sync.
- Ampelos tracks requested changes in the database and enacts them during scheduled Bragi availability.
- Watching Now and currently airing changes should show a clear "queued until next sync" status when they require Bragi.

Open decisions:

- How Ampelos detects Bragi availability.
- How the weekly schedule is configured and reported.

## Service Architecture Draft

Ampelos should likely be built as a single service with clear internal modules before splitting anything apart.

Proposed modules:

- `auth`: OIDC/Auth session handling and roles.
- `catalog`: metadata search and normalized media records.
- `users`: user profiles, quotas, and preferences.
- `demand`: watchlists, Watching Now, requests, and source signals.
- `policy`: computes desired state from demand and rules.
- `automation.tv`: native Ampelos TV automation — metadata, release search, download orchestration, and tier-aware import for TV.
- `automation.movies`: native Ampelos movie automation — same responsibilities scoped to movies.
- `integrations.plex`: Plex state adapter.
- `integrations.archive`: wrapper around current tv-manager capabilities.
- `storage`: storage tier definitions, availability, scan state, and file inventory.
- `dedup`: cross-storage duplicate detection and safe action planning.
- `transcode`: transcode-agent job coordination and result tracking.
- `jobs`: durable job queue and worker execution.
- `ui`: unified control panel and request interface.
- `admin`: policy, audit, and operations views.

Ampelos builds its own TV/movie automation natively, taking *arr concepts as reference rather than inheriting their code. The `automation.tv` and `automation.movies` modules should be isolated behind clear internal boundaries so the policy engine and UI do not become tightly coupled to their implementation details.

## Database Concepts

Potential core tables:

- `users`
- `user_identities`
- `media_items`
- `series`
- `seasons`
- `episodes`
- `movies`
- `external_ids`
- `demand_sources`
- `watchlist_items`
- `watching_now_items`
- `classics`
- `admin_overrides`
- `policy_runs`
- `desired_states`
- `observed_states`
- `jobs`
- `job_events`
- `arr_instances`
- `arr_mappings`
- `archive_locations`
- `storage_tiers`
- `storage_availability`
- `file_inventory`
- `file_identity_groups`
- `dedup_candidates`
- `storage_actions`
- `transcode_jobs`
- `audit_events`

Important database properties:

- External IDs should be first-class, especially TVDB/TMDB/IMDb IDs.
- Desired state should keep reason codes so the UI can explain decisions.
- Jobs should be durable so sync work survives restarts.
- File inventory should be rebuildable from scans.
- File action history should survive rescans so deletion and rename decisions remain auditable.

## UI Concepts

Regular user views:

- Search and browse media.
- Add/remove from personal lists.
- Manage Watching Now slots.
- See when an item is already covered by current-airing or recent-movie policy before using a slot.
- See availability: available now, restoring, queued, archive-only, not available.
- See basic quality/season availability without exposing automation internals.

Admin views:

- Demand dashboard.
- Policy explanation per media item.
- Current desired vs actual state.
- Forked TV/movie automation reconciliation status.
- Archive restore/evict queue.
- Storage availability and protection gaps.
- Dedup candidate review and pending storage actions.
- Worker and transcode-agent status/logs.
- User quotas and overrides.
- Quality and retention rules.

The UI should be a real app, not a thin wrapper around backend automation tools.

UI priority:

- Build a utilitarian admin interface and a simple user interface in parallel.
- The admin interface should expose correctness, jobs, policy decisions, and storage state.
- The user interface should stay simple and Ombi-like, focused on search, requests, availability, and Watching Now.

## Implementation Phases

### Phase 0: Planning and Inventory

- Finalize policy rules.
- Document current tv-manager behavior.
- Decide database and migration tooling.
- Choose the indexer backend (Jackett vs Prowlarr) and confirm active indexer coverage.
- Plan the native TV/movie automation module boundaries.
- Confirm Authentik OIDC claim names for `sticknife_users` and `sticknife_admins`.
- Choose the Node.js framework and frontend architecture.

### Phase 1: Foundation

- Create the new Ampelos service skeleton under `/srv/Ampelos`.
- Connect to Authentik for login.
- Connect to the database on `mimir`.
- Add migrations.
- Add durable jobs.
- Model storage tiers and storage availability.
- Import or wrap current tv-manager scan/restore/archive functionality.
- Start recording file inventory in the database.

### Phase 2: Demand and Policy Engine

- Model users, watchlists, Watching Now, and admin overrides.
- Add TV policy computation.
- Store desired state with reason codes.
- Add admin explanation view.

### Phase 3: Native TV/Movie Automation

- Integrate TVDB and TMDB directly for series and movie metadata.
- Integrate the chosen indexer backend (Jackett or Prowlarr) for release search.
- Add release name parsing using `guessit` or equivalent.
- Build release selection logic driven by Ampelos desired state and quality policy.
- Add download client integration (qBittorrent, Transmission).
- Build the import pipeline with full storage-tier awareness: correct tier routing, intentional variant support, and offline archive handling.
- Add file organization and naming.
- Add upgrade monitoring driven by the policy engine rather than external quality profiles.
- Begin retiring Sonarr and Radarr once TV and movie automation reaches parity.

### Phase 4: Archive Operations

- Queue archive restores and evictions.
- Integrate existing tv-manager move/restore workflows.
- Add database-backed cross-storage dedup planning.
- Add deferred rename/delete/copy actions for offline storage.
- Add transcode-agent job coordination instead of local transcoding.
- Add Bragi availability and sync-window behavior.
- Add failure recovery and audit logs.

### Phase 5: User-Facing Request App

- Add Ombi-like search and request UX.
- Add Watching Now management.
- Add availability and queue status.
- Add movie policy.
- Add user notifications for items already covered by automatic live/recent rules.

### Phase 6: Broader Media and Deep Cleanup

- Preserve Ampelos policy engine as the source of truth.
- Remove or retire replaced legacy services once feature parity is sufficient.
- Evaluate subtitles, music, books, and audiobooks as later media-type modules.

## Risks and Design Constraints

- Building native TV/movie automation from scratch is substantial scope. Release name parsing, quality selection edge cases, and download client quirks represent years of accumulated handling in *arr. `guessit` covers most parsing, but expect edge cases.
- The `automation.tv` and `automation.movies` modules should stay behind clean internal boundaries so implementation complexity does not leak into the policy engine and UI.
- Archive restore and evict operations are destructive enough to require audit logs and dry-run tooling.
- Filesystem scans alone are not enough for rich request UX; external metadata IDs need to be normalized early.
- Bragi offline windows require durable queued work and clear UI status.
- Backup/archive offline windows require deferred storage actions with explicit preconditions.
- Multiple users can demand the same item for different reasons; policy must aggregate demand cleanly.
- The new service should not inherit the current SQLite cache as persistent app state.
- Deduplication across tiers can become destructive if identity matching is weak; early versions should favor flagging and review over aggressive deletion.
- Main/live and backup have different trust and power profiles, so "duplicate" does not always mean "safe to delete."

## Resolved Early Decisions

1. Main product surface should be Node.js, with Python retained for backend hardware/filesystem agents where useful.
2. Database should be PostgreSQL on `mimir`, using a single database named `ampelos`.
3. Authentik groups are `sticknife_users` for normal users and `sticknife_admins` for full admins.
4. Ampelos should build native TV/movie automation inspired by *arr concepts, not fork or API-adapt Sonarr/Radarr. The *arr data model cannot represent multi-tier storage, offline archive inventory, or intentional file variants.
5. Dedup should start review-only during development, then graduate to active execution for proven safe rules.
6. Watching Now quotas start at 5 TV shows and 10 movies per user.
7. Users should be warned when adding items already covered by automatic current/recent rules, but not blocked.
8. Movies newer than the configured recent threshold stay live; admin-managed classics stay live permanently.
9. 4K/highest available quality is preferred for media marked live.
10. Currently airing episodes and the most recent completed season stay live at highest available quality.
11. The recently ended TV cutoff starts at one year.
12. Bragi runs on a scheduled weekly cycle; users cannot trigger wake/sync.
13. Ampelos needs both a utilitarian admin interface and a simple Ombi-like user interface.
14. The Node.js framework is Next.js. The admin-heavy UI with server-side job state, file inventory, and policy data is a good fit for the App Router/RSC model.
15. The database migration tool is Drizzle. The schema is complex enough to warrant readable plain-SQL migrations, and Drizzle provides TypeScript type safety in the Node app while the Python workers can consume the raw SQL files independently.
16. The movie "recent" age threshold is 5 years, provisional until the resulting live footprint is measured.
17. Watching Now promotes the entire show, never a single season. Recent-season availability for currently airing shows is handled by the automatic rule, not by user pinning.
18. 4K/highest-available is a general live-tier preference, not movie-specific. It applies to any media marked live.
19. **Plex watchlist** demand (the scraped list, `watchlist_items`) alone does not promote to live; it targets archive at 720p on a weekly cycle. This is distinct from **Watching Now** (`watching_now_items`), the quota-limited list managed in Ampelos, which does promote to live. Other rules (currently airing, Watching Now, admin override) can still place a watchlisted item live. "Default" in the Plex Watchlists section means exactly this, not "never Plex-facing".
20. Move verification and dedup are two separate processes with different identity strategies:
    - **Move**: full hash verification before anything is deleted. Slow is acceptable; moves are infrequent after the initial reconciliation.
    - **Dedup**: decided from the canonical tier recorded in the tracking database, not from hashing.
      - If the canonical tier is archive and a copy reappears in live (redownloaded, then flagged for archival again), the live copy is deleted even when it is higher quality. Canonical placement wins over quality.
      - If duplication occurs within live/backup (an upgrade was acquired), the highest-quality copy is kept.
21. Backup mirrors live. Mirror means live is a subset of backup, not that the two are identical: files present on backup but not live are never deleted, they are moved to live or archive according to the retention rules.
22. Bragi is the bare-metal host that runs the edda and silenus guests. The archive sync model still applies at that level; the power on/off cycle is manual until automated.

## Remaining Decisions Needed

1. Which indexer backend should be primary: Jackett or Prowlarr? Jackett has better compatibility with some trackers; Prowlarr has a more modern management UI. One should be chosen and the other retired to avoid maintaining both.
4. Which metadata provider should Ampelos use for search and status: TMDB, TVDB, or a combination? What is the fallback behavior when they disagree? (In practice TMDB is what is implemented and populated; TVDB is only reached by the smoke test.)
5. How much of the existing `tv-manager` code should be reused directly versus moved behind a new archive adapter?
7. How should the transcode agent claim work: database polling, Ampelos API, message queue, or a simpler job lease table? (Currently neither: `agent/ampelos-transcoder.mjs` walks and probes the archive itself.)

Resolved and moved to Resolved Early Decisions: 6 (see #20), 8 (see #16), 9 (see #17).
