Commit Graph

20 Commits

Author SHA1 Message Date
Joel Hooks cf55d75613 feat: one-shot microVM exec model — close the host↔guest gap
Fundamental fix: previous execInMicroVm used a poll-based protocol where
host wrote to a shared directory and polled for results. But Firecracker
uses virtio-block devices (not shared directories), so host and guest
can't simultaneously access the same filesystem without page cache issues.

New model (Lambda-style):
1. Host creates workspace ext4 image (64MB)
2. Host loop-mounts it, writes command.sh + request.json, unmounts
3. Host boots Firecracker VM with workspace as /dev/vdb
4. Guest-runner (one-shot) mounts /dev/vdb, executes command, writes
   result.json, powers off the VM
5. Host waits for VM process to exit
6. Host loop-mounts workspace, reads result.json, unmounts
7. Clean up

Changes:
- guest-runner.sh: converted from polling daemon to one-shot executor
  that halts the VM after writing results
- microvm.ts execInMicroVm: complete rewrite for sequential model with
  createWorkspaceImage, mountExt4, unmountExt4 helpers
- dag-orchestrator.ts executeMicroVm: simplified — no longer manages
  boot/destroy lifecycle, delegates to one-shot execInMicroVm

All existing tests pass. Rootfs rebuilt and deployed to PVC.
2026-03-17 08:32:09 -07:00
Joel Hooks d46a46c5b9 Implement ADR-0230 step 7 Firecracker microVM runner helpers, exports, docs, and tests 2026-03-16 15:57:02 -07:00
Joel Hooks ad56989ea1 Reconcile ADR-0221 sandbox registry truth from sandbox metadata before workload sandboxes list cleanup janitor and prove the guarded full-mode path still closes terminally 2026-03-09 19:20:21 -07:00
Joel Hooks f808d5c8c2 Finish ADR-0221 by adding joelclaw workload sandboxes list cleanup janitor, dogfood the live registry surface, and update docs and skill truth 2026-03-09 18:56:53 -07:00
Joel Hooks 2d2d46868d Fix the ADR-0221 full-mode workflow-rig fixture so docker compose healthcheck values serialize as strings at the sandbox base SHA 2026-03-09 17:49:52 -07:00
Joel Hooks 105df25d09 Implement ADR-0221 phase-4 full local sandbox mode, expose it through joelclaw workload run, and add a workflow-rig verification probe 2026-03-09 17:39:05 -07:00
Joel Hooks 3d7bd5fce2 Fix ADR-0221 local sandbox completion by accepting abbreviated base SHAs, writing failed inbox snapshots on dispatch crashes, and adding a verification probe 2026-03-09 17:12:20 -07:00
Joel Hooks 5f816bb8c4 Fix ADR-0221 local sandbox identity generation so path uniqueness survives long shared requestId prefixes 2026-03-09 16:44:26 -07:00
Joel Hooks 0479182618 Implement ADR-0221 phase 3 by adding local sandbox retention pruning, copy-first devcontainer helpers, env injection, and concurrency proof 2026-03-09 16:37:37 -07:00
Joel Hooks 2784500d90 Wire ADR-0221 local sandbox identity, env, registry, and inbox metadata into system/agent-dispatch and update architecture truth 2026-03-09 16:20:11 -07:00
Joel Hooks 2bf78e3569 Implement ADR-0221 phase-1 local sandbox primitives in @joelclaw/agent-execution and update architecture truth 2026-03-09 16:09:48 -07:00
Joel Hooks 8156a62355 Wire an opt-in ADR-0217 k8s sandbox backend into agent-dispatch and document how external repos should submit work through joelclaw queue emit 2026-03-07 22:24:23 -08:00
Joel Hooks 515f336be2 Wire executionMode=sandbox through the real local sandbox runner, carry baseSha/workflowId/storyId through Restate dispatch, and update the operator docs to match live sandbox behavior 2026-03-06 20:45:00 -08:00
Joel Hooks 6fed94d169 Prove Gate B with minimal coding sandbox vertical slice
Implement Gate B proof for sandbox runtime: execute a deterministic tiny coding task that creates one small change in sandbox scope, runs verification, and exports a clean patch artifact before any real acceptance workload runs.

