feat(queue): fase 3 — zelfherstellende claim-lease (refresh + stale-sweep) #100

Merged
janpeter merged 5 commits from feat/queue-fase3-hardening into main 2026-07-26 11:41:51 +02:00
Owner

Fase 3 van de s4m-queue: de claim-lease wordt zelfherstellend.

Stapelt op #98. Deze branch is afgetakt van feat/queue-tools-push, dus tot #98 gemerged is toont de diff ook die tien fase-2-commits. Merge #98 eerst; daarna krimpt deze PR vanzelf tot de vijf commits hieronder.

Commit Wat
cceff1e lease-refresh.ts — 10 s-tick die claimed_at ververst op strikte gelijkheid, entry snoeit bij count === 0, behoudt bij DB-fout
b095309 sweep.ts — idempotente sweep op 8–10 min, FOR UPDATE SKIP LOCKED, mcp:-claims na 5 min en CLI-claims na 4 h, NotifyEnvelope in dezelfde transactie
561313d bootstrap-wiring, stdio-only; http.ts onaangeroerd
23c661c negen integratietests tegen een echte Postgres
f2d6560 voorstel voor de rules-file, expliciet nog niet actief

Waarom dit nodig is. Fase 2 gaf claims een token, maar niets gaf ze ooit weer vrij. Sterft een MCP-proces na queue_next, dan blijft dat bericht eeuwig claimed. De sweep herstelt dat, en de refresh zorgt dat een nog levend proces zijn werk niet afgepakt krijgt.

Twee bewuste afwijkingen van het plan.

De plan-stap voor shutdown.ts schreef "vervang het hele bestand" door een versie die void retourneert en alleen SIGTERM/SIGINT afhandelt. Het echte bestand retourneert { shutdown } — waar index.ts transport.onclose aan hangt — en registreert stdin end/close, met een comment dat uitlegt dat dat het enige afsluitsignaal is waar een gespawnde stdio-MCP op kan rekenen. Letterlijk toepassen had de wees-processen teruggebracht. De wijziging is daarom puur additief: 14 regels erbij, nul eraf. Een test pint nu vast dat shutdown een aanroepbaar object blijft, zodat die plan-versie voortaan niet meer stil doorkomt.

Beide timers krijgen timer.unref?.(), dat het plan niet had, naar het precedent in presence/heartbeat.ts. Gemeten met een echte procesrun tegen scrum4me_test: mét unref eindigt het proces vanzelf (exit 0), zonder unref hangt het tot het gekild wordt. Dat verschil is de faalmodus waar deze repo eerder door geraakt is.

Verificatie. npm test → 1335 passed, 20 skipped. Typecheck schoon. Integratie: 9 groen (fase 3) plus 17 groen (fase 2, regressie) tegen scrum4me_test; zonder TEST_DATABASE_URL skippen ze, dus CI blijft groen.

Elke module is mutatiegetest. Twee integratietests uit het plan bleken minder te bewijzen dan ze beweerden — de concurrency-test was net zo groen zónder SKIP LOCKED, en de incarnatie-test draaide een lus die nul keer liep — en zijn versterkt tot ze de mutatie wél doden.

🤖 Generated with Claude Code

Fase 3 van de s4m-queue: de claim-lease wordt zelfherstellend. **Stapelt op [#98](https://git.jp-visser.nl/janpeter/scrum4me-mcp/pulls/98).** Deze branch is afgetakt van `feat/queue-tools-push`, dus tot #98 gemerged is toont de diff ook die tien fase-2-commits. Merge #98 eerst; daarna krimpt deze PR vanzelf tot de vijf commits hieronder. | Commit | Wat | |---|---| | `cceff1e` | `lease-refresh.ts` — 10 s-tick die `claimed_at` ververst op strikte gelijkheid, entry snoeit bij `count === 0`, behoudt bij DB-fout | | `b095309` | `sweep.ts` — idempotente sweep op 8–10 min, `FOR UPDATE SKIP LOCKED`, `mcp:`-claims na 5 min en CLI-claims na 4 h, NotifyEnvelope in dezelfde transactie | | `561313d` | bootstrap-wiring, stdio-only; `http.ts` onaangeroerd | | `23c661c` | negen integratietests tegen een echte Postgres | | `f2d6560` | voorstel voor de rules-file, expliciet nog niet actief | **Waarom dit nodig is.** Fase 2 gaf claims een token, maar niets gaf ze ooit weer vrij. Sterft een MCP-proces na `queue_next`, dan blijft dat bericht eeuwig `claimed`. De sweep herstelt dat, en de refresh zorgt dat een nog levend proces zijn werk niet afgepakt krijgt. **Twee bewuste afwijkingen van het plan.** De plan-stap voor `shutdown.ts` schreef "vervang het hele bestand" door een versie die `void` retourneert en alleen SIGTERM/SIGINT afhandelt. Het echte bestand retourneert `{ shutdown }` — waar `index.ts` `transport.onclose` aan hangt — en registreert stdin `end`/`close`, met een comment dat uitlegt dat dat het enige afsluitsignaal is waar een gespawnde stdio-MCP op kan rekenen. Letterlijk toepassen had de wees-processen teruggebracht. De wijziging is daarom puur additief: 14 regels erbij, nul eraf. Een test pint nu vast dat `shutdown` een aanroepbaar object blijft, zodat die plan-versie voortaan niet meer stil doorkomt. Beide timers krijgen `timer.unref?.()`, dat het plan niet had, naar het precedent in `presence/heartbeat.ts`. Gemeten met een echte procesrun tegen `scrum4me_test`: mét unref eindigt het proces vanzelf (exit 0), zonder unref hangt het tot het gekild wordt. Dat verschil is de faalmodus waar deze repo eerder door geraakt is. **Verificatie.** `npm test` → 1335 passed, 20 skipped. Typecheck schoon. Integratie: 9 groen (fase 3) plus 17 groen (fase 2, regressie) tegen `scrum4me_test`; zonder `TEST_DATABASE_URL` skippen ze, dus CI blijft groen. Elke module is mutatiegetest. Twee integratietests uit het plan bleken minder te bewijzen dan ze beweerden — de concurrency-test was net zo groen zónder `SKIP LOCKED`, en de incarnatie-test draaide een lus die nul keer liep — en zijn versterkt tot ze de mutatie wél doden. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No reviewers
No labels
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!100
No description provided.