[ISS-12] Dispatch: herstarte supervisor laat geclaimde poging wees achter (CLAIMED na lease_expired, geen reclaim) #189

Open
opened 2026-10-05 03:16:14 +02:00 by janpeter · 0 comments
Owner

Beheerd door Scrum4Me — wijzigingen hier worden overschreven. Bron: https://thuis.jp-visser.nl/issues/cmuuk3fb50001nt7rh0pfjs12

Status: investigating · Severity: s3_major · Gemeld door: scrum4me-server:claude · Occurrences: 1 (laatst: 2026-10-05T01:13:37.601Z) · PBI: PBI-35 · Aangemaakt: 2026-10-05T01:13:37.601Z

Registratie

Wat er gebeurde (2026-10-05, scrum4me-server)

  • 00:43:50Z — dispatch-request fb08c6d2-17c2-43dc-8442-84d585c8fbc5 (QUEUE_REVIEW, CLAUDE) wordt geclaimd door slot b41d91ae… (managed:srv-review-claude-1), incarnatie 3a5d05f4…, attempt 79314efd…; job f2de30c0-9452-457b-b55e-79b6232021e5 gaat naar CLAIMED.
  • 00:43:51Z — de memcg-OOM-killer stopt de supervisorcontainer (limit 262144kB; /tmp-tmpfs telt mee) tijdens get_artifact, nog vóór er een broker-scope bestond.
  • Daarna — docker herstart de supervisor. Die registreert een nieuwe incarnatie d70a54d5…; de oude wordt afgemeld om 00:43:54Z.
  • Event lease_expired om 00:45:53Z — maar verzoek, attempt en job blijven CLAIMED, en de reservering blijft bezet.
  • Geen reconcile, geen claim-stop, geen UNCERTAIN-overgang. De slot-heartbeat en de worker-heartbeat lopen gewoon door, dus er komt ook geen stale-reclaim.
  • Ook een annulering door de opdrachtgever om 01:07Z blijft hangen op CANCEL_REQUESTED met stop_required.

Verwacht

  • De herstarte supervisor (of de service bij lease_expired op een unstarted attempt waarvan de incarnatie is afgemeld) sluit de poging zelf af, zoals bij DISPATCH_PREPARED_SOURCES_REFUSED:
    • claim-stop met een begrensde DISPATCH_*-reden en een failed-resultaat, slot vrij;
    • of het verzoek bereikt minstens zichtbaar UNCERTAIN.
  • Een afgemelde incarnatie met een nooit gestarte attempt (scope_id null) is aantoonbaar zonder uitvoering. Dat hoort geen operator-attestatie te vereisen.

Workaround (gebruikt)

  1. Operator-attestatie via PUT /dispatch/v1/requests/:id/evidence/<key> (kind: operator_attested, binding op de oude incarnatie, scopeId "unstarted:").
  2. Daarna POST /requests/:id/recover met close_cancelled → verzoek en job CANCELLED, reservering vrijgegeven (01:11:21Z).

Mitigatie

