Skip to content

Handoff: Finish UH v0.8.0 — "Sandbox isolation" (full scope, oh-my-pi graduated)

  • Created: 2026-05-25
  • Project: /Users/eduardojaviergarcialopez/AgenticEngineering/ultimate-harness
  • Branch: dev (@ 6173162d)
  • Live npm latest: @agenticengineeringagency/ultimate-harness@0.7.0
  • Live tags on origin: v0.7.0, plugin-v0.7.0
  • Path chosen by lead: C — Full: ship #154 + #155 + #156 in v0.8.0; graduate oh-my-pi to active this cycle.
  • Continues from: the v0.8.0 kickoff handoff (filed at session start; see top-of-thread).
  • Supersedes: the kickoff handoff’s “Immediate Next Steps” — #154 + #155 are now done; #156 + release mechanics are what remain.

#154 (spike) + #155 (container backend) are merged on dev and shipped behind the OpenSandbox seam (PR #157, commit 6173162d). Container backend is real, gated through OpenSandbox, hardened against the three Codex P2 review threads (sandbox cwd, orphan delete, configurable lifecycle timeout). ADR + runbook landed.

What’s still required before v0.8.0 can be tagged + published to npm latest:

  1. #156 — graduate oh-my-pi to active (the only remaining v0.8.0 scope item).
  2. Close GitHub issues #154 + #155 (they did NOT auto-close on the merge of #157 — none of the merged PRs included a Closes # trailer that GitHub recognized).
  3. CHANGELOG [0.8.0] section.
  4. Version bumps: package.json and apps/hermes-plugin/dashboard/manifest.json → 0.8.0.
  5. Documentation truth-up across README + docs/ROADMAP.md + apps/docs/content/docs/index.mdx (oh-my-pi state, sandbox status, latest-release pointers).
  6. Release PR dev → main, CI green, squash-merge.
  7. Tags v0.8.0 + plugin-v0.8.0 on main; verify Publish workflow lands @…/ultimate-harness@0.8.0 on npm latest.
  8. Sync the live docs site (uh.agenticengineering.lat) — it lags main.

What landed in v0.8.0 so far (proof of done)

