Skip to content

fix(requests): same processing state in list and detail, retries picked up on page view - #73

Merged
Fluory merged 6 commits into
mainfrom
claude/fix-processing-state-70
Sep 27, 2026
Merged

Fluory merged 6 commits into
mainfrom
claude/fix-processing-state-70

Conversation

@Fluory

@Fluory Fluory commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Warum

Fixes #70 · gestapelt auf #72 (#69) und #68

Arbeitsstand

  • Ziel: Liste und Detailansicht zeigen denselben, verständlichen Verarbeitungszustand, und eine fällige Wiederholung läuft im Showcase durch normale Nutzung an, nicht erst durch den täglichen Cron.
  • Nicht-Ziele: neue Queue-Einstellungen, ein „Jetzt wiederholen“ für noch nicht fällige Jobs.
  • Erledigt: Hinweis der Detailseite aus derselben Zeilenansicht wie die Liste; Nachlauf beim Seitenaufruf mit Drossel und In-flight-Sperre; Tests für Hinweis, Sperre und Verdrahtung; Architekturkarte und CHANGELOG; Befunde des frischen Reviews eingearbeitet.
  • Offen: Deploy der Web-App aus diesem Stand (Freigabe des Orchestrators) und Sichtprüfung des Hinweises im Wiederholungszustand.
  • Annahmen: Ein Nachlauf pro 30 s und Instanz, nie zwei gleichzeitig, genügt als Kostenbremse.
  • Nächster kleinster Schritt: Nach Freigabe die Web-App deployen und prüfen, dass ein Seitenaufruf genau einen jobs.drain_run erzeugt.

Was ist passiert (Klartext)

In der Demo stand in der Liste „Der KI-Dienst ist nicht erreichbar“, in der Detailansicht derselben Anfrage aber nur „Die Dokumente werden gerade ausgewertet“. Wer die Anfrage öffnete, sah also keinen Fehler. Außerdem blieb sie liegen, bis einmal am Tag der Nachlauf kam. Jetzt baut die Detailansicht ihren Hinweis aus genau denselben Angaben wie die Liste: letzter Fehler, bisherige Versuche und der nächste Versuch. Ist dessen Zeit schon vorbei, steht dort „fällig seit …“. Ist kein Versuch mehr geplant, verspricht die Seite auch keinen. Zusätzlich startet das Öffnen der Liste oder einer Anfrage im Showcase einen Nachlauf, höchstens alle 30 Sekunden und nie, solange der vorige noch läuft. Fällige Wiederholungen laufen dadurch einfach bei normaler Nutzung an. Lokal und in einer späteren Produktion mit eigenem Worker ändert sich nichts.

Plan-Pflicht (SYSTEM.md §4)

  • Kein Auslöser – keine Modulgrenze, öffentliche API, Migration, Auth/Rechte, kein Zahlungs-/Daten-/Infrapfad, höchstens zwei Module, keine Architekturvarianten, umkehrbar
  • Auslöser zutreffend – Impact Manifest ausgefüllt (Plan vor Code)

Impact Manifest

  • Betroffene Module: App-Schicht src/app/_server (Nachlauf-Auslöser) und src/app/requests (Liste, Detailseite, Hinweis); features/requests verliert den zuvor hinzugefügten Export wieder. Keine Fachlogik der Queue geändert.
  • Schnittstellen / Datenänderungen: keine Schema-, API- oder Queue-Änderung. Neu ist ein Auslöser für die bestehende Drain-Runde im Showcase (Seitenaufruf) – ein zusätzlicher Queue- und Kostenpfad, deshalb dieser Abschnitt (Hinweis aus dem frischen Review).
  • Akzeptanzkriterien: die aus fix(requests): consistent processing state and pick-up of due retries on the showcase #70: gleiche Fakten in Liste und Detail; fällige Wiederholung durch normale Nutzung; nur mit JOB_DRAIN_INLINE=true; keine Drain-Flut.
  • Testplan: Unit – Hinweis aus der Zeilenansicht (6 Fälle), createRunGate (4), drainOnPageView an der after()-Grenze (4); Integration – die aufgerufene Drain-Runde ist dieselbe wie bei Upload/Freigabe/Cron und in tests/integration/drain.test.ts gegen echtes Postgres/pg-boss geprüft. Abweichung vom Testplan in fix(requests): consistent processing state and pick-up of due retries on the showcase #70 siehe Nachweis.
  • Verifizierte Fakten: after() läuft nach dem Senden der Antwort (bestehender Test scheduleAfterResponse); die Seiten haben maxDuration = 300; pg-boss hält Jobs exactly-once, auch bei parallelen Drains (bestehender Integrationstest „serverless drain and worker race“).
  • Offene Annahmen: eine Instanz pro Seitenfunktion reicht bei Demo-Last; bei mehreren Instanzen läuft höchstens ein Nachlauf je Instanz.
  • Nicht-Ziele: globale Sperre über Instanzen, „Jetzt wiederholen“-Knopf.
  • Risiken und Rollback: mehr Modellaufrufe bei vielen Seitenaufrufen – begrenzt durch Sperre, Drossel und das Upload-Limit; Abschalten: JOB_DRAIN_INLINE=false (Runbook §9); Code per Revert-PR.

