Commit Graph
10 Commits
Author SHA1 Message Date
timothyandClaude Opus 4.8 a1b75c1f2c docs(363): sweep the scan-flow drift the re-review caught
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 8s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m12s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 18m36s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 14m18s
The fix commit softened the script's scan-409 wording + dropped ?deep=true but
left the docs describing the old behavior. Match them:
- docs/e2e-local.md: POST .../scan (no ?deep; note local scans always ForceScan)
  + "409, deterministic bar a tiny residual TOCTOU gap" (was "guaranteed 409")
- docs/ci-cd.md: same "guaranteed 409" -> "409 (deterministic bar ...)"

Docs-only; harness behavior unchanged (still 38/38 green, deterministic).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 15:44:47 +02:00
timothyandClaude Opus 4.8 4345180a56 review(363): robustness + wording fixes from cold review
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 9s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 20s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 15m47s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 19m11s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 20m48s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
- check seed_library_path succeeded (print lastrowid) so a silent seeding
  failure surfaces as a FAIL instead of degrading Flow A to an advisory skip
  with no diagnostic (Medium)
- anchor the scan-status match to '"libraryId":2,' so it can't substring-match
  "libraryId":20/23 if the suite ever creates more libraries (Low)
- drop the no-op ?deep=true (local scans always ForceScan; deep only affects
  Plex/Jellyfin/Emby) + note why (Low)
- soften "guaranteed 409" for the scan flow to note the tiny residual TOCTOU
  gap the multi-second scan covers; Flow B stays race-free by construction (Low)
- correct the "WAL tolerates a second writer" wording to the real reason (the
  busy-timeout retry serializes the writer) in the script + both docs (Nit)
- use TEST-NET-1 192.0.2.1 (RFC 5737) instead of RFC1918 10.255.255.1 for the
  non-routable Jellyfin address (Nit)

