feat(queue): fase 2 restant — queue_done t/m integratietests (taken 10-15) #98

Merged
janpeter merged 10 commits from feat/queue-tools-push into main 2026-07-26 10:46:05 +02:00
Owner

Het restant van fase 2. PR #96 is gemerged op 1b7d359 — dat waren taken 8 en 9 (queue_push, queue_status, queue_list) plus de runbook-correctie. Daarna is er op dezelfde branch doorgewerkt en geforce-pusht, en is de titel van #96 aangepast naar "fase 2 compleet". Die titel klopte niet: deze tien commits zaten er niet in. Een gemergede PR kan geen nieuwe commits meer opnemen, vandaar deze tweede PR.

Inhoud — taken 10 t/m 15 uit docs/superpowers/plans/2026-07-12-s4m-queue-fase2-mcp-kernset.md:

Taak Commit Wat
10 queue_done tweetraps-claimer-check + doneWithReply in één transactie
11 queue_fail zelfde eigenaarscontract, gedeelde ownership.ts
12 queue_wait_reply de mis-routing-fix — correlatiefilter in de WHERE-clause
13 queue_next FIFO-claim met claim-token, bounded wait, MCP-cancel-rollback
14 registratie registerQueueTools stdio-only + non-exposure-test op HTTP
15 integratietests 17 tests tegen een echte Postgres (§8)

Waarom dit ertoe doet. Zonder taak 12 zit de bug er nog: twee sessies op één host claimen elkaars antwoorden, omdat adressering alleen (server, model) is. Het correlatiefilter staat nu in de WHERE, niet in een filter achteraf.

Verificatie. Gerebaset op main (na #97). npm test → 1317 passed, 20 skipped, 176 bestanden. npm run typecheck schoon. De 17 integratietests draaien tegen scrum4me_test en skippen zonder TEST_DATABASE_URL, dus CI blijft groen.

De integratietests vingen vier defecten die met mocks structureel onzichtbaar waren — de ernstigste een autorisatie-bypass via een voorspelbaar claim-token. De atomiciteit van doneWithReply is bewezen met een tijdelijke Postgres-trigger: onder de mutatie blijft er een weesreply achter met het verzoek eeuwig op claimed.

🤖 Generated with Claude Code

Het restant van fase 2. PR #96 is gemerged op `1b7d359` — dat waren taken 8 en 9 (`queue_push`, `queue_status`, `queue_list`) plus de runbook-correctie. Daarna is er op dezelfde branch doorgewerkt en geforce-pusht, en is de titel van #96 aangepast naar "fase 2 compleet". Die titel klopte niet: deze tien commits zaten er niet in. Een gemergede PR kan geen nieuwe commits meer opnemen, vandaar deze tweede PR. Inhoud — taken 10 t/m 15 uit `docs/superpowers/plans/2026-07-12-s4m-queue-fase2-mcp-kernset.md`: | Taak | Commit | Wat | |---|---|---| | 10 | `queue_done` | tweetraps-claimer-check + `doneWithReply` in één transactie | | 11 | `queue_fail` | zelfde eigenaarscontract, gedeelde `ownership.ts` | | 12 | `queue_wait_reply` | **de mis-routing-fix** — correlatiefilter in de WHERE-clause | | 13 | `queue_next` | FIFO-claim met claim-token, bounded wait, MCP-cancel-rollback | | 14 | registratie | `registerQueueTools` stdio-only + non-exposure-test op HTTP | | 15 | integratietests | 17 tests tegen een echte Postgres (§8) | **Waarom dit ertoe doet.** Zonder taak 12 zit de bug er nog: twee sessies op één host claimen elkaars antwoorden, omdat adressering alleen `(server, model)` is. Het correlatiefilter staat nu in de `WHERE`, niet in een filter achteraf. **Verificatie.** Gerebaset op `main` (na #97). `npm test` → 1317 passed, 20 skipped, 176 bestanden. `npm run typecheck` schoon. De 17 integratietests draaien tegen `scrum4me_test` en skippen zonder `TEST_DATABASE_URL`, dus CI blijft groen. De integratietests vingen vier defecten die met mocks structureel onzichtbaar waren — de ernstigste een autorisatie-bypass via een voorspelbaar claim-token. De atomiciteit van `doneWithReply` is bewezen met een tijdelijke Postgres-trigger: onder de mutatie blijft er een weesreply achter met het verzoek eeuwig op `claimed`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Twee van de zeven mutaties overleefden, en het waren de twee garanties die het
zwaarst wegen. De strikte gelijkheid op claimed_by was niet vastgepind: geen
fixture gebruikte een waarde die de verwachte als prefix heeft, dus startsWith
kwam er ongestraft doorheen -- terwijl 'mcp:inst:tok2' een andere claim is dan
'mcp:inst:tok'.

Verder waren de reply-adressering en het reply-type ongedekt: elke fixture
gebruikte hetzelfde adres als de gestubde identiteit, en alleen task->result
was uitgeprobeerd, dus zowel hardgecodeerde from/to als een hardgecodeerd
'result' bleven onzichtbaar.

Geen productiecode gewijzigd.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Het plan test de eigenaarsmatrix bewust niet opnieuw in dit bestand, en dat is
verdedigbaar -- maar het gevolg was dat queue-fail.test.ts in isolatie nul
dekking had van step (c) en van pending-met-token. De tests slaagden dánkzij
het delen van ownership.ts, zonder dat te bewijzen: wie die logica later inlinet
of forkt, merkt het hier niet.

Eén step-(c)-geval pint de routing vast, met een prefix-botsing als fixture
omdat dat het geval is dat een verzwakte vergelijking zou doorlaten.

Daarnaast was z.string().min(1) op de error-tekst volledig ongedekt. Een
mislukking zonder reden vastleggen is erger dan geen mislukking: de ontvanger
ziet failed zonder enige aanwijzing waarom.

Geen productiecode gewijzigd.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drie mutaties overleefden de acht tests uit het plan. De id-set is de
belangrijkste: de enige test die de aanroep-argumenten exact controleert
gebruikt een enkel id, dus bij meerdere ids mocht er stilletjes eentje
wegvallen -- en dat maakt precies dat ene verzoek onbeantwoordbaar, wat de
mis-routing is die deze tool moet oplossen.

Verder was de ?? DEFAULT_WAIT_SECONDS-fallback ongedekt (queue-list heeft voor
zijn analoge default wél een test), en de timeout-vorm was alleen gedekt op de
vroege tak, niet op de fallback ná het betreden van de bounded wait.

Geen productiecode gewijzigd.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Een vaste constante in plaats van randomUUID() kwam door alle zeven tests
heen, en dat is geen dekkingsgat maar een autorisatie-bypass binnen het proces:
verifyLocalOwnership sleutelt de lease op bericht-id en vertrouwt het token als
enige bewijs, terwijl bericht-ids zichtbaar zijn via queue_list en
queue_status. Met een voorspelbaar token kan een aanroeper queue_done of
queue_fail uitvoeren op werk dat hij nooit claimde.

Twee claims naast elkaar pinnen nu vast dat de tokens verschillen. De suite kon
dat niet zien omdat hij nooit twee claims deed.

Daarnaast: het wake-up-predicaat werd nooit uitgevoerd omdat
waitForQueueWakeup gemockt is -- nu uit de mock gevangen en direct getoetst --
en de meta in de respons was ongedekt, terwijl meta.task.cwd precies is wat de
ontvanger nodig heeft.

Geen productiecode gewijzigd.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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!98
No description provided.