Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ watch based on their region, streaming services, and preferences. External APIs
populate a local catalog; browsing and filter changes will query WatchPulse's
own data rather than calling upstream APIs.

The MVP provides region- and provider-aware discovery across five shared,
filterable sections:
The first-release UI provides region- and provider-aware discovery across four
shared, filterable sections:

- Top 10
- New Releases
- Recently Added
- Leaving Soon
- Upcoming

The evidence-only Leaving Soon API remains available, but its UI section is
deferred until expiration ingestion is enabled.

See [AGENTS.md](AGENTS.md) for the current product requirements and
[docs/architecture.md](docs/architecture.md) for the active architecture and
implementation decisions. `AGENTS.md` remains the authoritative product brief.
Expand All @@ -27,8 +29,7 @@ Documentation is split by responsibility:

## Project status

Version `v0.4` is released and `v0.5` frontend discovery is in progress. The repository currently
includes:
Version `v0.5` is the current release. The repository includes:

- configurable TMDB discovery by region and provider;
- full movie and TV metadata ingestion;
Expand All @@ -52,14 +53,17 @@ includes:
- scoped local title details with aggregated provider availability;
- a responsive React frontend with region-aware provider selection and shared
content type, genre, runtime, release-year, rating, and language filters;
- a filter-reactive Top 10 rail with reusable ranked poster cards and explicit
loading, empty, failure, and retry states;
- filter-reactive Top 10, New Releases, Recently Added, and Upcoming rails with
reusable poster cards and explicit loading, empty, failure, and retry states;
- local title search that respects the active region, providers, and filters;
- verified provider actions plus explicit outbound TMDB title-detail links;
- bounded broad and priority-based incremental TMDB enrichment planning;
- unit tests that do not make live API calls.

Version `v0.4` adds a read-only FastAPI discovery backend, typed global filters,
parameterized local queries, catalog reference/freshness routes, all five
discovery sections, and scoped title details. See the
[v0.4 release notes](docs/releases/v0.4.md).
Version `v0.5` adds the complete local guest discovery interface, catalog
search, Upcoming lifecycle presentation, TMDB/provider navigation, and
intelligent enrichment planning. See the
[v0.5 release notes](docs/releases/v0.5.md).

Add `STREAMING_AVAILABILITY_API_KEY` only when running live lifecycle ingestion;
offline tests and warehouse builds do not require it.
Expand Down
4 changes: 2 additions & 2 deletions api/tests/test_discovery_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ def test_discovery_errors_are_validated_and_sanitized(tmp_path: Path) -> None:
assert injection.status_code == 422


def test_openapi_contains_the_complete_v04_contract(catalog_app: FastAPI) -> None:
def test_openapi_contains_the_complete_v05_contract(catalog_app: FastAPI) -> None:
schema = _get(catalog_app, "/openapi.json").json()
required_paths = {
"/health",
Expand All @@ -362,4 +362,4 @@ def test_openapi_contains_the_complete_v04_contract(catalog_app: FastAPI) -> Non
}

assert required_paths <= set(schema["paths"])
assert schema["info"]["version"] == "0.4.0"
assert schema["info"]["version"] == "0.5.0"
66 changes: 66 additions & 0 deletions docs/releases/v0.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# WatchPulse v0.5 — Frontend Discovery

Released: 2026-09-01

## Outcome

WatchPulse now provides a complete local guest discovery experience. A user can
select Greece and their streaming services, apply shared filters, search the
catalog, and browse four deterministic discovery sections without triggering
TMDB or Streaming Availability requests from the browser.

## Delivered