Section titled “What landed in v0.8.0 so far (proof of done)”
Issue / PR What Where
#154 spike + ADR OpenSandbox chosen as the v0.8 execution-tier candidate; per-OS claim boundaries; two-tier framing (filesystem vs execution); AgentFS deferred; lean OCI/docker-CLI rejected as default (lead-confirmed-fallback only) docs/architecture/sandbox-backends.md (incl. ## #154 local smoke results (2026-05-24) + ## #157 lifecycle hardening)
#155 container backend Real ContainerBackend (src/harness/sandbox-backends.ts) replacing the #137 stub; reuses DirectoryBackend for host materialization; OpenSandbox command seam (runOpenSandboxCommand, runOpenSandboxTemplate); env-configurable templates (UH_OPENSANDBOX_*) src/harness/sandbox-backends.ts; CLI plumbing in src/cli.ts (~L1414 --backend flag), verify wiring in src/harness/verify.ts. Runbook: docs/runbooks/container-sandbox.md.
#157 P2 hardening (this session) (a) Templates spawn in the sandbox-bound cwd, not process.cwd(). (b) Force/orphan discards always run UH_OPENSANDBOX_DELETE_COMMAND so external sandbox resources don’t leak; spawn falls back to ctx.root when the worktree is gone. (c) UH_OPENSANDBOX_LIFECYCLE_TIMEOUT_MS (positive int, default 30000) — wired through create + delete templates with fail-fast validation. Commits 78db04c5, 85eabb75, 35fd1dcb. Tests: tests/sandbox.test.ts (3 new (#157) tests).
Item Status Files to touch
#156 oh-my-pi graduation OPEN; needs ToS decision then status flip + smoke + table sweep .harness/adapters/oh-my-pi.yaml (L18), src/harness/adapter-add.ts (template at L97), README.md (L28 adapter table), docs/ROADMAP.md (L157 Adapters considered table), apps/docs/content/docs/index.mdx (L32 mirror table), CHANGELOG.md
#154 / #155 GitHub issue closure OPEN; merged PRs did not include Closes # trailers gh issue close 154 155 --comment "Shipped in #157 via …"
CHANGELOG [0.8.0] section Missing CHANGELOG.md (insert above ## [0.7.0])
package.json version 0.7.0 package.json:3
apps/hermes-plugin/dashboard/manifest.json version 0.7.0 apps/hermes-plugin/dashboard/manifest.json:6
README “Latest release” pointer Says v0.7.0 README.md:22
ROADMAP ## Current release framing Tops at v0.7.0 docs/ROADMAP.md (Latest release + Adapters considered tables)
Docs-site mirror tables Top adapter table + index mirror apps/docs/content/docs/index.mdx (~L32)
Release PR dev → main Not opened —
Tags v0.8.0 + plugin-v0.8.0 missing —
npm publish Gated on tag Publish workflow auto-triggers; verify with npm view
Live docs deploy Lags main scripts/sync-docs-site.mjs + apps/docs/ Alchemy deploy
  • UH = runtime-agnostic harness, CLI uh, TS/Bun, Vitest, strict Zod (fail-fast).
  • Sandbox model is two-tier: filesystem (git-worktree default, directory clone) and execution (container = OpenSandbox-gated; the host worktree is also directory-style for compatibility with porcelain dirty detection + promotion).
  • Six wired adapters: hermes, codex, hermes-proxy, openrouter, pi are active; oh-my-pi is experimental (this milestone graduates it or formally documents the block).
  • Orchestration (index, metadata, path-safety, mission seeding, dirty-gating on discard) lives in src/harness/sandbox.ts. Each backend implements only materialize / teardown / collectDirtyChanges.

The memory artifact uh-adapter-wiring-points enumerates these; in this repo the surfaces that mention oh-my-pi’s status: (or its experimental framing) are:

  1. Manifest: .harness/adapters/oh-my-pi.yaml:18 — status: experimental.
  2. Scaffold template (the one uh adapter add oh-my-pi writes): src/harness/adapter-add.ts:97 — also status: experimental. Both must move in lockstep.
  3. Header comment in src/harness/adapter-add.ts:15 (oh-my-pi — experimental) — keep in sync.
  4. README adapter table: README.md:28 (state column).
  5. README capability summary line: README.md:22 (the inline list …openrouter, pi active; oh-my-pi experimental).
  6. README.md:85 + README.md:98 — CLI examples that read “active: hermes, codex, hermes-proxy, openrouter, pi; oh-my-pi experimental”. Update both occurrences.
  7. ROADMAP “Adapters considered” table: docs/ROADMAP.md:157.
  8. Docs-site mirror: apps/docs/content/docs/index.mdx:32.
  9. CHANGELOG: a ## [0.8.0] entry that calls graduation out.
  10. Runbook reference: docs/runbooks/anthropic-via-omp.md (already documents ToS posture; either link a fresh “active” smoke entry or extend it).

Existing live-smoke artifact for OMP is the reference mission at examples/missions/anthropic-via-omp-smoke.yaml invoked via uh mission run — see docs/runbooks/anthropic-via-omp.md:70-95.

Decisions (carried forward; do not re-litigate without lead sign-off)

Section titled “Decisions (carried forward; do not re-litigate without lead sign-off)”
Decision Source Implication for v0.8.0
Container = OpenSandbox first, no lean OCI/docker-CLI fallback unless lead approves docs/architecture/sandbox-backends.md “Decision summary” + “Pivot rule” #155 stays as shipped; do NOT add a parallel docker-CLI path under #156 pressure.
agentfs filesystem backend = deferred; AgentFS is FS-only, not execution isolation ADR Not in v0.8.0 scope.
Kubernetes / cloud sandbox tier = post-1.0 PRD + ADR Not in v0.8.0 scope.
Capability-declaration enforcement (--strict) = v0.10.0 PRD Not in v0.8.0 scope.
Muta = post-1.0 (needs co-founder conversation) PRD Not in v0.8.0 scope.
Path C chosen this session: graduate oh-my-pi to active in v0.8.0 This handoff #156 must be resolved this cycle — graduate or formally close-with-rationale before tagging.

Cardinal rule (from the kickoff handoff): “Resolve ALL Codex review comments AND resolve the threads. When you change a fact, grep ALL of README/ROADMAP/index.mdx/CHANGELOG for every occurrence.” The PRD PR took 3 rounds because of duplicate-section drift. Do not repeat that.

Phase 1 — Close v0.8.0 scope (#156 oh-my-pi)

Section titled “Phase 1 — Close v0.8.0 scope (#156 oh-my-pi)”

1a. Resolve the ToS posture decision.

  • Read docs/runbooks/anthropic-via-omp.md:21-50 (the ToS section). The current posture: OMP’s stealth surface bypasses Anthropic’s client_id detection of unauthorized 3rd-party CLI access against the Consumer ToS (Feb 20, 2026 update). This is a cat-and-mouse posture, not stable.
  • Two acceptable answers:
    • G1. Graduate — declare UH’s posture is “we route through OMP and inherit OMP’s posture; the runbook is authoritative; users opt in.” This is the path the lead has been moving toward; it is consistent with pi shipping active despite similar territory. Requires: a live uh mission run smoke against the reference mission, a CHANGELOG note, and a paragraph in the runbook noting “graduated to active in v0.8.0”.
    • G2. Block — close #156 with wontfix (or punt to v0.9.0 alongside the native Anthropic adapter), citing the ToS escalation risk. Then v0.8.0 ships oh-my-pi still experimental. This is the lead’s call; if they pick G2, stop here, jump to Phase 2 and remove #156 from the milestone.

1b. (If G1) Run the smoke.

Terminal window
# Pre-req: pi installed, OMP installed, OMP auth set up per the runbook.
uh mission run examples/missions/anthropic-via-omp-smoke.yaml --runtime oh-my-pi --json
# Expect: runtime-result.yaml status: passed.
# Capture the run dir path; you will reference it in the runbook.

If the smoke fails with blocked / quota / auth, OMP’s stealth surface has likely been tightened — pin a known-good OMP version (runbook §Troubleshooting), retry, or fall back to G2.

1c. (If G1) Flip status in the 10 touchpoints listed above.

  • .harness/adapters/oh-my-pi.yaml:18: status: experimental → status: active.
  • src/harness/adapter-add.ts:97: same flip. Also update the header comment at :15.
  • README.md: L22 (capability summary), L28 (table state column), L85 + L98 (CLI examples).
  • docs/ROADMAP.md: L157 (“graduated v0.8.0; see CHANGELOG / runbook”).
  • apps/docs/content/docs/index.mdx: L32 mirror table.
  • Add a dated subsection to docs/runbooks/anthropic-via-omp.md noting the v0.8.0 smoke (date, OMP version, mission id, result, claim).

1d. Targeted tests:

Terminal window
bun run typecheck
bunx vitest run tests/sandbox.test.ts tests/verify.test.ts tests/adapter-add.test.ts 2>/dev/null \
|| bunx vitest run tests/sandbox.test.ts tests/verify.test.ts

The full local suite flakes under machine load (see Gotchas). Push and trust per-PR CI.

1e. Open the PR, base dev, target dev. Title: feat(oh-my-pi): graduate to active — v0.8.0 (#156). Body: link the runbook smoke entry + the touchpoint diff.

1f. Resolve Codex comments + resolve threads.

  • Resolve every Codex review comment that lands.
  • After resolving, run the resolveReviewThread GraphQL mutation per thread (pattern: see this session’s earlier gh api graphql -f query='mutation($id:ID!){resolveReviewThread(input:{threadId:$id}){thread{id isResolved}}}' -f id="$tid").
  • Squash-merge.

Phase 2 — Close the GitHub issues + write the release notes

Section titled “Phase 2 — Close the GitHub issues + write the release notes”

2a. Close #154 + #155 (they did not auto-close):

Terminal window
gh issue close 154 --comment "Shipped: ADR landed in docs/architecture/sandbox-backends.md; runbook at docs/runbooks/container-sandbox.md. PR #157."
gh issue close 155 --comment "Shipped: ContainerBackend gated through OpenSandbox in src/harness/sandbox-backends.ts; tests in tests/sandbox.test.ts; runbook + ADR updated. PR #157 (initial #155, hardening #157)."

Close #156 only after Phase 1’s PR merges.

2b. CHANGELOG [0.8.0] section. Insert above the current ## [0.7.0] entry in CHANGELOG.md. Keep the existing tone (Keep-a-Changelog; group Added / Changed / Notes). Template:

## [0.8.0] — <YYYY-MM-DD>
Milestone **"Sandbox isolation"** (GitHub milestone v0.8.0, issues #154 / #155 / #156). Promotes the `container` sandbox backend from a fail-fast stub to a real OpenSandbox-gated execution-isolation tier and graduates `oh-my-pi` to `active`.
### Added
- **`container` sandbox backend — OpenSandbox-gated execution isolation** (#155): replaces the #137 stub. `ContainerBackend` reuses `DirectoryBackend` for host-side materialization (so porcelain dirty detection + promotion are unchanged) and routes mission/verification commands through the OpenSandbox seam (`runOpenSandboxCommand` → `runOpenSandboxTemplate`). Env contract: `UH_OPENSANDBOX_ENABLED=1` + `UH_OPENSANDBOX_EXEC_COMMAND` (must contain `{command}`; optional `{cwd}`, `{image}`, `{timeout_ms}` placeholders); optional `UH_OPENSANDBOX_CREATE_COMMAND`, `UH_OPENSANDBOX_DELETE_COMMAND`, `UH_OPENSANDBOX_IMAGE` (default `python:3.12`), `UH_OPENSANDBOX_LIFECYCLE_TIMEOUT_MS` (positive int ms, default 30000). Mock mode (`UH_OPENSANDBOX_MODE=mock`) for CI / dirty-roundtrip tests. Local smoke + claim-boundary table: `docs/runbooks/container-sandbox.md`. ADR: `docs/architecture/sandbox-backends.md` (incl. #154 spike + #157 lifecycle hardening).
- **`oh-my-pi` graduated to `status: active`** (#156): after the ToS posture documented in `docs/runbooks/anthropic-via-omp.md` and a fresh `uh mission run` smoke against `examples/missions/anthropic-via-omp-smoke.yaml`. Adapter manifest, scaffold template (`uh adapter add oh-my-pi`), README adapter table + capability summary, ROADMAP, and the docs-site mirror all reflect `active`. *(If Path G2 was taken instead, replace with: "**`oh-my-pi` posture clarified** (#156): formally remains `experimental` pending the native Anthropic adapter / ToS resolution; rationale recorded in `docs/runbooks/anthropic-via-omp.md`.")*
### Changed
- `runOpenSandboxTemplate` spawns commands in the sandbox-bound `cwd` (the host worktree) rather than `process.cwd()` so `verify` / create / delete templates with relative paths resolve against the sandbox (#157).
- `ContainerBackend.teardown` runs `UH_OPENSANDBOX_DELETE_COMMAND` unconditionally when configured; forced / orphan discards spawn from `ctx.root` when the worktree has already been removed so external sandbox resources cannot leak (#157).
- Lifecycle timeouts are configurable via `UH_OPENSANDBOX_LIFECYCLE_TIMEOUT_MS` (positive int ms, default 30000) (#157).
### Notes
- **No fallback to a lean in-house OCI/docker-CLI backend is authorized by this release** — the ADR pivot rule stands. The container path is OpenSandbox-only unless the lead approves a pivot.
- **CI does not exercise live container execution** — Depot runners have no container runtime. The OpenSandbox mock mode covers schema + lifecycle plumbing in CI; live evidence is in `docs/runbooks/container-sandbox.md`.
- **macOS isolation reality** — on macOS the boundary is the host's Linux VM (Docker Desktop / OrbStack / Colima). Firecracker / KVM / gVisor / Kata claims require a Linux host with a configured secure runtime, per the ADR per-OS table.
- **Deferred to v0.9.0+**: AgentFS as a filesystem backend (FS-only, not execution); native Anthropic adapter; Honcho MCP tools + opt-out; capability-declaration enforcement (`--strict`).

2c. Version bumps:

  • package.json:3 → "version": "0.8.0".
  • apps/hermes-plugin/dashboard/manifest.json:6 → "version": "0.8.0".

2d. README “Latest release” pointer at README.md:22 → **v0.8.0**. Also re-read the line in full and update the adapter inline list if Path G1 was taken.

2e. ROADMAP “Current release” framing at the top of docs/ROADMAP.md — add a v0.8.0 paragraph mirroring the CHANGELOG headline.

2f. Docs-site index apps/docs/content/docs/index.mdx — mirror the README adapter table and any “latest” pointers.

Phase 3 — Release mechanics (dev → main, tag, publish)

Section titled “Phase 3 — Release mechanics (dev → main, tag, publish)”

Memory artifact for the canonical sequence: uh-release-flow. Summarized here so the stranger doesn’t have to fetch it.

3a. Open the release PR:

Terminal window
# From dev:
gh pr create --base main --head dev \
--title "release: v0.8.0 — Sandbox isolation" \
--body "$(cat <<'EOF'
Milestone v0.8.0 (issues #154, #155, #156). See CHANGELOG `[0.8.0]` and `docs/architecture/sandbox-backends.md`.
Tag plan:
- v0.8.0
- plugin-v0.8.0
EOF
)"

3b. Wait for CI green. The CI workflow is “Typecheck + tests + build”; the “Pack + publish dry-run” job runs in .github/workflows/. Do NOT push to main directly.

3c. Squash-merge the release PR via gh pr merge <N> --squash (must be a standalone gh pr merge invocation — the harness auto-mode classifier blocks compound forms).

3d. Tag on main:

Terminal window
git checkout main && git pull --ff-only
git tag -a v0.8.0 -m "v0.8.0 — Sandbox isolation"
git tag -a plugin-v0.8.0 -m "plugin v0.8.0 (UH v0.8.0)"
git push origin v0.8.0 plugin-v0.8.0

If SSH push fails (see Gotchas), use the HTTPS-token pattern:

Terminal window
git push "https://x-access-token:$(gh auth token)@github.com/Agentic-Engineering-Agency/ultimate-harness.git" v0.8.0 plugin-v0.8.0

3e. Verify publish workflow ran on the tag:

Terminal window
gh run list --workflow "Publish package" --branch main --limit 5
# Wait for completion, then:
npm view @agenticengineeringagency/ultimate-harness dist-tags version
# Expect: latest = 0.8.0

If npm shows 404: cannot publish over the previously published versions style error, the in-place idempotency guard (UH-91) skipped — recheck the version bump landed in main.

3f. Close the GitHub milestone: gh milestone close v0.8.0 (or via web UI). Confirm all three issues are closed.

The lead flagged in the kickoff handoff: “Doc fixes are on dev, not main → the live docs site lags until v0.8.0’s dev→main (or an off-cycle docs sync). Flagged to the lead; their call.” After Phase 3, the site is unblocked.

Terminal window
node scripts/sync-docs-site.mjs # syncs docs/ → apps/docs/content/docs/
# Then deploy per docs/runbooks/ (alchemy / tsx — Bun segfaults on apps/docs/alchemy.run.ts, see Gotchas)
cd apps/docs && tsx alchemy.run.ts deploy # adjust to the canonical command per the runbook

Verify https://uh.agenticengineering.lat reflects v0.8.0 framing.

  • ToS posture for #156 — the lead is the only one who can pick G1 vs G2. Path C in this handoff assumes G1. If they pick G2, drop the manifest/template/table flips and replace the CHANGELOG #156 bullet with the “remains experimental, rationale captured” version above.
  • OMP version pin — if the live smoke fails, decide whether to pin a known-good OMP in the runbook or hold #156 for v0.9.0.
  • Doc-site deploy command — tsx alchemy.run.ts is the established pattern but the exact deploy script may differ; cross-reference scripts/sync-docs-site.mjs and the prior v0.7.0 deploy. The lead has handled this end-to-end before.
  • v0.9.0: Honcho MCP tools + opt-out; native Anthropic adapter (the ToS-clean alternative to OMP routing).
  • v0.10.0: Capability-declaration enforcement (warn + --strict); adoption package.
  • v1.0.0: Stability freeze on 4 public surfaces + friend’s external dry-run.
  • AgentFS as a filesystem backend — keep deferred unless the lead reopens scope.
  • Read first: this file, then docs/architecture/sandbox-backends.md (ADR with #157 lifecycle section), then docs/runbooks/container-sandbox.md, then docs/runbooks/anthropic-via-omp.md. After that you’re caught up.
  • Branching: feature branches off dev, PRs target dev. main is release-only. dev is currently at 6173162d (PR #157 merged).
  • Commit author: LaloLalo1999 (the lead’s GitHub identity). Do NOT commit as FR / luci / others.
  • Resolve all AI-review comments + resolve the GraphQL review threads before merging any PR. The lead enforces this.
  • CI is the authoritative test gate — Depot runs the full suite in ~1 min and is reliable. Local full-suite vitest runs flake under load (multiple git-spawning suites hang for minutes if machine load >8). Run targeted test files locally; trust per-PR CI for the full suite.
  • Never push to main directly. Release happens via dev → main PR + tag.

Gotchas (preserved from kickoff handoff — still apply)

Section titled “Gotchas (preserved from kickoff handoff — still apply)”
  • SSH push broken on this workstation (sign_and_send_pubkey: signing failed). Push via HTTPS token. The token has workflow scope so .github/workflows/** pushes work.
  • Auto-mode classifier blocks compound bash + some gh pr merge forms — run gh pr merge standalone.
  • Bun segfaults running apps/docs/alchemy.run.ts → docs deploy uses tsx (already wired).
  • semgrep PreToolUse/CI flags a pre-existing shell: true spawn in src/harness/verify.ts (the mission check runner) — by-design; CI is diff-aware and passes.
  • uh adapter check exits non-zero on not-found (useful for smoke gating; do not catch and swallow).
  • Local full-suite flakes under machine load — see above.
  • OpenSandbox lifecycle env contract is fail-fast: bad UH_OPENSANDBOX_LIFECYCLE_TIMEOUT_MS raises with the variable name in the message; bad UH_OPENSANDBOX_IMAGE does the same. Template missing {command} raises with the placeholder list. The 30s default is generous for warm caches and tight for cold image pulls — document any user-visible bump in the runbook.
  • gh CLI authed as LaloLalo1999 (scopes: gist, read:org, repo, workflow); SSH protocol selected but broken — use the HTTPS-token push pattern.
  • Bun 1.4.0, Node 24, Depot CI runners, semgrep 1.157 hook active.
  • npm registry token: NPM_CONFIG_TOKEN is gated in CI (Publish workflow); do not export locally.
  • None.

Environment Variables (names only; do NOT inline values)

Section titled “Environment Variables (names only; do NOT inline values)”
  • Repo secrets: NPM_CONFIG_TOKEN, OPENROUTER_API_KEY (optional CI live-smoke), Anthropic / OMP credentials as the lead’s local concern.
  • Local: UH_OPENSANDBOX_* are only needed when exercising the container backend live (per the runbook). They are NOT required for the release flow itself.
  • Active PRD: docs/prds/uh-roadmap-to-1.0-v1.0-prd.md — the road-to-1.0 plan with the container research and milestone exit criteria.
  • Sandbox ADR: docs/architecture/sandbox-backends.md (incl. ## #154 local smoke results (2026-05-24) + ## #157 lifecycle hardening).
  • Container runbook: docs/runbooks/container-sandbox.md.
  • OMP / ToS runbook: docs/runbooks/anthropic-via-omp.md.
  • GitHub milestone: v0.8.0 (#2) — issues #154 / #155 / #156.
  • Memory (~/.claude/projects/-Users-…-ultimate-harness/memory/): uh-adapter-wiring-points, uh-release-flow, docs-site-deploy-ops, cf-token-rotation-pending.

A fresh agent who reads docs/handoffs/v0.8.0-finish.md + the ADR + the OMP runbook should be able to answer, without this conversation:

  1. What do I do next? → “Decide G1 vs G2 with the lead. If G1, run the OMP smoke; flip status in the 10 touchpoints; open the PR. Then close issues, write the CHANGELOG, bump versions, open the release PR, tag, publish.”
  2. Why? → “v0.8.0 needs #156 + release mechanics. #154 and #155 are already merged on dev via PR #157.”
  3. What not to do? → “Don’t push to main. Don’t add a lean OCI/docker-CLI backend without lead approval. Don’t graduate OMP without the ToS read-through. Don’t trust the local full-suite vitest run under machine load. Don’t commit as anyone other than LaloLalo1999.”

If any of those answers is unclear, fix this file before stopping.