Geändert

Nachweis (SYSTEM.md §11)

  • verify:changed: grün – vitest src/app/_server src/app/requests 33/33; zuerst rot: createRunGate is not a function, fehlendes Hinweis-Modul, zweiter Drain während laufendem erstem
  • verify: lokal grün bis auf den bekannten Windows-Fall – lint 0 Fehler (1 Warnung in einer git-ignorierten Plugin-Datei), typecheck, depcruise (keine Verletzungen), build grün; Unit 206/212 – die 6 Fehlschläge sind ausschließlich tests/architecture/dependency-rules.test.ts unter Windows (fix(tests): architecture test cannot start dependency-cruiser on Windows #78, Ursache belegt), in der Linux-CI grün; Integration 134/134 gegen Postgres + S3 (Docker)
  • verify:full / E2E-Spec: nicht betroffen – die Smoke-Specs prüfen keine Verarbeitungstexte
  • Abweichung vom Testplan in fix(requests): consistent processing state and pick-up of due retries on the showcase #70: kein eigener Integrationstest für „fälliger Job nach Seitenaufruf“ und „Detailseite im Wiederholungszustand“. Begründung: Der Seitenaufruf startet dieselbe Drain-Runde wie Upload, Freigabe und Cron; diese Runde ist gegen echte Systeme getestet. Neu ist nur die Verdrahtung (Schalter, Sperre, after()), und die ist an der I/O-Grenze unit-getestet. Der Hinweis ist eine reine Funktion der Zeilenansicht und damit vollständig unit-testbar; eine Seiten-Integration bräuchte einen Server-Component-Renderer, den das Projekt nicht hat.
  • Manueller Prüfnachweis: Vorgängerstand live – jobs.drain_run bei GET /requests und bei der Detailseite (2026-09-27). Sichtprüfung des Hinweises im Wiederholungszustand folgt nach dem Deploy.
  • Frischer Review (P1 vor Ready-for-review; Architektur/API/DB immer): fix(requests): same processing state in list and detail, retries picked up on page view #73 (comment) – changes requested; alle 7 Befunde bearbeitet; der rote Vercel-Check requestflow-ai war ein von mir abgebrochener Preview-Build ohne Bezug zu diesem Diff

Doku-Entscheidung (genau eine)

Entferntes oder Umbenanntes: docs/ + README gegrept, Treffer bereinigt: processingNotice aus features/requests entfernt – keine Referenz in docs/ oder README; createThrottle durch createRunGate ersetzt – nur in Code und Tests referenziert

Dateigrößen und neue Bausteine (SYSTEM.md §7)

Dateien über 500 Zeilen im Diff (Ausnahmen: generierter Code, Lockfiles, Fixtures, Migrationen, Schemas, Ressourcen, Doku, Konfiguration):

  • keine
  • bewusst belassen – Begründung: –
  • im selben PR nach fachlicher Verantwortung geteilt
  • Folge-Issue –

Über 800 Zeilen mit neuer Fachlogik oder über 1000 Zeilen (P1/P2): nicht betroffen

Neue Shared-Komponente, Utility-Datei, Adapter oder fachlicher Service:

  • nein
  • ja – gesucht nach: bestehender Ableitung des Verarbeitungszustands (errorMessage, nextRetryAt, attempts in src/app und src/features) und nach Drossel-/Sperr-Helfern (throttle, debounce, lock, inflight); gefunden: requestRowView in src/app/requests/row-view.ts – der Hinweis nutzt sie jetzt statt eigener Ableitung; kein vorhandener Drossel- oder Sperr-Helfer außer pg-boss (Job-Ebene, nicht Seitenaufruf-Ebene)

Subagent-Einsätze

  • Frischer Review durch einen unabhängigen Reviewer-Agenten (nur lesend, ohne Umsetzungskontext); Ergebnis unverändert als Kommentar gepostet.

Risiken / offene Punkte


🤖 Generated with Claude Code

Fluory and others added 2 commits September 27, 2026 02:30
…picked up on page view

Symptom: the showcase list said "Der KI-Dienst ist nicht erreichbar" while
the detail page only said "Die Dokumente werden gerade ausgewertet", and
the request stayed there until the daily cron.
Cause: the detail page rendered a fixed text for PROCESSING and ignored
the failure the worker records (errorMessage, attempts, nextRetryAt); on
Vercel Hobby only user actions or the daily cron drain the queue.
Fix: processingNotice() gives the detail page the list's facts; opening
the list or a request drains once after the response when
JOB_DRAIN_INLINE=true, throttled to one run per 30 s and instance.

Fixes #70

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Refs #70

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
requestflow Ready Ready Preview Sep 27, 2026 10:50am UTC
requestflow-ai Ready Ready Preview Sep 27, 2026 10:50am UTC

@Fluory

Fluory commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Frischer Review (/review-pr) – unabhängiger Reviewer-Agent ohne Umsetzungskontext, 2026-09-27

Review PR #73 (nur eigener Diff, Basis #72)

Geprüft und in Ordnung: Die Fokus-Tests laufen lokal grün (21/21). In der CI sind check und compose-smoke grün. Auf beiden Seiten kommt die Auth-Prüfung vor dem Drain (src/app/requests/page.tsx:44-46, [id]/page.tsx:33-39). Der Drain startet nur mit drainInline und läuft über after(), also nach der Antwort. maxDuration = 300 ist gesetzt. Der Kontrast der Hinweise liegt bei 8,8:1 bzw. 10,7:1. Die Zeitzone ist wie in der Liste Europe/Berlin, die Einzahl/Mehrzahl stimmt, und der Klartext ist verständlich.

[Important] – CHANGELOG.md:93-94 – Der #69-Eintrag gehört zu PR #72. Dessen Diff ändert nur 3 Dateien unter services/ai und keinen CHANGELOG. – Wird #72 allein gemergt, landet der Fix ohne Changelog, und dieser PR dokumentiert eine fremde Änderung. – Den Eintrag nach #72 verschieben, hier nur #70 behalten.

[Important] – docs/technical/architecture.md:52, :67 – Das Ausnahmeregister nennt als Drain-Auslöser nur Upload, Freigabe, Reprocess und /api/jobs/drain. Neu ist der Drain beim Seitenaufruf, die Architekturkarte ist aber nicht angehakt. – Die Karte beschreibt den Showcase-Betrieb falsch. – Beide Zeilen hier ergänzen. Dabei ehrlich prüfen, ob die Plan-Pflicht-Antwort „kein Infrapfad" stimmt: Es gibt einen neuen Queue- und Kostenauslöser.

[Important] – src/app/_server/drain.ts:48-56 – Die Drossel zählt Starts, nicht laufende Drains. Ein Drain kann 70 s und länger dauern (env.ts:12: 50 s + 20 s, dazu KI-Timeouts), das Intervall beträgt aber nur 30 s. Außerdem drained jede neue Instanz beim ersten Aufruf. – Pro Instanz laufen Drains parallel, was beim Gemini-Free-Tier (etwa jeder dritte Aufruf 503) mehr Last und Kosten erzeugt. Die Korrektheit bleibt durch das pg-boss-Locking gewahrt. – Eine In-flight-Sperre einbauen oder das Intervall auf mindestens den Worst Case setzen, jeweils mit Test.

[Important] – drain.ts:54 / Nachweis – Der Testplan von #70 verlangt zwei Integrationstests: Detailseite im Retry-Zustand und ein fälliger Job, der nach einem Seitenaufruf verarbeitet wird. Keiner davon ist enthalten. `drainOnPageView` selbst ist ungetestet (Gate für AK 4, Drossel-Verdrahtung für AK 3). Die Abweichung ist nicht begründet, der manuelle Nachweis fehlt. – AK 2–4 sind ohne Beleg. – Einen Unit-Test mit gemocktem `getRuntime`/`after` ergänzen (Muster in drain-request.test.ts) und zusätzlich einen Integrationstest schreiben oder die Ausnahme begründen.

[Note] – src/features/requests/processing-notice.ts:18 – Ist `nextRetryAt = null`, heißt es „Ein neuer Versuch ist eingeplant". Null entsteht aber auch bei erschöpften Versuchen (features/jobs/drain.ts:90). Die Anfrage bleibt dann bis zum Dead-Letter-Lauf PROCESSING, während die Liste „–" zeigt. Ein bereits vergangener Termin erscheint weiter als „Nächster Versuch". – Die Detailseite verspricht mehr als die Liste. – Neutral formulieren und einen vergangenen Termin als „fällig" zeigen.

[Note] – processing-notice.ts:15-17 vs. src/app/requests/row-view.ts:30-32 – Dieselben Fakten werden zweimal abgeleitet und weichen schon ab: Die Liste zeigt den Fehler auch bei NEW und mit Stufen-Präfix, der Hinweis nicht. – Das ist ein Drift-Risiko gegen das Ziel „gleicher Zustand". – Den Hinweis aus `requestRowView` ableiten und die Suche nach Bestehendem im PR nennen (Regel 10).

[Note] – gh pr checks – „Vercel – requestflow-ai" steht auf fail („Canceled from the Vercel Dashboard"). Nicht verifiziert, ob das mit diesem Diff zusammenhängt. – Vor Ready-for-review ist ein Check rot. – Den Check neu auslösen oder im PR begründen.

