Skip to content

P1: FS-free harness — inline /runs + Redis leaf-result + durable Sandbox CR - #50

Merged
pdettori merged 17 commits into
mainfrom
docs/p1-fs-free-harness
Jul 2, 2026
Merged

pdettori merged 17 commits into
mainfrom
docs/p1-fs-free-harness

Conversation

@pdettori

@pdettori pdettori commented Jul 2, 2026

Copy link
Copy Markdown
Member

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 /work PVC; the sandbox becomes the sole filesystem via a durable agent-sandbox Sandbox CR.

What changed

  • Inline wire contract (clean break): inputs arrive inline in POST /runs; the sync verdict returns inline; the async verdict/gate/done state live in one Redis key leaf:result:<leafSessionId> (TTL LEAF_RESULT_TTL_SECONDS, default 86400); GET /runs/status?sessionId reads that key. The old file-ref fields (inputsRef/resultRef/doneMarkerRef/gateRef/decisionRef) are removed, not dual-supported. workspaceRef stays (a path inside the sandbox the harness never opens).
  • runLeaf is FS-free: inline item/decision, inline verdict/gate results, no node:fs anywhere on the leaf path. The done-marker/gate-marker modules are deleted; the dead readDecision + node:fs remnant is removed from gate.ts.
  • Async worker writes the Redis result record (via classifyOutcome{ack, retryable}) instead of a file marker; dead-letter writes a failed record.
  • Durable sandbox: sandbox.yaml is now an agents.x-k8s.io/v1beta1 Sandbox CR with volumeClaimTemplates (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.sh installs the agent-sandbox controller and waits on the selector-resolved pod.
  • Manifests: /work mount + leaf-pvc.yaml removed from harness+worker (they now mount only tmp); RBAC adds sandboxes get/list.
  • Smokes: rewritten to the inline contract (verdict from response / /runs/status), with a static FS-free red-team assertion.

Verification

  • Full unit suite green (harness 114, knative-server 30, k8s-sandbox 55, work-queue 9, session-backend 10; live-gated tests skip).
  • Live on Kind: sync smoke 10/10, async smoke 6/6, sandbox PVC durability (marker survives pod recreate), harness+worker pods mount only tmp, and .status.selector confirmed a plain string.

Follow-ups (out of P1 scope)

  • leaf-orchestrator.yaml + gate/cron smokes still reference the old /work PVC.
  • failed.message not surfaced on /runs/status (sync-vs-async observability asymmetry).

Assisted-By: Claude Code

Paolo Dettori added 17 commits July 2, 2026 11:45
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 pdettori closed this Jul 2, 2026
@pdettori pdettori reopened this Jul 2, 2026

@pdettori pdettori left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:107decideSeed'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

@pdettori
pdettori merged commit d199484 into main Jul 2, 2026
14 of 15 checks passed
@pdettori
pdettori deleted the docs/p1-fs-free-harness branch July 2, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P1: FS-free harness — leaf envelope off the filesystem (inline + Redis)

1 participant