feat(IDEA-213): supervisors, runtimebroker en egress-proxy (ST-1590) #82
No reviewers
Labels
No labels
severity/s3
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
janpeter/scrum4me-docker!82
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
Uitvoeringslaag (IP-07 t/m IP-09): supervisor voor geïsoleerde job- en hostpogingen, root-runtimebroker met journaal, egress-proxy met allowlist en DNS-pinning, bronstaging, receipts en niet-lancerend herstel.
Review-fixes (code-review 2026-09-20)
549b597— een child die ophangt tijdens een geweigerde CONNECT kon de proxy laten crashen (gereproduceerd met echte sockets).15c6f33— prepare heeft een eigen budget en hervat; de broker joint een lopende create en gooit het residu van een mislukte weg.ba56d0e— de broker handhaaft zelf een gejournaliseerde deadline, ook na herstart. Nieuwe verplichte config-sleutelmaxDurationSeconds.Verificatie (lokaal)
npm test: 649 groen, twee keer;npm run typecheck:dispatch0 fouten.npm run test:dispatch-runtimetegen echte Docker: exit 0, alle 16 isolatie-probes true.Commits
50442a1feat(IDEA-213): supervise isolated job and host attempts5d0123ffix(IDEA-213): serialize scope stops and recover non-launch receiptsf03740ffeat(IDEA-213): pin execution sources and preserve code artifacts14dfc3ffix(IDEA-213): recover source staging and align broker keysa561806fix(IDEA-213): preserve canonical receipts and unexported recovery evidence549b597fix(IDEA-213): keep a disconnecting child from crashing the egress proxy15c6f33fix(IDEA-213): let a timed-out or failed prepare be resumedba56d0efix(IDEA-213): enforce the run deadline in the broker, not only the supervisor🤖 Generated with Claude Code
The unmet-gates list still said POST /attempts/result answers no canonical result and that the transport therefore refuses with DISPATCH_RESULT_RECEIPT_INCOMPLETE. mcp ee898fe superseded that: the route answers {status, result_id, reason, canonical_result?}. The refusal stays in the code as the answer to a service that has not been updated, which is what the text now says. Also corrected: the child output section now describes post-stop collection of the child's exact bytes as `report`, and the recovery port is described as served rather than as something IP13 must still implement. Kept, because they are still true: no child receives its agent_token, there is no producer for `code`, non-launch recovery is not driven end to end from here, Linux confinement is unproven, and no attempt has run against a live service or through this compose template. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>76a5a890f34884f6f8c9WIP: feat(IDEA-213): supervisors, runtimebroker en egress-proxy (ST-1590)to feat(IDEA-213): supervisors, runtimebroker en egress-proxy (ST-1590)REQUEST_CHANGES
geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
error —
lib/dispatch-runtime.ts:10:buildChildEnv()wijstHTTP_PROXYenHTTPS_PROXYnaarhttp://127.0.0.1:18081, maar de container die metbuildContainerArgs()wordt gestart krijgt alleen de egress Unix-socket gemount op/run/dispatchen er wordt geen bridge/listener op127.0.0.1:18081gestart.startEgressBridge()bestaat inlib/dispatch-egress-proxy.ts:81, maar wordt nergens in de child-startup geïnjecteerd. Daardoor hebben normale HTTP(S)-clients in de attempt geen werkende proxy en faalt provider-egress ondanks de gemonteerde socket. De bestaande runtime probe test direct tegen/run/dispatch/egress.sock, waardoor dit pad niet wordt afgedekt.error —
deploy/queue-dispatch.compose.yml:66: de compose-template vereist${DISPATCH_PREPARED_SOURCES_DIR:?prepared sources staging directory required}als host mount, terwijl de supervisorconfig en README de operatorvariabeleDISPATCH_PREPARED_SOURCES_ROOTgebruiken voor prepared sources. Met alleen de gedocumenteerde variabelen faaltdocker compose ... config; met alleenDISPATCH_PREPARED_SOURCES_DIRblijftDISPATCH_PREPARED_SOURCES_ROOTleeg en refuseert de supervisor repo_write/source-mount profielen. Dit maakt de rollout-template niet conform de eigen configuratiecontracten.Verificatie
architecture/overview; er is geen specifiek dispatch-productdoc in de actieve docs-index gevonden via full-text search.f32472e044a19e46eafc33385b1bd4d06759e1cageïnspecteerd.npm ci && npm run typecheck:dispatchgeprobeerd, maarnpm cifaalde in deze tijdelijke omgeving opnode_modules/esbuild/bin/esbuildmetEACCES; daarom geen groen testresultaat kunnen bevestigen.Verdict: REQUEST_CHANGES
Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
error —
bin/run-dispatch-attempt.ts:160: de supervisor zetbusy=truealleen rondports.claim(...)en direct daarna weerfalse. Daardoor rapporteert de executor-heartbeat tijdens een lopende attempt als niet-busy, terwijl de job-supervisor in dezelfde loop wel één attempt bezet houdt totrunManagedWithSignalsterugkeert. Dit breekt de liveness/busy-contracten uit de operator-docs en kan de centrale dispatcher een bezette slot als beschikbaar laten zien. Zet de busy-state rond de volledige attempt-run, niet alleen rond de claim-call, en voeg een regressietest toe dierunJobSupervisorzelf tijdens een langlopende attempt heartbeat-busy laat rapporteren.error —
deploy/queue-dispatch.compose.yml:66:DISPATCH_PREPARED_SOURCES_ROOTwordt in de volume-mount verplicht gemaakt met:?, terwijllib/dispatch-config.tsen de comments erboven expliciet toestaan dat prepared-sources ontbreekt voor profielen zonderrepo_write/source mounts. Een read-only/artifact-only slot zonder producer kan met deze compose-template niet starten. Maak deze mount optioneel via een aparte compose-override/profile, of zorg dat de documentatie/config niet langer belooft dat de producer kan ontbreken.warning —
deploy/queue-dispatch.compose.yml:69: dezelfde template vereist altijdDISPATCH_HOST_SOCKET_DIR, ook voorDISPATCH_MODE=job, terwijl de comment zegt dat de host route alleen voor host mode is. Dit maakt eenvoudige job-mode deploys onnodig afhankelijk van een dummy host-socket directory. Overweeg ook deze mount mode-specifiek te maken of de operator-instructie aan te passen.Samenvatting
De runtime/broker-code is uitgebreid getest en volgt op veel plekken de bestaande runnergrenzen, maar bovenstaande contractbreuken raken scheduling en deploybaarheid. Daarom geen approval in deze vorm.
Verdict: REQUEST_CHANGES
geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
deploy/queue-dispatch.compose.yml:66— De compose-template maaktDISPATCH_PREPARED_SOURCES_ROOTverplicht met${DISPATCH_PREPARED_SOURCES_ROOT:?…}in de volume mount, terwijl dezelfde service deze waarde inenvironmentoptioneel doorgeeft (${DISPATCH_PREPARED_SOURCES_ROOT:-}) enlib/dispatch-config.tsbewust een no-producer/read-only configuratie toestaat zolang het profiel geenrepo_writeofsource_mount_keysvraagt. Daardoor faaltdocker compose ... --profile dispatch-not-activated configal voor een geldige read-only slot zonder prepared-sources producer, in strijd met de README/operator-docs die de producer als all-or-nothing maar optioneel beschrijven. Maak deze mount conditioneel via een apart producer-profiel/servicevariant, of zorg dat read-only slots zonder producer de template kunnen renderen zonder deze variabele.Review log
De diff is niet plan-gekoppeld. Ik heb vooral de nieuwe dispatch-runtime, supervisor/broker/proxy code, Dockerfile en compose-template beoordeeld op contractconsistentie, secret boundaries en test/deploybaarheid. De blocker zit in de operator-template en verhindert een ondersteunde configuratie voordat de runtime zelf kan weigeren of registreren.
Review afgehandeld — F-D2 gerepareerd; F-D1 is by design.
deploy/queue-dispatch.compose.ymlr66 mountte viaDISPATCH_PREPARED_SOURCES_DIRterwijl de supervisor-env,lib/dispatch-config.tsen de READMEDISPATCH_PREPARED_SOURCES_ROOTgebruiken. Gelijkgetrokken op_ROOT(enige plek in de repo). Commite8aecf4,npm test815 passed | 6 skipped.docs/dispatch-runtime-operator.mdstelt expliciet dat het model-imagestartEgressBridge()intern moet starten (binnen de netns) en de proxy-env moet honoreren — en markeert dat óók als "not proved for a release model/provider image". De broker mount alleen de socket + zet de proxy-env; de host-kant (startEgressProxy) is wél gewired en wordt door de broker geverifieerd. Geen codewijziging; het blijft een gedocumenteerd, nog te bewijzen integratiepunt bij een release-model-image.Herpind op shared
c65f5a8(4baa39a); cross-repo contractgate 7/7 groen.Re-review-blocker afgehandeld (F-D2, dieper). Terecht: de verplichte
${DISPATCH_PREPARED_SOURCES_ROOT:?}-mount botste metlib/dispatch-config.ts, dat een read-only slot zónder producer toestaat →docker compose configfaalde voor een geldige read-only-only deployment.Fix (
3301bdc): de/run/sources-mount is uit de base-template gehaald en verplaatst naar een additieve producer-overlaydeploy/queue-dispatch.producer.compose.yml(Composeprofilesgelden per service, niet per volume, dus een overlay is het schoonste). De base rendert nu read-only zonder de var; de overlay voegt de mount toe en maaktDISPATCH_PREPARED_SOURCES_ROOT+DISPATCH_PERMIT_PUBLIC_KEYSverplicht (:?), zodat de all-or-nothing-invariant uitdispatch-config.tsop render-tijd wordt afgedwongen. Bewezen met echtedocker compose config(read-only rendert zonder mount; producer voegt/run/sourcestoe; overlay-zonder-KEYS faalt). README bijgewerkt (base vs. overlay render-commando's) en een pre-existing doc-driftDISPATCH_PERMIT_PUBLIC_KEY_FILE→DISPATCH_PERMIT_PUBLIC_KEYSgecorrigeerd. 2 tests toegevoegd,npm test817 passed | 6 skipped.Verdict: REQUEST_CHANGES
geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
bin/run-dispatch-runtime-broker.ts:39:serveRuntimeBrokeraccepteertsocketGroup=0, omdat de helper alleen<0weigert. De CLI-main weigertsocketGroup<1en de docs noemen gid 1 of hoger terecht als harde eis: gid 0 zou de runtime-socket aan de root-groep geven en daarmee de expliciete supervisor-groep als autorisatiegrens omzeilen. OmdatserveRuntimeBrokergeëxporteerd is en ook in tests/andere assemblages wordt gebruikt, moet dezelfde>=1-validatie in de helper zelf zitten, bij voorkeur met een regressietest.Opmerkingen
De PR bevat veel relevante testdekking en operator-documentatie voor dispatch-supervisor, runtime broker, prepared sources en egress. Zonder gekoppeld plan heb ik geen acceptatiecriteria kunnen afvinken; de review is daarom beperkt tot diff, productdocs en de zichtbare veiligheids-/architectuurclaims.
Re-review-blocker afgehandeld (security). Terecht:
serveRuntimeBroker(bin/run-dispatch-runtime-broker.ts:37) weigerde alleensocketGroup<0, dus gid 0 (root-groep) werd geaccepteerd — in tegenstelling tot de CLI-main (r54,<1) en de docs. Helper-check nusocketGroup<1metDISPATCH_BROKER_SOCKET_GROUP_REFUSED(dezelfde code als CLI-main en de omliggende catch). Regressietest toegevoegd (__tests__/broker-socket-group-floor.test.ts) diechownmockt zodat uitsluitend de>=1-validatie gid 0 kan weigeren — anders geeftchownEPERM al een false-green. Rood-eerst bevestigd. Commite014c66,npm test819 passed | 6 skipped.REQUEST_CHANGES
geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
lib/dispatch-runtime.ts:9/lib/dispatch-runtime.ts:24: de child-container krijgtHTTP_PROXYenHTTPS_PROXYophttp://127.0.0.1:18081, maarbuildContainerArgsstart alleen het child-image met--network noneen mount de egress-volume read-only. Er wordt geenstartEgressBridge()in dezelfde netwerknamespace gestart om127.0.0.1:18081door te verbinden naar/run/dispatch/egress.sock;bin/run-dispatch-egress-proxy.ts:18start alleen de Unix-socket-proxy in de aparte egress-container. Daardoor falen normale model/tool HTTP(S)-requests vanuit de child met connection refused, terwijl de huidige integration probe dit mist omdat die rechtstreeks op de Unix-socket test. Dit blokkeert activatie van de managed runtime.Verdict
REQUEST_CHANGES — de PR introduceert de dispatch-runtime, maar de netwerk-egress wiring voor echte child-processen is niet functioneel.