Urteil: changes requested – Es fehlen die Tests aus dem Testplan und die Pflege der Architekturkarte, dazu kommen der fremde CHANGELOG-Eintrag und die überlappenden Drains. Das ist vor Ready-for-review zu klären, nichts davon ist ein Sicherheitsblocker.

Fluory added a commit that referenced this pull request Sep 27, 2026
README still said the SDK does not retry and lacked the two new
settings; the CHANGELOG entry for #69 lived in the stacked #73 instead
of the PR that changes the behaviour. The showcase runbook now sets the
model budget below the web app's AI timeout so the worker never gives up
while a model call is still running. The initial delay gets an upper
bound so a misconfiguration cannot silently exceed the budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fluory and others added 3 commits September 27, 2026 12:03
…to claude/fix-processing-state-70

# Conflicts:
#	CHANGELOG.md
… list row

The fresh review of #73 found three gaps. The throttle counted starts,
but a drain can run longer than its 30 s interval, so views could stack
drains on one instance; a run gate now also waits for the previous run
(and treats it as gone after the function limit). The detail notice
derived its facts separately from the list and already drifted (NEW with
an error, no scheduled retry); it now renders the list's row view, says
"fällig" for a past retry time and promises no retry when none is
scheduled. drainOnPageView itself is now unit-tested at the after()
boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The architecture map and its exceptions register listed upload,
approval, reprocess and the cron route as the only drain triggers; #70
added page views, which is a new queue and cost trigger on the showcase
and belongs in the map. The changelog names the new in-flight limit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Fluory

Fluory commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Merge auf ausdrückliche Anweisung des Orchestrators (2026-09-27: „Merge und deploy“).

@Fluory
Fluory merged commit 9275784 into main Sep 27, 2026
5 checks passed

This branch was successfully deployed

3 active (1 outdated) deployments
Preview – requestflow — 58151582 Deployed Sep 27, 2026 by vercel[bot]
Preview – requestflow-ai — 58151582 Deployed Sep 27, 2026 by vercel[bot]
Production – requestflow — 9163a41b Deployed Sep 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(requests): consistent processing state and pick-up of due retries on the showcase

1 participant