- a responsive React 19, TypeScript, and Vite frontend;
- region-aware provider selection with browser-local guest preferences;
- shared content-type, genre, runtime, release-year, rating, and language
filters;
- filter-reactive Top 10, New Releases, Recently Added, and Upcoming rails;
- reusable title cards with posters, concise descriptions, genres, years,
ratings, lifecycle dates, and available series totals;
- local title/original-title search scoped by the active catalog filters;
- verified provider deep links when retained evidence supplies them;
- explicit TMDB navigation for richer title details;
- required TMDB attribution and JustWatch data attribution;
- targeted metadata/provider reconciliation for lifecycle-only titles;
- broad backfill and priority-based incremental TMDB enrichment planning with
dry runs, configurable caps, freshness windows, and resumable raw batches;
- source-aware catalog freshness shown in the visitor's local timezone.

## Product decisions

- WatchPulse remains a decision engine; TMDB is the canonical rich title-detail
destination for the first public release.
- Leaving Soon is not displayed until expiration evidence is ingested. No
departure date is inferred.
- Provider actions are shown only for verified HTTPS links. Missing links are
presented honestly rather than guessed.
- All browsing and filter interactions query the local serving database only.

## Validation

- 115 offline Python tests pass.
- 25 frontend tests pass with ESLint, TypeScript, and production builds.
- The warehouse build passes 222 dbt models/seeds/tests with zero errors.
- Commit and push hooks include secret detection, coverage, dbt validation,
formatting, linting, and protected-branch checks.
- A complete Netflix Greece discovery fetched all 471 TMDB pages in a bounded
discovery-only run without consuming Streaming Availability quota.

## Known limitations

- The UI currently targets Greece; the schema and API remain multi-region.
- Netflix Greece has complete discovery coverage in the retained local
development catalog. Other launch providers require equivalent full scans.
- Runtime, season/episode totals, and direct provider links remain nullable
until targeted or broad enrichment provides evidence.
- Streaming lifecycle automation remains manual. Production scheduling,
durable artifact delivery, monitoring, and deployment arrive in v0.6.
- Leaving Soon remains absent while `expiring` ingestion is disabled.
- Authentication and personalization are intentionally outside this release.

## Next version

v0.6 deploys WatchPulse privately under an owned domain with protected API
access, durable data delivery, scheduled ingestion, monitoring, and reproducible
operations.
7 changes: 6 additions & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ the [v0.4 release notes](releases/v0.4.md).

Goal: deliver the complete deterministic user experience locally.

Current status: **release candidate (2026-09-01)**. React, TypeScript, and Vite are
Current status: **complete (2026-09-01)**. React, TypeScript, and Vite are
selected in ADR-018. The first increment establishes the responsive application
shell, typed local API client, catalog readiness state, explicit development
CORS policy, frontend tests, and CI build validation. The second increment adds
Expand Down Expand Up @@ -212,6 +212,11 @@ but is intentionally excluded from v0.5 because expiration ingestion is not
enabled. The existing backend endpoint remains evidence-only and returns no
guessed departures.

Exit criteria result: passed for the accepted four-section release scope. The
guest experience works end-to-end without login, all filters query only the
local WatchPulse API, desktop/mobile layouts are validated, and title details
lead explicitly to TMDB. See the [v0.5 release notes](releases/v0.5.md).

## v0.6 — Automation and deployment

Goal: make WatchPulse reproducible, observable, and publicly accessible.
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function App() {
</a>
{state.kind === "ready" ? (
<span className="freshness"><span className="status-dot" />Updated {formatRefresh(state.status.freshness.latest_source_updated_at)}</span>
) : <span className="version">v0.5 preview</span>}
) : <span className="version">v0.5</span>}
</nav>
<div className="app-layout">
<aside className="discovery-sidebar" aria-label="Discovery controls">
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "watchpulse"
version = "0.4.0"
version = "0.5.0"
description = "Region- and provider-aware streaming discovery data product"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion warehouse/dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: watchpulse
version: 0.4.0
version: 0.5.0
config-version: 2

profile: watchpulse
Expand Down
2 changes: 1 addition & 1 deletion watchpulse/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def create_app(

app = FastAPI(
title="WatchPulse API",
version="0.4.0",
version="0.5.0",
description="Read-only discovery over the locally published WatchPulse catalog.",
)
app.state.catalog_repository = catalog_repository
Expand Down