P1: FS-free harness — inline /runs + Redis leaf-result + durable Sandbox CR - #50
Conversation
Adds the P1 design for the two-tier FS-free harness epic (#49, #45): move the leaf envelope and human-gate markers off the /work PVC (inputs inline in POST, sync verdict inline in response, async verdict + gate/done state in a single Redis result record with TTL), and move the sandbox working set from emptyDir to a durable agent-sandbox Sandbox CR (agents.x-k8s.io) with label-selector exec resolution. Clean-break wire contract (no live external consumer). Adds a P-prefix track to the spec registry and annotates the MVP leaf-session and async-completion specs as superseded-in-part by P1. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Adds the task-by-task implementation plan (9 tasks, TDD) for P1 and refines spec §6.2 to per-leaf-session sandbox pod resolution (resolve at extension init; restart picked up on next invocation) instead of mid-exec cache invalidation, avoiding fragile kubectl-error parsing. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…eaf) Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…ct test Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…d by Redis) Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…ee red-team grep Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…e-dispatch Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
leaf-result-store.ts imports redis directly; it was resolving only transitively, which broke the Docker build's pnpm --frozen-lockfile. Declare redis ^6.0.0 (matching session-backend/work-queue) and sync the lockfile. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…evision on rebuild Under the inline contract a well-formed item always runs, so the old 'bad item_id -> failed' claims no longer hold (terminal failed is covered by the bogus-model claim). Re-scope sync Claim 5 / async Claim 6 to assert the isLeafEnvelope/validateItem guard: a missing-item envelope returns HTTP 400. Also force a new Knative Revision after applying service.yaml, since the mutable :local tag otherwise keeps the previous Revision (old image digest) serving. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
…ne decision path) Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <paolo.dettori@example.com>
pdettori
left a comment
There was a problem hiding this comment.
Review — APPROVE (posted as a comment; GitHub blocks approving one's own PR).
Well-executed Phase-1 rearchitecture. The credentialed harness is now FS-free: the leaf envelope moves inline onto POST /runs, async verdict/gate/done state consolidates into a single TTL-bounded Redis key (leaf:result:<id>), and the sandbox becomes a durable agents.x-k8s.io/v1beta1 Sandbox CR resolved via .status.selector.
The clean break is thorough — no dangling *Ref references survive in runtime code (only in docs/spec prose and negative test assertions). Worker retry/ack logic (classifyOutcome: transient error → no-ack/reclaim, terminal → ack + record) is correct; setup-kind.sh has sound selector-wait error handling plus a build-marker patch for the mutable ksvc tag; manifests carry proper non-root securityContext and scoped sandboxes RBAC. No hidden test skips, and all 9 CI checks pass.
Areas reviewed: TypeScript (harness/server/worker/k8s-sandbox), Helm/K8s manifests, Shell (setup + smokes), Docs. Commits: 17, all DCO-signed.
Nit (non-blocking): harness/src/gate.ts:107 — decideSeed's docstring still says the decision is "read from decisionRef", but that field was removed in the clean break. The decision now arrives inline (env.decision, passed as the decision param). Suggest updating to "the (optional) inline decision".
Assisted-By: Claude Code
P1 — FS-Free Harness (epic #49, closes #45)
Makes the credentialed harness perform no filesystem I/O. The leaf envelope (inputs, verdict, gate, done-marker) moves off the shared
/workPVC; the sandbox becomes the sole filesystem via a durable agent-sandboxSandboxCR.What changed
POST /runs; the sync verdict returns inline; the async verdict/gate/done state live in one Redis keyleaf:result:<leafSessionId>(TTLLEAF_RESULT_TTL_SECONDS, default 86400);GET /runs/status?sessionIdreads that key. The old file-ref fields (inputsRef/resultRef/doneMarkerRef/gateRef/decisionRef) are removed, not dual-supported.workspaceRefstays (a path inside the sandbox the harness never opens).runLeafis FS-free: inlineitem/decision, inline verdict/gate results, nonode:fsanywhere on the leaf path. Thedone-marker/gate-markermodules are deleted; the deadreadDecision+node:fsremnant is removed fromgate.ts.classifyOutcome→{ack, retryable}) instead of a file marker; dead-letter writes afailedrecord.sandbox.yamlis now anagents.x-k8s.io/v1beta1SandboxCR withvolumeClaimTemplates(RWO). The harness resolves the sandbox pod via the CR's.status.selector(a label-selector string) instead of a hardcoded pod name.setup-kind.shinstalls the agent-sandbox controller and waits on the selector-resolved pod./workmount +leaf-pvc.yamlremoved from harness+worker (they now mount onlytmp); RBAC addssandboxesget/list./runs/status), with a static FS-free red-team assertion.Verification
tmp, and.status.selectorconfirmed a plain string.Follow-ups (out of P1 scope)
leaf-orchestrator.yaml+ gate/cron smokes still reference the old/workPVC.failed.messagenot surfaced on/runs/status(sync-vs-async observability asymmetry).Assisted-By: Claude Code