diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 54f7a86e1..9ee826d92 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -298,3 +298,36 @@ jobs: echo "===== container logs (tail) ====="; docker logs "$NAME" 2>&1 | tail -n 40 || true exit 1 fi + + # Non-blocking nudge: if a PR migrates/adds a route but forgets the parity tracker, warn. + # The rule lives in CLAUDE.md → Conventions; this only surfaces an easy-to-miss omission. + # Deliberately no setup-dotnet/setup-node (and thus no actions/cache) so it can't hit the + # cache-save issues seen on the relocated runner (server-management#570). + docs-reminder: + name: Docs update reminder + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Warn when a screen/route change skips the parity doc + run: | + base_ref="${{ github.base_ref }}" + git fetch --no-tags --depth=100 origin "$base_ref" || true + changed="$(git diff --name-only "origin/${base_ref}...HEAD" 2>/dev/null || true)" + echo "Changed files in this PR:"; printf '%s\n' "$changed" + screen_or_route=no + if printf '%s\n' "$changed" | grep -Eq '^web/src/screens/.+\.tsx$|^ErsatzTV/LegacyUiRedirects\.cs$'; then + screen_or_route=yes + fi + parity=no + if printf '%s\n' "$changed" | grep -qx 'docs/blazor-route-parity.md'; then + parity=yes + fi + if [ "$screen_or_route" = yes ] && [ "$parity" = no ]; then + echo "::warning::This PR touches a SPA screen or LegacyUiRedirects.cs but does not update docs/blazor-route-parity.md. If you added/migrated/redirected a route, update the parity tracker (and docs/domain-model.md) in THIS PR — see CLAUDE.md → Conventions." + else + echo "Parity-doc reminder: nothing to flag." + fi diff --git a/CLAUDE.md b/CLAUDE.md index 792aa0cfb..715b0d500 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,7 +56,18 @@ docker build -f docker/Dockerfile -t ersatztv:dev . ## Conventions - **Read [`docs/contributing.md`](docs/contributing.md)** before non-trivial changes — it documents the established patterns (layering, CQRS handlers, LanguageExt, Blazor/MudBlazor, EF Core + dual-provider migrations, the FFmpeg pipeline, analyzers, testing) and the **deviation policy**: match the established style; diverge only with a concrete, stated reason. -- **Convention docs replace re-recon**: before API/SPA/E2E/parity work, read `docs/README.md` (index) → `docs/api-conventions.md`, `docs/spa-conventions.md`, `docs/e2e-local.md`, `docs/domain-model.md`, `docs/blazor-route-parity.md`, `docs/decisions.md`. Any PR that changes a convention, migrates a route, or reverses a decision MUST update the relevant doc in the same PR. +- **Docs-first is a HARD RULE — read before you explore**: before ANY API / SPA / E2E / parity / scheduling work, read `docs/README.md` (index) → the convention docs (`api-conventions`, `spa-conventions`, `e2e-local`, `domain-model`, `blazor-route-parity`, `decisions`). **Do NOT reverse-engineer conventions from source (Grep/Read) before reading these** — they exist precisely so you don't. Only recon the task-specific delta the docs deliberately don't freeze (a merged endpoint's exact DTO, a Blazor page's field list). **This applies to delegated subagents too**: tell each agent which doc section to read; never let one re-derive conventions from code. +- **Docs-update is part of "done" — same PR, never a follow-up**: any PR that changes a convention, adds/migrates/redirects a route, adds/changes a `/api/*` endpoint, or reverses a decision MUST update the relevant doc in that same PR: + + | Change | Update in the same PR | + |---|---| + | Migrate / add / redirect a route (new `web/src/screens/*.tsx`, `LegacyUiRedirects.cs`) | `docs/blazor-route-parity.md` + `docs/domain-model.md` | + | Add / change a `/api/*` endpoint | `docs/api-conventions.md` checklist, then regenerate `v1.json` + `endpoint-index.md` via `./scripts/update-openapi.sh` | + | Change a SPA screen convention | `docs/spa-conventions.md` | + | Establish / reverse a convention or decision | `docs/decisions.md` (append-only) + the affected doc | + | Add / remove / retitle a doc | `docs/README.md` index | + + The `docs-reminder` CI job flags a screen/route change that skips `blazor-route-parity.md`, but it's a **non-blocking** nudge — the rule is on you, not the check. - Follow existing MediatR CQRS pattern for new features - Domain logic in `ErsatzTV.Core`, infrastructure in `ErsatzTV.Infrastructure` - Keep UI thin: the SPA talks to `/api/*` only; legacy Blazor pages delegate to MediatR handlers. New screens go in the SPA (`web/`), never in Blazor diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 872528b0e..4b82391d2 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -28,7 +28,8 @@ Upstream's final release was **`v26.3.0`** (archived). Our line continues from t ## The workflow: `.gitea/workflows/docker-build.yml` -Single workflow, two jobs (`test` → `build`). +Single workflow. Gating jobs `test` + `migrations` run in parallel and gate `build`; a +non-blocking `docs-reminder` job runs on PRs only (see below). ### Triggers & tags @@ -83,6 +84,18 @@ the image build. cleanup; dumps container logs on failure. Catches routing / base-URL (#1) / migration regressions that leave the app "up" but serving broken output. +### `docs-reminder` job (non-blocking, PR-only) + +A lightweight nudge that enforces the CLAUDE.md "docs-update is part of done" rule for the +one case that's easy to forget and easy to detect: a PR that touches a SPA screen +(`web/src/screens/*.tsx`) or `ErsatzTV/LegacyUiRedirects.cs` but **does not** update +`docs/blazor-route-parity.md`. It diffs the PR against its base branch and emits a +`::warning::` annotation (never fails the build — it's a reminder, not a gate; prose-doc +gates get gamed with token edits). Deliberately has **no** `setup-dotnet`/`setup-node` (and +thus no `actions/cache`), so it can't hit the cache-save hangs seen on the VM-127 runner +(server-management#570). It does not cover the other doc obligations in the CLAUDE.md table +(api-conventions, domain-model, decisions, spa-conventions) — those stay on the author. + ## Dockerfile notes (`docker/Dockerfile`) - Base image: **`192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2`** (our Gitea fork of