feat(dispatch): Codex-reviewer-kind, release en run-log (M41 increment 2) #95

Merged
janpeter merged 8 commits from feat/m41-reviewer-kind into master 2026-10-02 16:46:17 +02:00
Owner

Samenvatting

M41 increment 2 (sprint S-2026-10-02-2, story ST-1615, taken T-1933…T-1939): een echte Codex-reviewer in de afgeschermde container, met logs en opruimen. Sluit ook T-1869 (egress-bridge in de modelimage) af.

  • U2 — kindimage model-codex (Dockerfile.dispatch, bin/run-dispatch-child.ts, lib/dispatch-child.ts):
    • bevat de egress-bridge, de payload uit /sources/__dispatch_input en de queue-prompt die op MCP_GIT_REF is vastgelegd;
    • draait codex exec --json;
    • laat altijd een geldig result.json achter: een begrensd failed-resultaat bij een ontbrekend of ongeldig resultaat en bij stop.
  • Besluit B1 (JP): een eigen Codex-API-sleutel als operatorbron /sources/codex_api_key.
    • Hij komt alleen in de omgeving van het codex-proces, nooit in argv.
    • De sleutel en zijn gemaskeerde echo worden uit transcript, stderr, resultaat en run-log gefilterd.
  • U6 / T-1869: npm run test:dispatch-model-image. Met de echte broker en egress-image loopt een CONNECT via HTTPS_PROXY door de bridge naar een toegestane gateway; een niet-toegestane host krijgt 403. assertProviderProxySupport(true) geldt alleen voor deze adapter.
  • U3 — broker release: na het terminale antwoord worden de container en de pogingsmap verwijderd, inclusief de sleutelkopie.
    • Alleen toegestaan na de eigen stop met vastgelegd stopbewijs.
    • Controleert de exacte container-id, labels en dat de container niet meer draait, en gebruikt docker rm zonder -f.
    • Het journaal blijft bewaard; de operatie is idempotent.
  • U4 — run-log voor Worker Logs (lib/dispatch-run-log.ts, lib/dispatch-redact.ts):
    • alleen JSON-regels uit het transcript, gefilterd en begrensd;
    • eigen metaregels in het run-one-job-formaat, met job-id uit de pogingscontext (scrum4me-mcp#176);
    • atomisch geschreven naar <DISPATCH_WORKER_LOG_DIR>/dispatch/<slot>/runs/;
    • optionele overlay deploy/queue-dispatch.worker-logs.compose.yml.
  • U7 — proefscript: npm run smoke:dispatch-live-review (niet in CI).
  • U8: operator-doc (sectie "Model image") en README.

Test

  • npm test: 1030 groen, plus 2 failures in transcript-retention.test.ts die al vóór deze branch bestonden; die tests vereisen GNU chmod --reference, die macOS niet heeft.
  • typecheck:dispatch: schoon.
  • skills:verify: OK.
  • Cross-repo contractgate tegen scrum4me-mcp#176: 9/9.
  • test:dispatch-model-image: groen.
  • Praktijkproef op mac Docker Desktop (2026-10-02, met JP's eigen sleutel):
    • verdict NO-GO voor het ingebouwde gebrek, in 34 s; het resultaat valideert;
    • de sleutel staat in geen enkele uitvoer;
    • het run-log wordt door de Ops-dashboard-parser gelezen (Ops-dashboard#279);
    • alleen api.openai.com:443 gecontacteerd;
    • container, pogingsmap en sleutelkopie zijn na release weg.

Niet bewezen

  • Linux (scrum4me-server, max2).
  • Een draaiende dispatchservice; dat volgt in M41 increment 3.

Restrisico (B1): het model kan het sleutelbestand zelf lezen. Een gateway buiten het kind is de latere verharding.

Operatornotities

  • De egressimage heeft --tmpfs /tmp nodig.
  • Op Docker Desktop moet het policybestand binnen de VM staan.
  • Het sleutelbestand moet leesbaar zijn voor uid 10001.
  • Zet DISPATCH_AGENT_OUTPUT_KEY niet.

🤖 Generated with Claude Code

## Samenvatting M41 increment 2 (sprint S-2026-10-02-2, story ST-1615, taken T-1933…T-1939): een echte Codex-reviewer in de afgeschermde container, met logs en opruimen. Sluit ook T-1869 (egress-bridge in de modelimage) af. - **U2 — kindimage `model-codex`** (`Dockerfile.dispatch`, `bin/run-dispatch-child.ts`, `lib/dispatch-child.ts`): - bevat de egress-bridge, de payload uit `/sources/__dispatch_input` en de queue-prompt die op `MCP_GIT_REF` is vastgelegd; - draait `codex exec --json`; - laat altijd een geldig `result.json` achter: een begrensd `failed`-resultaat bij een ontbrekend of ongeldig resultaat en bij stop. - **Besluit B1 (JP):** een eigen Codex-API-sleutel als operatorbron `/sources/codex_api_key`. - Hij komt alleen in de omgeving van het `codex`-proces, nooit in argv. - De sleutel en zijn gemaskeerde echo worden uit transcript, stderr, resultaat en run-log gefilterd. - **U6 / T-1869:** `npm run test:dispatch-model-image`. Met de echte broker en egress-image loopt een CONNECT via `HTTPS_PROXY` door de bridge naar een toegestane gateway; een niet-toegestane host krijgt 403. `assertProviderProxySupport(true)` geldt alleen voor deze adapter. - **U3 — broker `release`:** na het terminale antwoord worden de container en de pogingsmap verwijderd, inclusief de sleutelkopie. - Alleen toegestaan na de eigen stop met vastgelegd stopbewijs. - Controleert de exacte container-id, labels en dat de container niet meer draait, en gebruikt `docker rm` zonder `-f`. - Het journaal blijft bewaard; de operatie is idempotent. - **U4 — run-log voor Worker Logs** (`lib/dispatch-run-log.ts`, `lib/dispatch-redact.ts`): - alleen JSON-regels uit het transcript, gefilterd en begrensd; - eigen metaregels in het `run-one-job`-formaat, met job-id uit de pogingscontext (scrum4me-mcp#176); - atomisch geschreven naar `<DISPATCH_WORKER_LOG_DIR>/dispatch/<slot>/runs/`; - optionele overlay `deploy/queue-dispatch.worker-logs.compose.yml`. - **U7 — proefscript:** `npm run smoke:dispatch-live-review` (niet in CI). - **U8:** operator-doc (sectie "Model image") en README. ## Test - **`npm test`:** 1030 groen, plus 2 failures in `transcript-retention.test.ts` die al vóór deze branch bestonden; die tests vereisen GNU `chmod --reference`, die macOS niet heeft. - **`typecheck:dispatch`:** schoon. - **`skills:verify`:** OK. - **Cross-repo contractgate** tegen scrum4me-mcp#176: 9/9. - **`test:dispatch-model-image`:** groen. - **Praktijkproef op mac Docker Desktop** (2026-10-02, met JP's eigen sleutel): - verdict **NO-GO** voor het ingebouwde gebrek, in 34 s; het resultaat valideert; - de sleutel staat in geen enkele uitvoer; - het run-log wordt door de Ops-dashboard-parser gelezen (Ops-dashboard#279); - alleen `api.openai.com:443` gecontacteerd; - container, pogingsmap en sleutelkopie zijn na `release` weg. ## Niet bewezen - Linux (scrum4me-server, max2). - Een draaiende dispatchservice; dat volgt in M41 increment 3. **Restrisico (B1):** het model kan het sleutelbestand zelf lezen. Een gateway buiten het kind is de latere verharding. ## Operatornotities - De egressimage heeft `--tmpfs /tmp` nodig. - Op Docker Desktop moet het policybestand binnen de VM staan. - Het sleutelbestand moet leesbaar zijn voor uid 10001. - Zet `DISPATCH_AGENT_OUTPUT_KEY` niet. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
De broker kent een nieuwe strikte operatie release({attemptId}). Die is
alleen toegestaan als het eigen journaal 'stopped' met stopbewijs heeft,
verwijdert precies de eigen container (id + labels, docker rm zonder -f)
en de pogingsmap (bronnen incl. sleutelkopie, work, output), en laat
journaal en stopbewijs staan met releasedAt. Tweede aanroep is een no-op;
een crash halverwege wordt bij de volgende aanroep afgemaakt.

De supervisor roept release aan na een terminaal antwoord op het
resultaat (accepted/late, closeUnstarted, herstelpad), vóór het
opruimen van het eigen journaal. Een release-fout verandert de uitkomst
niet en wordt gelogd met een begrensde code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Target model-codex in Dockerfile.dispatch: gepinde node-digest, @openai/codex
op dezelfde CODEX_VERSION als de worker, alleen src/prompts/queue/*.md uit
scrum4me-mcp op MCP_GIT_REF, uid 10001, entrypoint bin/run-dispatch-child.ts.

Het kind start de egress-bridge, bouwt /tmp/payload.json uit
/sources/__dispatch_input met eigen sha256 per bron (sleutelbron uitgesloten),
kiest review.codex.md of task.codex.md en draait één codex exec met de
Codex-API-sleutel (besluit B1) alleen in de env van codex. Transcript en
resultaat worden geschoond en begrensd; bij exit, ontbreken, ongeldigheid of
SIGTERM blijft altijd een gevalideerd result.json achter.

De image-invariantentest is gesplitst: de supervisor/egress-lijn houdt alle
oude eisen, de modellijn krijgt eigen eisen (pin, codex-pin gelijk aan de
worker, alleen promptkopie, USER 10001).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/dispatch-run-log.ts schrijft na stageResult en vóór submitResult
(best-effort, ook bij een staged replay) één run-log in het bestaande
run-one-job-formaat naar <logRoot>/dispatch/<slotId>/runs/<ts>.log.
Het transcript van het kind wordt gelezen met dezelfde link-, realpath-
en groottecontroles als result.json (max 16 MiB, met afkapmarkering);
alleen JSON-objectregels blijven over, zodat door het kind geschreven
metaregels wegvallen, en elke regel wordt op geheimpatronen gefilterd
(lib/dispatch-redact.ts; onder de lib/dispatch-*-glob van de image).
Zonder jobId (hostroute) wordt geen log geschreven. Atomisch via een
verborgen tijdelijke naam en link (nooit overschrijven).

Optionele DISPATCH_WORKER_LOG_DIR (absoluut, bestaand, geen symlink) en
de overlay deploy/queue-dispatch.worker-logs.compose.yml met precies één
schrijfbare mount voor de dispatch-pool. Een deploymenttest bewaakt dat
elke module die de supervisor-entrypoint importeert in de image staat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run test:dispatch-model-image bouwt de echte egress-image en een
model-codex-probe-image (echte entrypoint, codex vervangen door een stub) en
draait ze via de echte broker zonder testOnly-pad. De stub doet CONNECT via
HTTPS_PROXY → bridge → /run/dispatch/egress.sock → proxy: de toegestane
gateway-stub antwoordt (200 + banner, de stub logt de verbinding), een
niet-toegestane host krijgt 403. Het resultaat valideert, het transcript
bestaat, en de sleutel staat niet in argv, transcript of resultaat; codex
krijgt alleen HOME/PATH/proxy/git-safe.directory en CODEX_API_KEY.

Rood eerst: zonder bridge-start in de entrypoint gaf dezelfde test
ECONNREFUSED op 127.0.0.1:18081. Daarna verklaart alleen deze adapter
assertProviderProxySupport(true) (CODEX_PROVIDER_PROXY_VERIFIED).

Bevinding: de echte egress-image start read-only alleen met een /tmp-tmpfs
(tsx-cache); de test geeft die mee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eén echte Codex-review in het verzegelde kind op Docker Desktop (T-1938):
echte model-codex- en egress-image (allowlist alleen api.openai.com), echte
broker create/start/stop/release en writeRunLog. Sleutel als operatorbron uit
M41_CODEX_KEY_FILE; wordt nooit geprint, alleen na afloop vergeleken.
Niet in npm test of CI: npm run smoke:dispatch-live-review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
De proefrun met een nepsleutel liet zien dat OpenAI een geweigerde sleutel
terugmeldt als de eerste 8 en laatste 4 tekens rond sterretjes. Die vorm
viel buiten de bestaande patronen en kwam in transcript en run-log. Zowel
het kind als de run-log-schrijver van de supervisor maskeren hem nu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(dispatch): model-codex-image, providersleutel en bewijs (M41 U8)
All checks were successful
CI / Compose config (pull_request) Successful in 10s
CI / Build-arg coverage (pull_request) Successful in 9s
CI / Docker build (pull_request) Successful in 1m55s
c77da7731b
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s4m-codex-reviewer left a comment

Verdict: APPROVED

Geen blokkerende findings aangetroffen.

  • INFO — testverificatie: de dispatch-TypeScript-check is succesvol uitgevoerd op commit c77da7731ba82ef674707bd05362539d73a53bf8. De volledige Vitest-run kon in deze runner niet starten omdat /tmp met noexec is gemount; de toegevoegde tests en relevante fout-, cleanup- en run-logpaden zijn aanvullend statisch beoordeeld.
  • INFO — geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.

De implementatie houdt de broker-/child-grens intact, maakt credential-opruiming afhankelijk van terminale centrale bevestiging en documenteert de nieuwe model-image- en Worker Logs-operatie voldoende.

## Verdict: APPROVED Geen blokkerende findings aangetroffen. - INFO — testverificatie: de dispatch-TypeScript-check is succesvol uitgevoerd op commit `c77da7731ba82ef674707bd05362539d73a53bf8`. De volledige Vitest-run kon in deze runner niet starten omdat `/tmp` met `noexec` is gemount; de toegevoegde tests en relevante fout-, cleanup- en run-logpaden zijn aanvullend statisch beoordeeld. - INFO — geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden. De implementatie houdt de broker-/child-grens intact, maakt credential-opruiming afhankelijk van terminale centrale bevestiging en documenteert de nieuwe model-image- en Worker Logs-operatie voldoende.
janpeter merged commit 19972a1acb into master 2026-10-02 16:46:17 +02:00
Sign in to join this conversation.
No reviewers
No labels
severity/s3
No milestone
No project
No assignees
2 participants
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-docker!95
No description provided.