Commit Graph

2379 Commits

Author SHA1 Message Date
Marcel
9f1b8b4215 fix(enrichment-block): migrate $app/stores → $app/state to eliminate birpc race
The async vi.mock factory in EnrichmentBlock.svelte.spec.ts performed an
`await import(...)` in its body — the same mechanism #535/#546 fixed for
pdfjs-dist. Issue #553: when Chromium's playwright route handler fetches
the mocked module after the worker's birpc channel has closed, the
factory's RPC roundtrip raises `[birpc] rpc is closed, cannot call
"resolveManualMock"` and the run exits 1.

Migrate EnrichmentBlock from the deprecated `$app/stores.navigating`
(store) to the modern `$app/state.navigating` (reactive proxy). The
spec uses vi.hoisted + a sync vi.mock factory with a getter that defers
the read — no dynamic import in the factory body. Delete the now-unused
__mocks__/navigatingStore.ts.

Fix path applied: $app/state migration (Markus's recommendation /
Felix's Path 2). See ADR-012.

Refs #553

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:00:44 +02:00
Marcel
89860403f6 fix(notification): remove role=link from view-all button — restores semantically honest button role
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m50s
CI / OCR Service Tests (pull_request) Successful in 18s
CI / Backend Unit Tests (pull_request) Successful in 4m12s
CI / fail2ban Regex (pull_request) Successful in 38s
CI / Compose Bucket Idempotency (pull_request) Failing after 10s
CI / Unit & Component Tests (push) Failing after 2m5s
CI / OCR Service Tests (push) Successful in 17s
CI / Backend Unit Tests (push) Successful in 4m14s
CI / fail2ban Regex (push) Successful in 39s
CI / Compose Bucket Idempotency (push) Failing after 12s
nightly / deploy-staging (push) Failing after 2m36s
The role=link override on a <button> creates a WCAG 4.1.2 keyboard-contract
mismatch: ARIA role=link tells AT users "press Enter to activate (Space does
nothing)", but the native <button> responds to both Enter and Space. Removes
the override so the element is announced as "button" (accurate).

Test selectors updated from getByRole('link') to getByRole('button')
accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 18:01:38 +02:00
Marcel
6b78557954 refactor(notification-tests): use vi.mocked instead of type cast in call-order test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 17:50:55 +02:00
Marcel
bc2dd3a98a fix(notification): add role=link and touch target to view-all button
Some checks failed
CI / Backend Unit Tests (push) Successful in 4m15s
CI / fail2ban Regex (push) Successful in 39s
CI / Compose Bucket Idempotency (push) Failing after 11s
CI / OCR Service Tests (pull_request) Successful in 17s
CI / Backend Unit Tests (pull_request) Successful in 4m17s
CI / Unit & Component Tests (push) Failing after 1m48s
CI / OCR Service Tests (push) Successful in 17s
CI / Unit & Component Tests (pull_request) Failing after 2m3s
CI / fail2ban Regex (pull_request) Successful in 40s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
- role="link" restores screen reader link semantics (Leonie blocker)
- min-h-[44px] px-1 meets WCAG 2.2 §2.5.8 and our 44×48px target size
- Comment in handleViewAll explains close-before-navigate ordering
- Tests updated to getByRole('link') + new call-order assertion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 17:43:11 +02:00
Marcel
3005782a75 docs(adr-012): correct pattern note to document button+goto, not anchor+preventDefault
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 17:43:11 +02:00
Marcel
8ccc9aba1a fix(notification): replace view-all anchor with button to prevent iframe navigation
SvelteKit's capture-phase link interceptor fires before the component's
onclick handler, so e.preventDefault() was structurally too late to stop
iframe navigation in vitest-browser. Replacing the <a href> with a
<button type="button"> removes the href entirely — the interceptor never
fires — and the existing goto() mock in tests is sufficient.

Also splits the single view-all test into two focused it() blocks and
clears mocks in afterEach to prevent cross-test mock leakage.

Fixes #551

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 17:43:11 +02:00
Marcel
d21ba8fed2 refactor(pdf-viewer-tests): extract shared fake, add loadDocument error path, fix assertions
Some checks failed
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / OCR Service Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / fail2ban Regex (pull_request) Has been cancelled
CI / Compose Bucket Idempotency (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Failing after 2m3s
CI / OCR Service Tests (push) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m15s
CI / fail2ban Regex (push) Successful in 38s
CI / Compose Bucket Idempotency (push) Failing after 11s
- Extract makeFakePdfjsLib / makeFakeLibLoader to testHelpers.ts — single
  source of truth used by both PdfViewer.svelte.test.ts and
  usePdfRenderer.svelte.test.ts; removes the diverging-fidelity DRY violation
  flagged by @felixbrandt and @saraholt in the PR review
- Add 'loadDocument sets error and loading=false when getDocument().promise
  rejects' test to usePdfRenderer.svelte.test.ts — closes the error-path gap
  flagged by @felixbrandt and @saraholt
- Replace toBeInTheDocument() with toBeVisible() in the three absorbed
  spec-file tests — uniform assertion style across the loaded-state describe
  block, as flagged by @felixbrandt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 16:19:26 +02:00
Marcel
23cbb6be22 test(pdf-renderer): eliminate real pdfjs-dist loading from browser tests — use fake libLoader for all init() calls
Five tests in usePdfRenderer.svelte.test.ts called createPdfRenderer() without
a libLoader, causing init() to dynamically import pdfjs-dist in the browser.
Every dynamic import goes through Playwright's route handler, which calls
resolveManualMock via birpc to check for mocks. If the RPC closes during
teardown while one of these imports is in flight, the birpc race fires —
even though pdfjs-dist was never explicitly vi.mock()-ed.

Replace all bare createPdfRenderer() calls that invoke init() with
createPdfRenderer(makeFakeLibLoader()), identical to the pattern already
used in PdfViewer.svelte.test.ts. No real module loads, no route-handler
calls, no birpc exposure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 16:19:26 +02:00
Marcel
9260866f47 ci(unit-tests): add early grep check for banned vi.mock pdfjs-dist pattern
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m47s
CI / OCR Service Tests (push) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m11s
CI / fail2ban Regex (push) Successful in 38s
CI / Compose Bucket Idempotency (push) Failing after 11s
Adds a static grep step that runs after Lint and before the test suite.
Fails in ~1 s if any file under frontend/src/ contains the banned
vi.mock('pdfjs-dist' pattern, catching the regression before Playwright
spins up. Belt-and-suspenders with the ESLint rule (ADR 012).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:32:23 +02:00
Marcel
7c8811e439 refactor(eslint): ban vi.mock('pdfjs-dist', ...) in spec/test files — ADR 012
Adds a no-restricted-syntax rule scoped to *.spec.ts / *.test.ts that
flags any vi.mock call whose first argument starts with 'pdfjs-dist'.
Turns the ~2-min CI wait into an immediate lint error on save.
Updates ADR 012 Enforcement section to document the rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:32:23 +02:00
Marcel
ef592ddd0c test(pdf-viewer): consolidate spec.ts into test.ts — absorb page-counter test, delete spec
Absorbs the three tests from PdfViewer.svelte.spec.ts (nav buttons, zoom
controls, page counter) into the loaded-state describe in test.ts, then
deletes the now-empty spec file. One spec file per component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:32:23 +02:00
Marcel
6c596babcb test(pdf-viewer): port PdfViewer.svelte.test.ts to libLoader prop injection — remove vi.mock
Removes both vi.mock('pdfjs-dist', …) calls that caused the birpc teardown
race (ADR 012). Replaces with static import + makeFakeLibLoader() helper
injected via the libLoader prop on every render() call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:32:23 +02:00
Marcel
763e9f5708 docs(adr-012): add overlay navigation pattern note
Some checks failed
CI / Backend Unit Tests (push) Successful in 4m10s
CI / fail2ban Regex (push) Successful in 38s
CI / Compose Bucket Idempotency (push) Failing after 11s
CI / Unit & Component Tests (push) Failing after 1m50s
CI / OCR Service Tests (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
37026bbbb8 refactor(notification): extract handleViewAll named function
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
53ecfee25e test(notification): assert href is preserved on view-all link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
fa4f8ed661 fix(style): move transkription print styles to global CSS to suppress Tailwind noise
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
890b811bc1 fix(style): move ChronikFuerDichBox animation to global CSS to suppress Tailwind noise
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
ed91c9bcf6 fix(notification): prevent iframe navigation — use goto instead of href follow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
661e8582a2 test(notification): add goto mock and tighten selector in NotificationDropdown spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:35:40 +02:00
Marcel
7ee038faaf test(ocr): fix track_reactivity_loss in OcrTrainingCard spec
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m50s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (pull_request) Successful in 4m11s
CI / fail2ban Regex (pull_request) Successful in 39s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
CI / Unit & Component Tests (push) Failing after 1m50s
CI / OCR Service Tests (push) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m7s
CI / fail2ban Regex (push) Successful in 37s
CI / Compose Bucket Idempotency (push) Failing after 10s
Two root causes:

1. In-flight test: resolveFetch() was the last line, leaving the async
   finally-block writing `training = false` after cleanup destroyed the
   component. Awaiting the button becoming re-enabled ensures the finally
   block settles before cleanup runs.

2. Success-dismiss test: startTraining() schedules setTimeout(5000) which
   fired after cleanup destroyed the component. vi.useFakeTimers() +
   vi.runAllTimers() scoped to the describe block drains the timer while
   the component is still alive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:22:03 +02:00
Marcel
ae1688319e test(annotation): replace synchronous query().toBeNull() with async not.toBeInTheDocument()
Svelte defers DOM updates to microtasks; .query() is a synchronous
snapshot that can fire before the element disappears — making the
absence assertions in AnnotationShape and AnnotationLayer non-deterministic.

Sweeps all 4 instances across both spec files (Sara's ≤5 threshold).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:21:24 +02:00
Marcel
7f07180c71 docs(adr-012): add enforcement note and libLoader revisit signal
Some checks failed
CI / OCR Service Tests (push) Successful in 15s
CI / Unit & Component Tests (push) Failing after 2m4s
CI / Backend Unit Tests (push) Successful in 4m6s
CI / fail2ban Regex (push) Successful in 38s
CI / Compose Bucket Idempotency (push) Failing after 10s
- Explicitly states no lint rule is planned; CI guard is the backstop
  (addresses Elicit OQ-001 from PR #536 round 4)
- Adds a "when to revisit" note: extract shared DynamicImportLoader<T>
  if 3+ components adopt the libLoader pattern
  (addresses Markus Keller round-4 observation on PR #536)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
1ead1f293f ci(coverage): document that birpc guard covers coverage run only
Adds a comment above the assertion step so a future developer diagnosing
a birpc-related failure in `npm test` knows where to find the diagnostic.

Addresses Sara Holt + Tobias Wendt round-4 observation on PR #536.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
a693f07eca refactor(test): convert TextLayerMock to class syntax in PdfViewer spec
Prototype-style assignment was a vi.mock hoisting artifact from the old
version of the file. Rest of the codebase uses class syntax — aligning.

Addresses Felix Brandt round-4 suggestion on PR #536.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
3ae7c9da0c test(pdf-renderer): guard init() against repeated calls — libLoader must fire once
Adds idempotency test: calling init() twice must invoke libLoader only once.
Adds `if (pdfjsReady) return;` guard to satisfy the contract.

Addresses Felix Brandt round-4 suggestion on PR #536.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
729f5c66d6 ci(coverage): use grep -F for birpc guard to avoid BRE escaping
-F (fixed string) matches the literal pattern [birpc] rpc is closed
without relying on BRE bracket escaping, making the intent explicit
and immune to accidental regex interpretation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
d40f477397 ci(coverage): include coverage log in artifact upload
The birpc guard step writes to /tmp/coverage-test-<run_id>.log and exits 1
when a race is detected. Without this file in the artifact, the evidence
disappears when the runner tears down — only the exit code remained visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
f126634804 refactor(test): remove what-comment from makeFakePdfjsLib
The name already implies it's a fake; the comment described what, not why.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
bdadff787c test(pdf-renderer): assert init() re-throws when libLoader rejects
.catch(()=>{}) swallowed the rejection, so the test passed vacuously even
if a future refactor silently caught the error. rejects.toThrow() proves
the propagation contract holds before asserting pdfjsReady stays false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
cf78957476 ci(coverage): harden coverage guard step
- Add explicit set -eo pipefail so npm test:coverage exit code
  propagates through the pipe (not just tee's always-0 exit)
- Scope log file to github.run_id to prevent stale-log false positives
  on retried steps sharing the same runner /tmp
- Tighten grep pattern to \[birpc\] rpc is closed to avoid matching
  unrelated log lines that happen to contain "rpc is closed"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
f8dad85020 test(pdf-renderer): document libLoader rejection leaves pdfjsReady false
Regression-protection test: init() propagates the loader rejection
before pdfjsReady is set, so the renderer stays in a safe unready state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
5cd330de74 docs(pdf-viewer): comment untrack invariant on renderer init
Without untrack, a reactive libLoader prop reference change would
reinitialise the whole renderer and lose all loaded state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
06b158bf54 refactor(pdf-viewer): export LibLoader type and update callers
Exporting LibLoader gives the type a stable, named identity.
PdfViewer.svelte and PdfViewer.svelte.spec.ts now import it directly
instead of using Parameters<typeof createPdfRenderer>[0].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
3594204214 ci(coverage): simplify coverage step and pin shell to bash
- removes unreachable `; exit ${PIPESTATUS[0]}` — already covered by pipefail (Tobias)
- adds explicit `shell: bash` to both new steps for clarity (Tobias)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
073b6cb45d refactor(test): move PdfViewer import to top and annotate partial fake cast
- import PdfViewer left mid-file from vi.mock hoisting — no longer needed (Sara/Felix)
- adds one-line comment explaining as unknown as cast is an intentional partial fake (Felix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
a7e0a66355 docs(adr): 012 — browser-mode test mocking strategy
Documents why vi.mock(module, factory) races with birpc teardown for
dynamically-imported modules, the libLoader injection pattern used to fix
#535, and the residual exceptions ($app/*, $env/*) that are safe to keep
as vi.mock because they are resolved statically before any test runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
538adb43a9 ci(guard): fail unit-tests job if [birpc] rpc is closed appears in coverage run
Captures npm run test:coverage output with tee and adds an always-run step
that greps for the teardown-race fingerprint. Any future regression where a
vi.mock factory races with birpc teardown will now surface as an explicit CI
failure rather than a silent exit-1 after all tests report green (#535).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
115476453a fix(pdf-viewer): replace vi.mock(pdfjs-dist) with injected libLoader prop
Removes both vi.mock('pdfjs-dist', factory) and
vi.mock('pdfjs-dist/build/pdf.worker.min.mjs?url', factory) from
PdfViewer.svelte.spec.ts — the ManualMockedModule registrations that were
racing with vitest-browser-playwright's birpc teardown channel.

PdfViewer.svelte now accepts an optional libLoader prop (typed as
Parameters<typeof createPdfRenderer>[0]) that is passed untracked to
createPdfRenderer(). Tests supply a vi.fn() fake loader directly as a prop;
production code uses the default loader that imports the real pdfjs-dist.
The birpc route handler for pdfjs-dist is never registered, so no teardown
race is possible. Fixes #535.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
Marcel
817ec44439 test(pdf-renderer): inject libLoader into createPdfRenderer to eliminate vi.mock factories
Adds an optional LibLoader parameter (defaults to the real pdfjs-dist dynamic
imports) and a failing test that verified the loader is called during init().
This is the first step toward removing ManualMockedModule registrations that
race with vitest-browser-playwright's birpc teardown (#535).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:57:28 +02:00
51e2d50dd0 Merge pull request 'fix(ci): replace iproute2 ip with /proc/net/route for gateway detection' (#544) from fix/nightly-caddy-reload into main
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Compose Bucket Idempotency (push) Has been cancelled
2026-05-12 09:57:02 +02:00
Marcel
9c26c00eee fix(ci): replace iproute2 ip with /proc/net/route for gateway detection
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Compose Bucket Idempotency (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / OCR Service Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / fail2ban Regex (pull_request) Has been cancelled
CI / Compose Bucket Idempotency (pull_request) Has been cancelled
`ip route` (iproute2) is not installed in the Gitea runner container,
causing the smoke test step to exit 127. /proc/net/route is a kernel
virtual file that is always present on Linux; awk decodes the
little-endian hex gateway field to dotted-decimal without any external
binary dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:50:56 +02:00
Marcel
6d16be4669 fix(ci): quote \$RESOLVE in all curl calls
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m51s
CI / OCR Service Tests (pull_request) Successful in 18s
CI / Backend Unit Tests (pull_request) Successful in 4m1s
CI / fail2ban Regex (pull_request) Successful in 38s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
CI / Unit & Component Tests (push) Failing after 1m51s
CI / OCR Service Tests (push) Successful in 18s
CI / Backend Unit Tests (push) Successful in 4m10s
CI / fail2ban Regex (push) Successful in 38s
CI / Compose Bucket Idempotency (push) Failing after 10s
Unquoted variable expansion is safe here since the value contains
no spaces or glob characters, but quoting is the correct default
and keeps the script consistent with surrounding style.

Addresses review suggestion by Felix Brandt and Tobias Wendt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:26:35 +02:00
Marcel
f1032865f3 fix(ci): guard against empty HOST_IP in smoke test
If `ip route show default` returns no output the old code passed
an empty string to curl --resolve, producing a confusing error 6
("couldn't resolve host") with no indication that gateway detection
had failed.  The new guard exits immediately with a clear message.

Addresses review concern raised by Tobias Wendt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:26:35 +02:00
Marcel
3056311c24 fix(ci): resolve smoke test host via bridge gateway, not 127.0.0.1
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m50s
CI / OCR Service Tests (pull_request) Successful in 17s
CI / Backend Unit Tests (pull_request) Successful in 4m8s
CI / fail2ban Regex (pull_request) Successful in 38s
CI / Compose Bucket Idempotency (pull_request) Failing after 10s
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Unit & Component Tests (push) Has started running
CI / Compose Bucket Idempotency (push) Has been cancelled
Job containers run in bridge network mode (runner-config.yaml). Inside
a bridge-networked container 127.0.0.1 is the container's own loopback;
Caddy on the host is unreachable there, causing an immediate ECONNREFUSED.

Use the Docker bridge gateway IP instead — the host's docker0 interface
where Caddy (bound on 0.0.0.0:443) is reachable from the container.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:10:17 +02:00
Marcel
e9caa3a1f7 chore(renovate): require manual review for privileged CI image digest bumps
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m46s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (pull_request) Successful in 4m8s
CI / fail2ban Regex (pull_request) Successful in 38s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
CI / OCR Service Tests (push) Successful in 16s
CI / Unit & Component Tests (push) Failing after 1m52s
CI / Backend Unit Tests (push) Successful in 4m11s
CI / fail2ban Regex (push) Successful in 39s
CI / Compose Bucket Idempotency (push) Failing after 10s
Adds a packageRule matching .gitea/workflows/** digest updates with
automerge: false. Digest bumps for images running --privileged --pid=host
have root-equivalent host access and must not be auto-merged.

Addresses Nora's review concern on #537.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00
Marcel
58922bee53 docs(ci): add Troubleshooting section for Reload Caddy failures
Covers the three failure modes Sara flagged: Caddy stopped (explicit
systemctl error), symlink missing/mis-pointed (silent reload, stale
smoke test), and Docker socket / nsenter unavailable (container error).
Each failure mode includes symptoms and recovery steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00
Marcel
bbdf1c3e67 docs(adr): ADR-012 — nsenter via privileged container for host service management in CI
Captures the architectural decision, alternatives considered (sudo
systemctl, Caddy admin API, SSH), and consequences (symlink contract,
Renovate review requirement, step duplication tracked in #539).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00
Marcel
8536b2ebbd docs(deploy): note Caddyfile symlink is a CI dependency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00
Marcel
4bb988824f docs(ci): update nsenter example to Alpine, document alternatives considered
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00
Marcel
544b96bc9e fix(ci): pin Reload Caddy to alpine:3.21 digest, add reload-vs-restart rationale
- Switch ubuntu:22.04 (floating, ~70 MB) to alpine:3.21 pinned by sha256
  digest (~5 MB); util-linux installed at run time via apk add
- Add explicit comment explaining why `reload` not `restart`: SIGHUP
  re-reads config in-process without dropping TLS connections

Addresses Tobias + Nora blocker from PR review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:42:28 +02:00