feat(IDEA-213): queue-job-kinds en gedeelde document-reader (ST-1590) #102
No reviewers
Labels
No labels
severity/s4
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
janpeter/scrum4me-workers!102
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/idea-213-dispatch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Draft — niet mergen. Geopend om CI te laten draaien; IP-10 t/m IP-14 van ST-1590 zijn nog niet uitgevoerd. Hoort bij Scrum4Me #251; de zeven
feat/idea-213-dispatch-branches horen bij elkaar.Inhoud
Job-kinds QUEUE_TASK/QUEUE_REVIEW in labels, kolommen en settings, en
review-document-source-server.tsleunt op de gedeelde reader (regel voor regel gedragsbehoudend volgens de review).Review-fixes (code-review 2026-09-20)
Geen wijzigingen in deze repo. Open MINOR: T-1852 — de settings-UI accepteert voor de queue-kinds onveilige waarden die daarna elke managed job laten falen (faalt dicht).
Verificatie (lokaal)
Commits
828694dfeat(IDEA-213): pin execution sources and preserve code artifacts🤖 Generated with Claude Code
IP-13, workers. Inventory of every workers write to `claude_jobs` and `agent_message`, and a decision per path. `claude_jobs` (only two writers outside the orchestrator): * `cancelClaudeJobAction` — a row bound to a dispatch request is routed to the central API (`getAutomaticDispatch` for the current version, then `cancelAutomaticDispatch`) with the authenticated user resolved inside `actions/queue-dispatch.ts`; no caller-supplied user id is forwarded. No local status write and no local NOTIFY: the projector owns that state. * `restartClaudeJobAction` — refused. The central equivalent is recovery with stop evidence (§2.4), which a restart button cannot express; the refusal falls before the status check so the text stays actionable. * `enqueueManualJobAction` — QUEUE_TASK/QUEUE_REVIEW refused. The kind read there comes from the drafts table, which `MANUAL_JOB_KINDS` does not bind. Every guard falls before the write and before the NOTIFY. `agent_message`: IP-10 already refuses managed rows in cancel/requeue/fail, archive/unarchive, retention and clear. `replyToAgentMessage` was the gap — a managed ROOT stays open while the dispatcher works, so the path reached it and the Scrum4Me row guard only stopped it once the reply INSERT was on the wire, with its own raw text. The refusal now sits up front, like the rest. Display: a managed kind references its dispatch request id and title and no longer falls through to the legacy target order, which would have presented a neighbouring Task, Idea or Doc as the job's subject — a fabricated Task link for work that has no Task. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>Bump vendored scrum4me-shared to 9c3ce16, which adds the DispatchState value UNCERTAIN_UNSTARTED. Make the dispatch view total for the new value: dispatchStateLabel groups UNCERTAIN_UNSTARTED with UNCERTAIN ("Uitvoering onzeker"), and CLAIMED_STATES includes it so dispatchExecutorLabel still surfaces the executor (dispatchRootStatus in shared already maps it to 'claimed'). No behaviour beyond making the projection total; generated prisma/schema.prisma is byte-identical (72 models, 47 enums). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>WIP: feat(IDEA-213): queue-job-kinds en gedeelde document-reader (ST-1590)to feat(IDEA-213): queue-job-kinds en gedeelde document-reader (ST-1590)REQUEST_CHANGES
Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
error —
lib/queue/dispatch-client-server.ts:185—once()ondertekentrawBodyals bytes, maar verstuurt dezelfde body vervolgens alsrawBody.toString('utf8'). VoorPUT /requests/:id/evidence/:keykan een bewijsbestand willekeurige bytes bevatten; niet-UTF-8 bytes worden door deze conversie vervangen/anders gecodeerd. Daardoor ontvangt de dispatchservice andere bytes dan de bytes waarvoorbody_sha256enX-Content-SHA256berekend zijn, waardoor recovery-evidence corrupt raakt of verificatie faalt. Verstuur deBuffer/Uint8Arrayzelf als fetch-body en dek dit af met een binaire uploadtest.error —
app/(app)/queue/messages/_components/messages-view.tsx:373— de recovery flow wordt zichtbaar gemaakt, maarDispatchRecoveryDialogkrijgt altijdattempt={null}enpublication={null}. In de dialog is de herstelknop disabled zolangattemptontbreekt, enrecover()retourneert dan direct. Een UNCERTAIN dispatch kan dus niet daadwerkelijk worden hersteld via de nieuwe UI, terwijl de gewone queue-acties voor managed rows juist verborgen zijn. Haal attempt/publication detail op uit de dispatchservice of verberg de recovery-actie tot die data beschikbaar is.Verdict
REQUEST_CHANGES — de PR heeft brede testdekking en documentatie, maar deze twee runtime-paden blokkeren respectievelijk evidence upload en herstel van onzekere managed uitvoeringen.
The dispatch REST client signed body_sha256 / X-Content-SHA256 over the raw `rawBody` bytes but then shipped `rawBody.toString('utf8')`. For `PUT /requests/:id/evidence/:key` an artefact can hold arbitrary (non-UTF-8) bytes, so the utf8 round-trip replaced/re-encoded them and the service received different bytes than the ones the signature/hash covered — corrupt evidence or a failed verification. Send the Buffer/Uint8Array directly (WHATWG/undici fetch accepts it) and add a unit test that captures the wire body for non-UTF-8 input and asserts it is byte-for-byte equal to the input (and to body_sha256). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Verdict: APPROVED
Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
Review-notities
Review afgehandeld — F-W1 gerepareerd; F-W2 gepland als follow-up.
lib/queue/dispatch-client-server.ts:once()tekende de evidence-bytes maar verstuurderawBody.toString('utf8'), waardoor niet-UTF-8 bewijs hercodeerd raakte en afweek van de ondertekende bytes (body_sha256/X-Content-SHA256). Nu wordt deBufferrauw verstuurd (body: rawBody), met een binaire regressietest die byte-voor-byte gelijkheid met de ondertekende bytes assert (rood-eerst bevestigd tegen de oude code). Commitfaa8b857,npm run verify1271 passed | 18 skipped.attempt/publication=null): correct waargenomen, maar dit is een bewuste IP-13-limiet (in de code gedocumenteerd — de dialog verzint geen ids waar het bewijs over zou gaan). De echte oplossing (de dispatch-view attempt/publication-detail laten exposeren zodat recovery bruikbaar wordt) is een feature-increment en staat gepland als T-1867.Herpind op shared
c65f5a8(ef747d5).