De mem_limit van de supervisor is verhoogd van 256m naar 512m (scrum4me-docker PR #109 + host-overlay). Dat neemt de trigger weg, maar niet het wees-gedrag.

Onderzoek


2026-10-05T10:04:59.126Z — scrum4me-server:claude

Analyse (2026-10-05, scrum4me-server:claude)

Correctie op de beschrijving. De DB-events van request fb08c6d2 laten zien dat lease_expired (00:45:53Z) het request wél naar UNCERTAIN zette (uncertain(), src/dispatch/attempts.ts:308-314). Wat bleef hangen: claude_jobs op CLAIMED en de reservering bezet. UNCERTAIN is by design een hold-state die alleen via operator-recover sluit (runbook queue-dispatch.md §9.1–9.2).

Waarom de supervisor het niet zelf kan: bin/run-dispatch-attempt.ts erft na een herstart bewust niets. Er is geen journal-scan, en de AttemptProof met de credential staat alleen in het geheugen. De claim-stop (/attempts/claim-stop) vereist die proof en request-state CLAIMED, maar na lease_expired is het request UNCERTAIN. Een supervisor-fix in scrum4me-docker kan dit faalpad dus niet dichten.

Waarom de service het wél kan bewijzen. Het ontwerp sluit auto-close bij lease-expiry uit, omdat er "een container kan bestaan". De sterkere invariant is echter:

  • signPermit wordt alleen aangeroepen in startDispatchAttempt. Die zet in dezelfde transactie started_at/scope_id en emit started_scope.
  • De broker weigert een start zonder geldig, ondertekend permit (verifyBrokerPermit, scrum4me-docker lib/dispatch-runtime-broker.ts:240).
  • startDispatchAttempt vereist active(), dus !signed_off_at. Een afgemelde incarnatie krijgt nooit meer een permit.

Uit started_at IS NULL ∧ scope_id IS NULL ∧ geen started_scope ∧ incarnatie signed_off_at IS NOT NULL volgt daarom: er is nooit een container gestart en dat kan ook niet meer. Hooguit staat er een created container of scope-dir als restant, wat een resourcelek is en geen uitvoering.

Voorgestelde fix: alleen in de service (scrum4me-mcp). Een sweep in de lease-tick sluit pogingen met die signatuur af:

  • CLAIMED of UNCERTAIN: stop_accepted (systeem-evidence) + failed-resultaat DISPATCH_INCARNATION_SIGNED_OFF_BEFORE_START. De job gaat naar FAILED en de reservering komt vrij.
  • CANCEL_REQUESTED: dezelfde afsluiting, maar als CANCELLED.

Het plan wacht op GO van JP.


2026-10-05T10:17:07.175Z — scrum4me-server:claude

Voortgang (2026-10-05)

JP gaf GO op het plan (alleen een fix in de service). De gematerialiseerde onderdelen zijn PBI-35, ST-055 en T-172 t/m T-174 (sprint S-2026-10-05-1).

PR's (niet gemerged)

  • scrum4me-mcp #191 (#191) bevat de tick-stap closeSignedOffUnstartedAttempts en de regressietest.
  • Scrum4Me #306 (janpeter/Scrum4Me#306) bevat het runbook §9.1/9.2.

Testresultaten

  • De regressietest is rood op origin/main en groen met de fix.
  • Volledige dispatch-suite: 295/295.
  • Unit-tests: 2103/2103 (69 skipped).

Open: review, merge en deploy wachten op een aparte GO van JP. De mitigatie van mem_limit naar 512m blijft staan.

Oplossing

Nog geen oplossing.

> Beheerd door Scrum4Me — wijzigingen hier worden overschreven. Bron: https://thuis.jp-visser.nl/issues/cmuuk3fb50001nt7rh0pfjs12 Status: investigating · Severity: s3_major · Gemeld door: scrum4me-server:claude · Occurrences: 1 (laatst: 2026-10-05T01:13:37.601Z) · PBI: PBI-35 · Aangemaakt: 2026-10-05T01:13:37.601Z ## Registratie ## Wat er gebeurde (2026-10-05, scrum4me-server) - **00:43:50Z** — dispatch-request `fb08c6d2-17c2-43dc-8442-84d585c8fbc5` (QUEUE_REVIEW, CLAUDE) wordt geclaimd door slot `b41d91ae…` (`managed:srv-review-claude-1`), incarnatie `3a5d05f4…`, attempt `79314efd…`; job `f2de30c0-9452-457b-b55e-79b6232021e5` gaat naar `CLAIMED`. - **00:43:51Z** — de memcg-OOM-killer stopt de supervisorcontainer (`limit 262144kB`; /tmp-tmpfs telt mee) tijdens `get_artifact`, nog vóór er een broker-scope bestond. - **Daarna** — docker herstart de supervisor. Die registreert een nieuwe incarnatie `d70a54d5…`; de oude wordt afgemeld om 00:43:54Z. - **Event `lease_expired` om 00:45:53Z** — maar verzoek, attempt en job blijven `CLAIMED`, en de reservering blijft bezet. - Geen reconcile, geen claim-stop, geen `UNCERTAIN`-overgang. De slot-heartbeat en de worker-heartbeat lopen gewoon door, dus er komt ook geen stale-reclaim. - Ook een annulering door de opdrachtgever om 01:07Z blijft hangen op `CANCEL_REQUESTED` met `stop_required`. ## Verwacht - De herstarte supervisor (of de service bij `lease_expired` op een unstarted attempt waarvan de incarnatie is afgemeld) sluit de poging zelf af, zoals bij `DISPATCH_PREPARED_SOURCES_REFUSED`: - claim-stop met een begrensde `DISPATCH_*`-reden en een failed-resultaat, slot vrij; - of het verzoek bereikt minstens zichtbaar `UNCERTAIN`. - Een afgemelde incarnatie met een nooit gestarte attempt (`scope_id` null) is aantoonbaar zonder uitvoering. Dat hoort geen operator-attestatie te vereisen. ## Workaround (gebruikt) 1. Operator-attestatie via `PUT /dispatch/v1/requests/:id/evidence/<key>` (`kind: operator_attested`, binding op de oude incarnatie, `scopeId` "unstarted:<attempt>"). 2. Daarna `POST /requests/:id/recover` met `close_cancelled` → verzoek en job `CANCELLED`, reservering vrijgegeven (01:11:21Z). ## Mitigatie De `mem_limit` van de supervisor is verhoogd van 256m naar 512m (scrum4me-docker PR #109 + host-overlay). Dat neemt de trigger weg, maar niet het wees-gedrag. ## Onderzoek --- *2026-10-05T10:04:59.126Z — scrum4me-server:claude* ## Analyse (2026-10-05, scrum4me-server:claude) **Correctie op de beschrijving.** De DB-events van request `fb08c6d2` laten zien dat `lease_expired` (00:45:53Z) het request wél naar `UNCERTAIN` zette (`uncertain()`, `src/dispatch/attempts.ts:308-314`). Wat bleef hangen: `claude_jobs` op CLAIMED en de reservering bezet. UNCERTAIN is by design een hold-state die alleen via operator-recover sluit (runbook `queue-dispatch.md` §9.1–9.2). **Waarom de supervisor het niet zelf kan:** `bin/run-dispatch-attempt.ts` erft na een herstart bewust niets. Er is geen journal-scan, en de AttemptProof met de credential staat alleen in het geheugen. De claim-stop (`/attempts/claim-stop`) vereist die proof en request-state CLAIMED, maar na `lease_expired` is het request UNCERTAIN. Een supervisor-fix in scrum4me-docker kan dit faalpad dus niet dichten. **Waarom de service het wél kan bewijzen.** Het ontwerp sluit auto-close bij lease-expiry uit, omdat er "een container kan bestaan". De sterkere invariant is echter: - `signPermit` wordt alleen aangeroepen in `startDispatchAttempt`. Die zet in dezelfde transactie `started_at`/`scope_id` en emit `started_scope`. - De broker weigert een start zonder geldig, ondertekend permit (`verifyBrokerPermit`, scrum4me-docker `lib/dispatch-runtime-broker.ts:240`). - `startDispatchAttempt` vereist `active()`, dus `!signed_off_at`. Een afgemelde incarnatie krijgt nooit meer een permit. Uit `started_at IS NULL` ∧ `scope_id IS NULL` ∧ geen `started_scope` ∧ incarnatie `signed_off_at IS NOT NULL` volgt daarom: er is nooit een container gestart en dat kan ook niet meer. Hooguit staat er een *created* container of scope-dir als restant, wat een resourcelek is en geen uitvoering. **Voorgestelde fix: alleen in de service (scrum4me-mcp).** Een sweep in de `lease`-tick sluit pogingen met die signatuur af: - CLAIMED of UNCERTAIN: stop_accepted (systeem-evidence) + failed-resultaat `DISPATCH_INCARNATION_SIGNED_OFF_BEFORE_START`. De job gaat naar FAILED en de reservering komt vrij. - CANCEL_REQUESTED: dezelfde afsluiting, maar als CANCELLED. Het plan wacht op GO van JP. --- *2026-10-05T10:17:07.175Z — scrum4me-server:claude* ## Voortgang (2026-10-05) JP gaf GO op het plan (alleen een fix in de service). De gematerialiseerde onderdelen zijn PBI-35, ST-055 en T-172 t/m T-174 (sprint S-2026-10-05-1). **PR's (niet gemerged)** - scrum4me-mcp #191 (https://git.jp-visser.nl/janpeter/scrum4me-mcp/pulls/191) bevat de tick-stap `closeSignedOffUnstartedAttempts` en de regressietest. - Scrum4Me #306 (https://git.jp-visser.nl/janpeter/Scrum4Me/pulls/306) bevat het runbook §9.1/9.2. **Testresultaten** - De regressietest is rood op origin/main en groen met de fix. - Volledige dispatch-suite: 295/295. - Unit-tests: 2103/2103 (69 skipped). **Open:** review, merge en deploy wachten op een aparte GO van JP. De mitigatie van mem_limit naar 512m blijft staan. ## Oplossing _Nog geen oplossing._ <!-- s4m:issue:cmuuk3fb50001nt7rh0pfjs12 -->
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-mcp#189
No description provided.