# Cinema RPG — Personal Progress

Personal Progress is the active Cinema RPG progression mode. It is designed for an individual's private, non-competitive profile and uses the activity that Plex exposes to that person's connected account.

## Product contract

- The user connects their own Plex account to Comrade.
- Comrade discovers Plex servers and connections accessible to that account, including owned and shared servers.
- Each server source is checked independently so a permission failure in one source does not block activity available from another.
- Qualifying post-start movie activity enters the existing idempotent XP, quest, achievement, franchise, profile-statistics, and Activity pipelines.
- Playback below the completion threshold remains visible as an **In progress** Activity card with its highest Plex percentage. It grants no XP or reward progress.
- A recent-history row means playback ended, not that the movie was completed. Recent history keeps Plex's reported offset and must reach the active completion threshold before it can grant XP or reward progress.
- Live evidence remains attached to the active watch session when the same sync also reconciles recent history. Activity displays the current title as **Playing now** and refreshes it after every successful foreground tracking check.
- Resumed playback merges into the recent incomplete movie session even when Plex assigns a new session identifier. Completion replaces the partial state and still awards first-watch XP only once.
- Movie cards use authenticated, server-proxied Plex artwork with a local placeholder fallback. Plex tokens and private server URLs never enter browser markup or poster URLs.
- Artwork is limited to movie watch and in-progress events. Achievement, quest, franchise, level, system, and adjustment events use event-specific RPG icons instead of inheriting the triggering movie poster.
- Watched status dated before Personal Progress started is imported only as earlier Plex history. It cannot grant active XP or complete active rewards.
- Personal Progress does not require server-owner registration, Tautulli, an administrator playback mapping, or a complete Comrade library sync. Tautulli is now an optional faster owner-assisted source with separate webhook and retained-history connections.
- When a verified Plex server owner participates, a user may separately opt into owner-assisted exact-account session and recent-history tracking. This is an optional reliability source; Personal Progress remains the fallback.
- Cinema Profile and Cinema RPG read one canonical viewing snapshot. Duplicate copies across Plex locations are merged by stable movie identifiers and title/year fallback, while the highest play count and union of genre metadata are retained.
- Both API responses now invoke the same canonical viewing loader. Cinema Profile never falls back to a competing browser-only total; if the shared snapshot cannot load, those overlapping totals are shown as unavailable instead of disagreeing with Cinema RPG.
- Shared-server live-session and server-history permission failures are treated as unavailable capabilities rather than user-facing errors. A shared user is warned only when their watched-library data cannot be read.
- Search, Randomizer, History, and browsing still benefit from the normal Comrade library synchronization.
- The mode is not suitable for competitive verification. Plex visibility varies by server version, server permissions, account type, and source endpoint, so results may be incomplete and are not independently verified.

The Stage 8 deterministic mapping tables are now reused only when a user explicitly opts into a participating owner's server. Ordinary Personal Progress still does not require or read an administrator-created mapping. Competitive Verified Progress remains later work.

## Plex evidence

Personal Progress checks three kinds of Plex evidence:

1. **Live sessions** — matching movie sessions for the connected Plex identity. Playback qualifies when progress reaches the active completion threshold.
2. **Recent history** — ended playback records Plex exposes for the connected account. A post-start record qualifies only when Plex supplies runtime and offset metadata that reaches the active completion threshold.
3. **Watched library status** — movie-section watched flags read directly from accessible Plex servers. Post-start completion can qualify; pre-start items stay historical-only.

The watched-status scanner processes bounded pages and stores a per-user, per-server, per-section cursor in `rpg_plex_sync_state`. Completed sections are eligible for a periodic rescan so new watched activity can still be reconciled.

Movie rewards retain the existing first-watch protection: one normal movie XP award per Plex server and rating-key identity. Source event keys remain idempotent, and all downstream RPG processors use the same normalized watch event.

## Database changes

Apply `supabase/migrations/20260715_cinema_rpg_personal_progress.sql`, followed by `supabase/migrations/20260716_plex_owner_assisted_tracking.sql` when enabling the optional owner-assisted layer, `supabase/migrations/20260717_tautulli_owner_webhooks.sql` for real-time owner webhooks, and `supabase/migrations/20260718_tautulli_history_sync.sql` for retained-history import and daily reconciliation.

The migration:

- adds `progress_mode`, `personal_progress_started_at`, and `last_personal_sync_at` to `rpg_profiles`;
- expands the watch-evidence constraint for `plex_session`, `plex_history`, and `plex_watched_status`;
- creates service-role-only resumable scan state in `rpg_plex_sync_state`;
- updates seeded player-facing achievement and franchise copy;
- preserves legacy internal names such as `verified_xp` and `verified_percentage` to avoid a risky destructive rename.

The API exposes neutral aliases including `personal_xp`, `progress_percentage`, `progressMovies`, and `progressHours`. Older internal fields remain available for compatibility.