Created packages/agent-execution/__tests__/gate-b-smoke.test.ts with 5 passing tests that prove:
- Sandbox executor can materialize a repo at a specific SHA using materializeRepo()
- Code changes can be made in isolation (add comment to schema.ts)
- Git operations work (add, commit, format-patch via generatePatchArtifact())
- Verification commands execute and results are captured (bunx tsc --noEmit)
- Patch artifacts are generated with full commit metadata
- Touched-file reporting comes from sandbox-local checkout via getTouchedFiles()
- Host checkout stays clean (zero dirt verified in beforeEach/afterEach)
- Patch is reviewable and promotable (git format-patch format)
- Verification success/failure is reported truthfully in artifacts.verification
- Tests are rerunnable deterministically (not tribal knowledge)

Updated documentation:
- packages/restate/README.md: Mark Gate B as proven, document what's proven and known gaps
- docs/inngest-functions.md: Update sandbox mode gate status to reflect Gate B proven
- skills/system-bus/SKILL.md: Update execution mode documentation with Gate B status

All tests pass. Host checkout remains clean. Gate B acceptance criteria met.

Precondition: Gate A passing (verified before work).
Verification: bunx tsc --noEmit, pnpm biome check (passing), bun test packages/agent-execution (105 tests passing).
Files touched: packages/agent-execution/__tests__/gate-b-smoke.test.ts, docs/inngest-functions.md, packages/restate/README.md, skills/system-bus/SKILL.md.
Known gaps: No k8s Job launcher (that's Gate C), no multi-story orchestration, no cancellation/timeout.
2026-03-06 17:58:19 -08:00
Joel Hooks ad84d583e7 Prove Gate A sandbox runtime with non-coding vertical slice
Implement Gate A proof for the sandbox runtime PRD:
- Create packages/agent-execution/__tests__/gate-a-smoke.test.ts with 4 passing tests
- Prove truthful state transitions: running → completed
- Prove artifact generation (read file, write temp artifact)
- Prove zero host dirt (operator checkout stays clean)
- Prove failure state handling with honest error reporting
- Prove JSON serialization round-trip
- Add simple local sandbox executor (not k8s Job launcher - that's Gate B)
- Tests are rerunnable (not tribal knowledge)
- Document Gate A contract in packages/restate/README.md with proven/gaps sections
- Update docs/inngest-functions.md to reference Gate A status
- Update skills/system-bus/SKILL.md to reference Gate A proof

Gate A proves the contract validity and state machine. Known gaps:
- Local executor only (no k8s)
- No real git operations (deterministic SHA)
- No network isolation
- No resource limits
- No cancellation support

Next gates: B (k8s Job launcher), C (multi-story orchestration), D (cancellation/timeout)
2026-03-06 17:53:38 -08:00
Joel Hooks bf89365fa8 Make sandbox terminal states, cancel, and dedupe truthful
Implement sandbox runtime PRD Story 5: ensure duplicate requestIds do not spawn duplicate work, terminal snapshots always land with logs attached, and cancellation kills the sandbox job honestly.

Changes:
1. Add stdout/stderr log surfacing in ExecutionArtifacts type and schema validators
2. Enhance serve.ts with isTerminalState() helper and terminal-aware deduplication
3. Add requestId-level deduplication at agent-dispatch function entry
4. Track active processes in activeProcesses map for cancellation support
5. Implement onFailure handler that kills subprocess and writes cancelled snapshot
6. Capture and attach stdout/stderr (10KB truncated) to all terminal results
7. Add terminal-results.test.ts with validation tests for all execution states
8. Update dag-orchestrator.ts with cancellation contract note
9. Document terminal state guarantees, cancellation, and log surfacing in:
   - docs/inngest-functions.md
   - packages/restate/README.md
   - skills/system-bus/SKILL.md

Verification: bunx tsc --noEmit, bun test packages/agent-execution all pass

Result: No execution can pretend to be running when it finished. Duplicate dispatches return existing terminal results. Cancellation terminates the subprocess and writes honest state. Logs always attached for debugging.
2026-03-06 17:31:22 -08:00
Joel Hooks ec8c4c500b Route Restate story execution through sandbox mode behind PRD_EXECUTION_MODE flag
Add execution-mode flag to route deterministic Restate story execution between host (shared checkout) and sandbox (isolated k8s Jobs) execution paths, preserving the current stable host behavior as default while laying the foundation for sandbox pilot.

Changes:
- Add ExecutionMode type ('host' | 'sandbox') to @joelclaw/agent-execution with schema validators
- Add PRD_EXECUTION_MODE environment variable to trigger-prd.ts (default: 'host')
- Pass executionMode in agent-dispatch payload from trigger-prd
- Route agent-dispatch to sandbox stub when executionMode='sandbox' (returns error until k8s Job launcher is implemented)
- Capture executionMode in InboxResult for observability
- Update Restate README with execution mode documentation and operator contract
- Update inngest-functions.md to document executionMode parameter
- Update system-bus skill with execution mode routing rules

Verification:
✅ bunx tsc --noEmit
✅ pnpm biome check (touched files only)
✅ bun test packages/restate packages/agent-execution (84 pass)

Operator-facing contract:
- PRD_EXECUTION_MODE=host (default): stable shared-checkout path
- PRD_EXECUTION_MODE=sandbox: stub error until k8s Job launcher ships
- Result polling (/internal/agent-result/:requestId) works for both modes
- Stable requestId/workflowId/storyId/agent identity preserved end-to-end

Story: ADR-0217 Sandbox Runtime PRD Story 4
2026-03-06 17:26:26 -08:00
Joel Hooks 08338a6c9f Add repo materialization and patch artifact export to @joelclaw/agent-execution
Implement sandbox runtime PRD Story 3: clean repo materialization and patch-artifact export so sandbox runs mutate only their own checkout and return auditable output instead of touching the host worktree.

New capabilities:
- materializeRepo(): Clone or checkout repo at exact SHA in sandbox-local workspace. Fresh clone if target doesn't exist, fetch+checkout otherwise. SHA verification with automatic unshallow. Isolated from host worktree.
- generatePatchArtifact(): Export auditable patch from baseSha..headSha with touched-file inventory, verification summary, and log references. Uses git format-patch for commits, git diff for uncommitted changes.
- getTouchedFiles(): Capture modified/untracked files via git status --porcelain.
- verifyRepoState(): Validate repo is at expected SHA.
- writeArtifactBundle()/readArtifactBundle(): Serialize ExecutionArtifacts to/from JSON.

Promotion boundary: Phase 1 output is patch bundle + metadata. Runtime does NOT merge to main or push to remote. Operator reviews patch + verification, then applies to host repo or discards.

Tests: Full coverage for repo materialization, artifact export, touched-file inventory, and bundle serialization. All 84 tests pass.

Docs: Updated deploy.md, architecture.md, and system-architecture skill with new contract details and Phase 1 promotion boundary explanation.

Files:
- packages/agent-execution/src/repo.ts (new)
- packages/agent-execution/src/artifacts.ts (new)
- packages/agent-execution/src/index.ts (exports)
- packages/agent-execution/__tests__/repo.test.ts (new)
- packages/agent-execution/__tests__/artifacts.test.ts (new)
- docs/deploy.md (updated)
- docs/architecture.md (updated)
- skills/system-architecture/SKILL.md (updated)

Verification: bunx tsc --noEmit ✓, pnpm biome check ✓, bun test packages/agent-execution ✓ (84/84 pass)
2026-03-06 17:20:54 -08:00
Joel Hooks 9c8f3fef1f Add cold isolated k8s Job runner for sandboxed story execution
Implement sandbox runtime PRD Story 2: cold-runner Job spec, runtime image contract, deterministic naming, and resource cleanup policy for isolated story runs in k8s Jobs.

Outcomes:
- Deterministic k8s Job spec generation via @joelclaw/agent-execution/job-spec
- Job naming keyed by requestId (DNS-1123 compliant)
- Runtime image contract: Git, Bun, agent tooling, /workspace, env-driven config
- Resource limits: 500m-2 CPU, 1-4Gi memory (configurable)
- TTL cleanup: auto-delete after 5 minutes (default)
- Active deadline: 1 hour max runtime
- Backoff limit: 0 (no retries)
- Security: non-root (UID 1000), no privilege escalation, capabilities dropped
- Cancellation support at Job level (delete Job -> SIGTERM)
- Environment variables: WORKFLOW_ID, REQUEST_ID, STORY_ID, TASK_PROMPT_B64, VERIFICATION_COMMANDS_B64, etc.
- Comprehensive test coverage (37 tests for job-spec, 69 total)

Changes:
- packages/agent-execution/src/job-spec.ts: NEW - Job spec generator
- packages/agent-execution/src/index.ts: export job-spec functions
- packages/agent-execution/__tests__/job-spec.test.ts: NEW - comprehensive tests
- k8s/agent-runner.yaml: NEW - runtime contract documentation
- docs/architecture.md: document cold k8s Jobs and runtime contract
- docs/deploy.md: deployment procedures for agent runner
- skills/k8s/SKILL.md: agent runner operations guide

No live infrastructure deployed - code, manifests, and contracts only.

Verification:
- bunx tsc --noEmit ✓
- pnpm biome check ✓
- bun test packages/agent-execution ✓ (69 passing)

Next stories:
- Story 3: Build runtime image with Git + Bun + codex + pi
- Story 4: Hot-image CronJob for pre-warmed images
- Story 5: Warm-pool scheduler for instant dispatch
- Story 6: Wire Restate DAG orchestrator to launch Jobs
2026-03-06 17:13:21 -08:00
Joel Hooks 29edbdb5e7 Create @joelclaw/agent-execution as canonical sandbox execution contract package
Story 1: Define shared sandbox execution contract

Created new workspace package @joelclaw/agent-execution to unify ad-hoc types between Restate workflows and system-bus Inngest functions.

Contract types defined:
- SandboxExecutionRequest: workflow ID, request ID, story ID, task, agent identity, sandbox profile, base SHA, verification commands
- SandboxExecutionResult: request ID, execution state, timestamps, duration, artifacts, error
- ExecutionArtifacts: head SHA, touched files, patch, verification summary, log references
- AgentIdentity: name, variant, model, program
- StoryPlan, WavePlan, PrdExecutionPlan: PRD structure types
- Lifecycle states: pending, running, completed, failed, cancelled
- Sandbox profiles: workspace-write, danger-full-access
- InboxResult: legacy format for backward compatibility

Runtime validation:
- Type guards for all contract shapes (isSandboxExecutionRequest, etc.)
- JSON serialization round-trip tests
- Schema validators with comprehensive test coverage

Integration:
- Restate prd-types.ts now re-exports from @joelclaw/agent-execution
- system-bus agent-dispatch.ts imports InboxResult from shared package
- system-bus serve.ts annotated with contract location comment
- docs/architecture.md created with full system architecture overview

Verification:
- 31 tests pass (contract invariants, serialization, validation)
- TypeScript compilation: clean
- Biome formatting: applied

This package provides the stable contract foundation for Story 2 (Restate wrapper), Story 3 (k8s Job launcher), and Story 4 (dispatch routing).
2026-03-06 17:06:56 -08:00