Re-verified: fresh-instance harness runs green (38/38), lock section
deterministic. Functional E2E CI job already green on the prior head.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 15:33:39 +02:00
timothyandClaude Opus 4.8 8a85f9ddb5 test(363): functional-E2E harness — add deterministic scan-lock + collections-lock 409 flows
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 5s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 10s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 11s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 28s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m24s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Failing after 3m45s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 14m16s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 19m46s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Extends scripts/e2e-functional.sh with the two IEntityLocker 409 contracts the
first cut (ersatztv#299) deferred as "racy", made DETERMINISTIC by firing the
racing request only once the lock is provably held (no sleep-and-hope):

- library-scan "already scanning" 409: seed ~60 tiny ffmpeg clips into the
  built-in Shows library so the scanner subprocess runs a few seconds, poll
  GET /libraries/scan-status until the library is active (that window is a
  strict subset of the scan lock's held window — StartScan after LockLibrary,
  EndScan before UnlockLibrary), then a second POST .../scan is a guaranteed
  409. Self-skips (advisory) when ffmpeg is absent.
- external-collections "already scanning" 409: seed a Jellyfin media-source row
  pointing at a non-routable address so the background sync hangs and the
  per-family lock stays held; the lock is taken synchronously before the 202,
  so the 202 proves it held. collections-scan-status corroborates; unknown
  source 404.

Seeding uses python3's stdlib sqlite3 (already a harness dep) to insert rows the
API can't create (LibraryPath, media-source); WAL mode tolerates the second
writer. No new CI step/dependency — ffmpeg ships in the toolchain image.
Verified: 4/4 fresh-instance runs green (38/38), lock section deterministic.

Still deferred to #363 follow-ups: the playout-build lock 409 + isLocked
projection (#215) and the UI-interactive Playwright flows.

Docs updated same PR: docs/e2e-local.md, docs/ci-cd.md, the functional-e2e
job comment in .gitea/workflows/docker-build.yml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 15:17:21 +02:00
timothyandClaude Opus 4.8 0d7803079c ci: add advisory functional-E2E curl harness (fixes #299)
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m54s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 17s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 18s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 4m17s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m42s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m22s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m38s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 6m2s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m41s
Codify the manual live-E2E curl flows sessions have been re-running by hand
into a CI regression net: a new `functional-e2e` job boots the app from source
(via scripts/e2e-local.sh, parameterized for Release) and drives scripts/
e2e-functional.sh against it.

First-cut contracts (all curl-only, deterministic, no seeded media/ffmpeg/browser):
- legacy->SPA redirect sweep + the /api,/artwork never-redirect exemption
- auth/CSRF/security-stamp flow (setup-claim, read-gate, CSRF, login, logout+revoke)
- library-scan status contract (404/202/scan-status)
- optimistic-concurrency If-Match/412 round-trip

Advisory by design (separate job, not a `build` dependency, not a required
check) so a functional-E2E flake can't block the unit-test gate; promote once
proven, mirroring the migrations-job rollout. SQLite default -> no DB service.

Deferred to #299 follow-ups (need scanner+seeded media or a browser to be
deterministic): the racy 409 re-trigger, playout-build lock 409, Playwright UI
flows.

Assertions verified 30/30 green against a real Release-built instance; caught
/artwork/* returning 400 (not the 404 a static read suggested).

Docs updated same PR: docs/ci-cd.md (new job), docs/e2e-local.md (harness),
docs/decisions.md (append-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 13:49:33 +02:00
timothyandClaude Opus 4.8 ef2bd65c27 feat(api): #286 — mount the whole /api surface at /api/v1
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 10s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m12s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 3m4s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m17s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m36s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Version every /api route to /api/v1 (251 controller routes + ~24 Location
headers + the scanner callback URL + the Startup request-log literal),
uniform across the machine API, auth, scanner and scripted-build surfaces.

Add ApiVersionRewriteMiddleware: a legacy unversioned /api/* request is
rewritten (NOT redirected) to /api/v1/* in-pipeline — method, body, auth
headers and query survive — carrying RFC 8594 Deprecation/Sunset headers,
so curl / the future MCP server / bookmarks keep working. An already-
versioned path passes through; a future /api/v2 is never forced to v1.

Standardize the route convention (leading-slash absolute route per method,
no class-[Route] — except the two Scanner/Scripted controllers whose ~all
actions share a parametrized {id} prefix), enforced by ApiRouteVersioningTests
(^/api/v\d+/ over the whole Controllers.Api surface; browser-nav
/auth/oidc/login is out of scope).

Regenerate v1.json (160 paths, all /api/v1)/endpoint-index/v1.d.ts; sweep 945
SPA request literals + the test mocks (regex + positional URL parsers). /api/v1
is additive-only after freeze; the legacy-rewrite shim sunsets in ~2 releases
(owner decision) with removal tracked as a Phase-3 follow-up.

Docs: decisions.md 2026-07-13, api-conventions §1/§9, rest-api/spa-conventions/
blazor-route-parity/e2e-local/domain-model.

fixes #286
refs #197

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 00:30:20 +02:00
timothyandClaude Opus 4.8 461c763dc6 docs: #295 PR2 + #301 — decisions entry, api-conventions §9, e2e-local browser flow
- decisions.md: new entry (SPA cookie-only cutover, boot-gate-not-route, #301
  POST-ification rationale, machine-key-read + OIDC-logout residual) + TOC line.
- api-conventions §9: #301 resolved (POST-ify) + 'never add a side-effecting GET'
  standing rule; machine-key endpoint added to the auth surface list; PR2-shipped note.
- e2e-local: fix stale 'no key required' claim (fail-closed since #197) + browser
  setup/login boot-gate flow.
(spa-conventions §5e rewrite landed with the SPA-consumers slice.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 21:50:42 +02:00
timothyandClaude Opus 4.8 b20ee50b1f docs(process): #303 follow-ups — Codex-skip rubric + write-path live-E2E requirement
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 8s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m33s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Two of the four non-hook #303 process follow-ups (the two docs items; the
security scan and migration-on-prod-copy smoke are deferred to their own
sessions):

1. Codex-skip rubric (kickoff workflow lore): an independent review pass is
   MANDATORY for diffs touching locks/concurrency, auth/security, API
   write-path handlers, or migrations, or >~150 changed C# lines; skippable
   only for a pure-SPA/docs leaf, and a skip must be stated + justified. Makes
   self-exemption an auditable claim (the correlated-blindspot net).

2. Live-E2E is now a STATED REQUIREMENT for API write-path handler changes:
   new "When live-E2E is required" section in docs/e2e-local.md + a decisions.md
   entry, formalizing the #229 lore bullet. The seeding recipe was already in
   e2e-local.md (added for #220), so the stale "recipe not yet in docs" lore
   bullet is pruned to a pointer.

Docs-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:02:36 +02:00
timothyandClaude Fable 5 0156077e18 docs(e2e): add local TV library seeding recipe (#220)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m17s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Failing after 3s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Document the on-disk media + direct-SQLite LibraryPath + scan recipe for
E2E, since a local library is not API-seedable. Capture two gotchas hit
while verifying the episode-nav PR: deleting search-index/ leaves search
permanently empty (migration doesn't reindex from DB; rescan skips
unchanged files), and /api/search needs field/wildcard queries
(title:Alpha), not bare title words.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:52:29 +02:00
timothy 279052ccd4 fix(e2e): stale-asset gotcha — rm build wwwroot before copy in e2e-local.sh (+doc)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Failing after 2m45s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Failing after 2s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
2026-07-10 07:56:34 +02:00
timothyandClaude Fable 5 50ae0a7f3b docs: onboarding/convention docs part 1 + handoff past #180/#182/#183 (parity endgame)
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m53s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m56s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m52s
Adds docs/README.md (index), api-conventions.md, spa-conventions.md, e2e-local.md +
scripts/e2e-local.sh, blazor-route-parity.md (#91 phase-b tracker), domain-model.md,
decisions.md. Rule (CLAUDE.md): read these at session start instead of re-recon; update
in the same PR that changes a convention/route/decision. Part 2 = #185.

Handoff: #180/#141/#158/#161 closed (PRs #181/#182/#183); #145 playback-only; #91
readiness plan posted; next prompt = #185 + quick wins + #155/#151/#152/#153.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:03:01 +02:00