The owner-assisted migration adds service-role-only owner-server and user-consent records, the `owner-opt-in` mapping source, and explicit owner session/history evidence types. See `OWNER_ASSISTED_TRACKING.md`.

The Tautulli migration adds a hash-only one-time webhook credential and `tautulli_session` evidence. The public webhook accepts only signed movie playback events and still requires one exact active user consent. See `TAUTULLI_OWNER_SETUP.md`.

The Tautulli history migration stores API keys in Supabase Vault, verifies the Tautulli/Plex machine identity, imports normalized opted-in history with stable row IDs, enriches runtimes, genres, posters, and external IDs through Tautulli metadata, and adds the `tautulli_history` evidence type plus a once-daily authenticated Vercel reconciliation. See `TAUTULLI_HISTORY_SYNC.md`.

## Deployment order

1. Back up the target database according to `BACKUP_AND_ROLLBACK.md`.
2. Apply all earlier Cinema RPG migrations through Stage 8 if they are not already present.
3. Apply `20260715_cinema_rpg_personal_progress.sql` in Supabase.
4. Apply `20260716_plex_owner_assisted_tracking.sql` for optional owner participation.
5. Apply `20260717_tautulli_owner_webhooks.sql` for optional real-time Tautulli events.
6. Apply `20260718_tautulli_history_sync.sql` for optional retained history and daily reconciliation.
7. Add `CRON_SECRET` to Vercel.
8. Deploy this repository version to Preview.
9. Hard-refresh Cinema RPG and start Personal Progress with a test account.
10. Press **Refresh Plex** and inspect the Personal Plex Sync diagnostics. Shared connections should show watched-status health without owner-only playback permission warnings.
11. Validate one owned-server account and one shared-server account where available.
12. Enable one owned server, opt in with a shared user, and complete the checks in `OWNER_ASSISTED_TRACKING.md`.
13. Configure Tautulli with `TAUTULLI_OWNER_SETUP.md`, confirm a real start and stop event, then connect/import history with `TAUTULLI_HISTORY_SYNC.md`.
14. Promote only after the Preview checks below pass.

The migration must be applied before the matching API code because the API reads and updates the new profile and scan-state fields.

## Preview validation

- Confirm Personal Progress starts without an administrator playback mapping.
- Confirm no Tautulli configuration or server-owner Comrade account is required.
- Confirm an owner can configure the signed webhook without entering a Tautulli URL or API key into Comrade.
- Confirm the separate retained-history form accepts only a public HTTPS URL, never returns its saved URL/key, and rejects a Tautulli instance attached to a different Plex server.
- Import retained history and confirm Cinema Profile and Cinema RPG update from the same canonical snapshot without changing XP, quests, achievements, franchises, or streak rewards for pre-cutoff rows.
- Confirm a Tautulli event is ignored until the exact Plex user opts into that owner server.
- Stop a Tautulli-tracked movie below the threshold and confirm it remains partial with no XP.
- Confirm an owner must pass live Plex ownership verification before enabling a server.
- Confirm a shared user must opt in with an exact connected Plex identity and can revoke consent while owner tracking is paused.
- Confirm personal and owner-assisted sources use one idempotent event identity and never duplicate XP.
- Confirm accessible owned and shared servers appear in diagnostics.
- Confirm Cinema Profile and the Cinema RPG Plex Viewing Snapshot show identical unique movies, rewatches, unique hours, total hours, genres, and decades.
- Complete a new movie after activation and confirm exactly one movie XP transaction is created.
- Confirm the same activity advances applicable quests, achievements, franchises, statistics, and Activity entries.
- Refresh repeatedly and confirm the movie XP is not duplicated.
- Confirm a watched movie dated before activation appears only in earlier Plex history.
- Confirm an owned server that blocks an activity source reports a concise source warning while other permitted sources continue.
- Confirm a shared server does not expose live-session/history permission failures or raw Plex endpoint addresses to the user.
- Stop a movie below the completion threshold and confirm it remains in Activity as **In progress** with no XP.
- Refresh after stopping that movie and confirm the recent-history importer preserves its partial percentage instead of converting it into a completion.
- Resume that movie and confirm the existing Activity card advances instead of creating a second partial session.
- Confirm live and Activity cards show Plex artwork when available and a placeholder when artwork cannot be reached.
- Confirm the current owner-assisted title appears as **Playing now** within one foreground tracking interval and remains visible after crossing the completion threshold while playback continues.
- Confirm quest, achievement, level, franchise, and system Activity cards use RPG icons rather than movie posters.
- Confirm users cannot see another viewer's live session when Plex provides conflicting explicit account IDs.
- Confirm Search and Randomizer library behavior remains unchanged.

## Automated validation

Run:

```sh
node --test tests/*.test.js
```

The Personal Progress, Tautulli, and Cinema RPG regression tests pass in this package. The full suite reports 162 passing tests and one unrelated pre-existing Randomizer fixture, `tests/multi-decade-randomizer.test.js`, still failing at line 116 in both the supplied alpha.6.5 baseline and this package.
