Stand up .pi/ as a real project workspace for the pi-coding-agent CLI:
- settings.json: OpenAI defaults (gpt-4o), model cycling list, declared resource paths
- mifune-banner extension: flush-left MIFUNE wordmark + pi harness · ryaneggz/mifune subtitle
- path-guard extension: tool_call interceptor with case-insensitive risky-bash regex,
hasUI headless guard, /dev/sd*|nvme*|hd*|disk* bare-redirection match, defensive
toolName.toLowerCase() (verified lowercase in pi source), canonical input key `path`
- APPEND_SYSTEM.md: generic, consumer-agnostic
- prompts/review.md: /review template
- skills + rules: directory symlinks to ../.claude/{skills,rules} (single source of
truth, mirrors .codex/ pattern)
- new canonical .claude/skills/harness-context/SKILL.md reachable from pi via symlink
Tests at .pi/extensions/__tests__/{path-guard,mifune-banner}.test.ts (32 + 7 cases,
hand-rolled mock ExtensionAPI, no pi devDep). vitest.config.ts include glob extended
to .pi/**/__tests__/**/*.test.ts. ci-harness.yml paths filter extended and a new
"Root tests" step runs pnpm test:scripts (also lifts cron-runtime.test.ts into CI).
86 tests passing across 6 files.
Closes #255
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
42 KiB
42 KiB
Changelog
All notable changes to this project are documented here.
Format follows Keep a Changelog; versions use CalVer (YYYY.M.D with -N suffix for same-day releases) and match git tags.
Update policy and release automation live in .claude/rules/git.md § Changelog.
[Unreleased]
Fixed
- Docs landing page supported-agent copy and agent grid now include DeepAgents with a matching icon tile.
opencode --version(and anyopencodeinvocation) failing withEACCES: permission denied, mkdir '/home/sandbox/.config/opencode'on a fresh sandbox. Docker auto-creates~/.configas root to satisfy thegh-config:/home/sandbox/.config/ghnamed-volume mount, which then blocks the sandbox user from creating siblings like~/.config/opencode. The entrypoint's non-recursive parent-chown loop now also covers~/.configalongside the existing~/.local/~/.local/shareparents, while leaving the bind-mounted~/.config/ghcontents untouched. (#247)opencodeandopencode /connectfailing withEACCES: permission denied, mkdir '/home/sandbox/.local/state'on a fresh sandbox. Docker auto-creates~/.localand~/.local/shareas root to satisfy theopencode-hostbind-mount path; the entrypoint now non-recursively chowns those parent dirs tosandbox:sandboxso the user can create siblings like.local/state. The bind-mounted~/.local/share/opencodeitself stays untouched. Re-applies a fix that was lost when #241 merged a conflictingentrypoint.sh. (#245)
Added
- Project-local Pi config under
.pi/standing the agent up at the harness root: OpenAI-defaultsettings.json(gpt-4o, model-cycling list scoped to GPT/o-series),mifune-banner.tsextension replacing pi's startup header with a flush-leftMIFUNEwordmark +pi harness · github.com/ryaneggz/mifunesubtitle,path-guard.tsextension interceptingtool_callevents to confirm before write/edit on sensitive paths (.env*,secrets/,credentials.{json,yaml},*.pem,*.key, ssh keys) and risky bash (rm -rf,sudo,git push --force,git reset --hard, bare> /dev/sd*|nvme*|hd*|disk*) with case-insensitive matching andctx.hasUIguards for headless/RPC mode, generic consumer-agnosticAPPEND_SYSTEM.md,prompts/review.mdtemplate invokable as/review, andskills+rulesdirectory symlinks to../.claude/skills/../.claude/rulesmatching the single-source-of-truth pattern already in.codex/. New canonical.claude/skills/harness-context/SKILL.mdreachable from pi via the symlink. Test suite at.pi/extensions/__tests__/{path-guard,mifune-banner}.test.ts(32 + 7 cases, hand-rolled mockExtensionAPI, no pi devDep); pi source verified viagh apifor tool-name casing (lowercase) and write/edit canonical input key (path).vitest.config.tsincludeextended to.pi/**/__tests__/**/*.test.ts;.github/workflows/ci-harness.ymlpaths:filter extended (.pi/**,scripts/**,vitest.config.ts) and a new "Root tests" step runspnpm test:scripts, also bringing the previously-orphanedscripts/__tests__/cron-runtime.test.tsinto CI. (#255) - Project-local Pi plan-mode extension under
.pi/extensions/plan-mode/, with/plan,/plan approve,--plan, read-only tool/bash gating, plan approval prompts, and[DONE:n]progress tracking. - Directory README pattern: tracked
README.mdinapps/,crons/,scripts/,tasks/, and.worktrees/documenting each directory's intent, subfolder conventions, and lifecycle. New rule.claude/rules/directory-readme.mdcodifies the pattern;.gitignoreexempts only.worktrees/README.md(replacing the unused.gitkeepexemption);.claude/rules/git.md§ Worktrees points to the new README and documents the.worktrees/project/<project-name>/<repo>/slot for independent project clones;docs/architecture/container-runtime.mdmoves.worktrees/into the canonical tree (README tracked, contents ignored). Three session lessons appended tocontext/IDENTITY.mdcovering README-over-.gitkeep, "treat X same as Y" interpretation discipline, and semantic-subfolder enumeration. - OpenCode as a core sandbox agent: install
opencode-aiin the base image, persist~/.local/share/opencode, add theopencode-hostoverlay, supportscripts/ralph.sh --harness=opencode, and document OpenAI OAuth as the default OpenCode provider path. - DeepAgents CLI support as a first-class supported sandbox runtime alongside Claude Code, Codex, OpenCode, and Pi.
.devcontainer/Dockerfileinstallsdeepagents-cliviauv tool installinto/usr/local/bin;.devcontainer/docker-compose.ymladds adeepagents-authnamed volume at/home/sandbox/.deepagentsso provider keys, memory, skills, and sessions persist across rebuilds;.devcontainer/docker-compose.deepagents-host.ymlis an opt-in host-state overlay (off by default — DeepAgents stores raw provider API keys with no scope/expiry, so the blast radius is wider than the Claude/Codex host overlays);.devcontainer/entrypoint.shextends its chown loop to include.deepagentsand skips it whenDEEPAGENTS_HOST_BIND_MOUNT=1.scripts/ralph.shaccepts--harness=deepagents(orRALPH_HARNESS=deepagents) and dispatches to a constrained default invocationdeepagents -y --shell-allow-list recommended -q --no-stream --max-turns "$RALPH_DEEPAGENTS_MAX_TURNS" -n "$task"(default 25 turns);RALPH_DEEPAGENTS_FLAGSoverrides the safety/output flag string, and--max-turnsis always appended after so the per-iteration cap cannot be silently dropped.--shell-allow-list allcombined with the sandbox's mounted Docker socket can affect sibling containers or host Docker — only enable for trusted tasks via the env-var override.install/banner.shreports DeepAgents[installed]fromcommand -v deepagentsand[configured]only when~/.deepagents/.envor~/.deepagents/config.tomlis non-empty (an empty mounted directory is no longer treated as authenticated). New agent doc atdocs/agents/deepagents.md;docs/agents/overview.md,README.md,docs/quickstart.md,docs/onboarding.md,docs/installation.md,docs/operations/{provision,destroy,repair}.md,docs/guide/{overlays,permissions,configuration}.md, anddocs/architecture/{container-runtime,overview}.mdenumerate the new agent, volume, overlay, and Ralph harness option. Repo-local.deepagents/is project data and follows normal git review/ignore rules; secrets and provider keys belong only in~/.deepagentsor ignored local files. Claude remains the default Ralph harness; existing Claude/Pi/Codex execution and fallback behavior are unchanged. (#237) docs/agents/pi.mdstub (linking upstream tobadlogic/pi-mono) and a Pi entry in the home-page agent grid atapps/docs/src/pages/index.tsx, matching Pi's restoration as a default-installed CLI. (#233)- Top-level
Makefilewrappingdocker compose -f .devcontainer/docker-compose.ymlso users typemake sandbox/make shell/make destroyinstead of the 50-character compose lines. Targets:sandbox,shell,destroy,stop,logs,ps,restart,help(default, self-documenting via awk). Uses-include .devcontainer/.envsoSANDBOX_NAMEflows through. No agent-CLI targets — the harness's value is sandbox isolation, agent choice (claude/codex/pi) happens insidemake shell.
Changed
- BREAKING: Installer now clones to
~/.openharness/(was~/openharness/);scripts/install.shauto-migrates existing~/openharness/checkouts in place. - BREAKING: In-repo
.openharness/subdirectory removed entirely. - BREAKING:
composeOverrides[]config moved from.openharness/config.jsonto repo-rootconfig.json(gitignored, seeded from trackedconfig.example.json). - BREAKING:
pi-host.ymldropped from defaultcomposeOverrides— Pi users add it back manually inconfig.json. /cloudflared-tunnelskill reconstructs a missing<tunnel-id>.jsonfromcloudflared tunnel tokeninstead of failing — fixes the case where a tunnel exists in the Cloudflare account (e.g. created on another host) but onlycert.pemis on the running machine. Documents that token-mode runs (--token <T>) silently drop the local--urlflag, leaving Cloudflare to return 530s while connections register. (#249)- Quickstart collapsed from two parallel paths (installer + manual) to a single linear flow in
README.mdanddocs/quickstart.md. Manual setup demoted to a<details>block. New "Configuration" section in both docs surfaces.devcontainer/.envas the customization surface (make destroy && make sandboxto re-apply). install.shmoved toscripts/install.shto colocate withcron-runtime.tsandralph.shper the orchestrator-scripts convention. Repo-detection logic and help-text self-references updated for the new path. Action required for the curl-pipe URL: update thehttps://oh.mifune.dev/install.shredirect tohttps://raw.githubusercontent.com/ryaneggz/open-harness/refs/heads/main/scripts/install.sh.scripts/install.shsmarter: auto-detects hostgh auth tokenand prompts to share it with the sandbox (skips in-sandboxgh auth loginwhen accepted); auto-populates.devcontainer/.envwith host timezone and git identity; newprompt_ynhelper for interactive Y/n prompts; idempotency guard preserves existing.devcontainer/.envon re-runs. Dropped the unconditionalSANDBOX_PASSWORDprompt (only honored by the opt-in sshd overlay; defaults silently tochangeme). Build-time message added before the cold-cache build to set ~10-min expectations. "Next steps" output restructured to lead with the.devcontainer/.envpath so the customization surface is discoverable.CLAUDE.mdLifecycle section uses the newmaketargets (make sandbox,make shell,make ps,make destroy); deeper inspection commands (docker exec ... tmux ls) keep their docker-native form..devcontainer/.example.env: top header rewritten to document the file as the single customization surface;INSTALL_AGENT_BROWSERdefault flipped fromtruetofalse(~1 GB / +3 min on first build, opt-in only when needed);SANDBOX_PASSWORDcomment clarified to note it's only honored with the sshd overlay.- Consolidate repository layout documentation into
docs/architecture/container-runtime.md#repo-layout; reduce duplicate tree blocks inAGENTS.mdandREADME.mdto one-line pointers; add.claude/rules/repo-layout-source.mdto prevent the next drift round. Remove phantom heartbeat env vars (HEARTBEAT_ACTIVE_START,HEARTBEAT_ACTIVE_END,HEARTBEAT_AGENT,HEARTBEAT_INTERVAL) fromdocs/architecture/container-runtime.mdand.devcontainer/.example.env. Fix brokenREADME.mdcroner link (was.claude/specs/structure-spec-v0.7.md, nowdocs/architecture/crons-and-heartbeats.md).
Added
- Restore
@mariozechner/pi-coding-agentas a default-installed CLI alongside Claude Code and Codex.DockerfileAGENTSdefault goes back toclaude-code,codex,pi-coding-agent;.devcontainer/docker-compose.pi-host.ymloverlay restored and listed in.openharness/config.jsoncomposeOverrides;.devcontainer/entrypoint.shextends its host-bind skip-chown loop with.pi/PI_HOST_BIND_MOUNT. Pi auth shares the same trust tradeoffs as the claude/codex host overlays (host UID = 1000 + pre-existing~/.pi). The Mom Slack bot stays out of base — install@ryaneggz/mifunefor that.docs/installation.md,docs/onboarding.md,docs/operations/{provision,repair}.mdupdated; stalemomshell alias dropped frominstall/.zshrc(leftover from #209). "type": "module"in rootpackage.jsonto silence the NodeMODULE_TYPELESS_PACKAGE_JSONwarning emitted on everycron-runtime.tsinvocation. The script already uses ESMimportsyntax; no CommonJS inscripts/to break.## Active itemswatchlist insidecrons/heartbeat.md— session-curated section the hourly heartbeat reads each pulse to validate deferred follow-ups across session boundaries via an enumerated permitted-checks list (gh pr view,gh issue view,gh run list,gh release list, date-based reminders). Cron is read-only on the section; sessions add and remove lines. Newcrons/.cron.log(gitignored undercrons/*.log) appended each pulse as a one-line ISO-8601 liveness signal — additive to the existingmemory/<today>/log.mdwrites.context/USER.mdgains a 3-sentence "Tracking deferred follow-ups" pointer at theAuthorization patternsboundary. Plan at.claude/plans/polished-spinning-dijkstra.mdsurvived 3 critic rounds (round 1 rejected a separatememory/HEARTBEAT.md, round 2 rejectedAGENTS.mddocumentation, round 3 converged on the v5 shape). (#224)
Removed
ohCLI references and the multi-agent / N-worktree / heartbeat-daemon copy from the marketing landing page atapps/docs/src/pages/index.tsx; quickstart code block now usesmake shell, hero + architecture + WHY sections rewritten to "we provide the sandbox, you choose the harness".docs/agents/overview.mdsimilarly drops the "Why run multiple agents" framing and the deletedoh harness addinvocation. (#233)- Gemini hidden from the docs sidebar and sitemap via
unlisted: trueindocs/agents/gemini.md(Docusaurus 3); page stays reachable by direct URL for old links. The home-page agent grid swaps the Gemini tile for Pi. (#233) - Stale files missed in #223:
.devcontainer/tests/cli-binaries.sh,.devcontainer/init-env.sh(orphan helper —install.shwrites.devcontainer/.envdirectly and docker compose loads it from that folder by default),.claude/posts/{linkedin,x}.md,tsconfig.base.json,.claude/plans/crispy-beaming-torvalds.md. Doc tree comments indocs/architecture/container-runtime.mdand.openharness/README.mdupdated to drop theinit-env.shreferences. (#228) - Pruned four orphaned files in
.openharness/left over from the v0.7 convergence:agents(broken symlink to deletedworkspace/.claude/agents/),agent/settings.json(referenced non-existentgpt-5.4model andlastChangelogVersion: 0.66.1; no loader onmain),banner.json(custom-banner skill deleted infb71365), andsettings.json(pointed at non-existent./.claude/skills/). Refreshed.openharness/.gitignoreheader and added.openharness/README.mddocumenting the remaining live file (config.json) and the dormant status ofCaddyfile/exposures.json. The directory's load-bearing role —composeOverridesconsumed byinstall.shand the container build — is unchanged. (#222) - Remove 0 eligible stale remote branches after critic-gated review; preserve all live remote branches because none met the >60-day deletion gate, and the backup branch still contains unique commits. (#213)
- Remove five stale or corrupted
.worktrees/agent directories and stale worktree registrations, reducing the ignored worktree footprint below 100 MB. (#213) - Remove the dead legacy install-side setup, entrypoint, and tmux wrapper scripts now that the Dockerfile wires
.devcontainer/entrypoint.shdirectly. (#213) - Remove the empty untracked
packages/web-ui/andpackages/slack/husks, plus the stale Slack workspace and entrypoint references. (#213) - Remove the obsolete on-disk SPEC v0.6 artifact so
.claude/specs/only exposes canonical tracked specs. (#213) - BREAKING:
oh harness adddeprecated;mifune(and any other harness pack) installs viagit clone+ manualcpper the pack's README for v1. The CLI-driven pack installer was deleted along withpackages/sandbox/in US-006; itsfetchGitPack(command-injection-prone) andregisterComposeOverlays(path-traversal-prone) plumbing is gone with it. A redesigned, security-first standalone installer (~50 LOC) tracked for v0.8 in #211: URL allowlisting, signature/SHA verification, sandboxed install, command-injection-safespawnSync, path-traversal-safe overlay resolution, atomic registry write. Per SPEC v0.7 §"Pack mechanism status in v0.7". (#210) - BREAKING: workspace template emptied of pi-coupled scaffolding. Deleted
workspace/HEARTBEAT.md,workspace/IDENTITY.md,workspace/MEMORY.md,workspace/SOUL.md,workspace/TOOLS.md,workspace/USER.md(six identity files belonging to the v0 multi-agent persona);workspace/heartbeats/(replaced by rootcrons/per US-002),workspace/.ralph/,workspace/.codexsymlink,workspace/.openharnesssymlink, andworkspace/memory/(instance state in template);workspace/.claude/skills/{compress,content-gate,eval-conciseness,quality-gate,wiki-ingest,wiki-lint,wiki-query,ralph,prd,ci-status}/(ten workspace skills);workspace/.claude/agents/{council,critic,implementer,pm,agent-builder,command-builder,rule-builder,skill-builder}.md(eight sub-agents); andworkspace/.claude/hooks/notify_slack.sh(Slack hook + its references inworkspace/.claude/settings.local.json). Rewroteworkspace/AGENTS.md(and theCLAUDE.mdsymlink) as a generic ~22-line agent-runtime stub. Kept:workspace/AGENTS.md,workspace/startup.sh,workspace/.claude/.example.env.claude,workspace/.claude/rules/{code-quality,git,token-conservation}.md,workspace/.claude/settings.local.json,workspace/.claude/screenshots/.gitkeep. The workspace template is now agent-runtime-agnostic; identity, skills, sub-agents, and routines come from a harness pack (e.g.@ryaneggz/mifune), not from this directory. Per SPEC v0.7 §"Harness Packs". (#210) - BREAKING: orchestrator skill set triaged to a defensible 4-skill core. Deleted
.claude/skills/{delegate,harness-audit,strategic-proposal,skill-lint,prd,ralph}/(six skill directories that mapped to v0 multi-agent / autonomous-agent flows). Converted.claude/skills/{provision,destroy,repair}/to plain Markdown documentation atdocs/operations/{provision,destroy,repair}.md(the operations are nowdocker composeinvocations, not skills). Surviving orchestrator skills:/release,/ci-status,/cloudflared-tunnel,/agent-browser. Stale heartbeat/daemon/worktree-discovery docs (docs/heartbeats/,docs/architecture/daemon.md,docs/architecture/worktrees.md) deleted alongside; incidentalworkspace/heartbeats/references indocs/guide/{configuration,workspace}.mdanddocs/agents/{overview,claude-code}.mdrewritten to point at the croner runtime +crons/. Pack-side counterparts forcloudflared-tunnelandagent-browsertracked inryaneggz/mifune#1and#2. Per SPEC v0.7 §"Orchestrator vs workspace agent skills". (#210) - BREAKING: multi-agent comparison narrative archived. Deleted the still-draft
blog/2026-04-29-worktree-per-agent.md; movedblog/2026-04-28-byoh.mdtoblog/archive/2026-04-28-byoh.mdwitharchived: truefrontmatter and a top-of-post note that v1 framing differs. Stripped the residual "side-by-side" wording fromdocs/agents/overview.md. Per SPEC v0.7 §"oh CLI" + §"Documentation source"; the v1 ICP statement at.claude/ICP.mdnow explicitly rejects multi-agent comparison-shopping framing as out-of-scope. (#210) - BREAKING:
@openharness/sandboxCLI removed;docker composeis the canonical substrate. Deleted thepackages/sandbox/workspace package (CLI binary + harness/onboard/lib/tools source + tests), the entry frompnpm-workspace.yaml, thelink:cliscript from rootpackage.json, the now-stale.github/workflows/ci.yml(sandbox-only CI), and the duplicate.github/workflows/build.yml(which had been pushing a driftedghcr.io/ryaneggz/open-harnessimage alongside the canonicalghcr.io/ryaneggz/openharnessproduced byrelease.yml). The CLI build/symlink block in.devcontainer/entrypoint.shthat wired/usr/local/bin/openharnessand/usr/local/bin/ohis gone.release.ymlis now the sole image producer. Per SPEC v0.7 §"oh CLI". (#210) - BREAKING: heartbeat daemon removed. Deleted
packages/sandbox/src/lib/heartbeat/(~1,752 LOC),packages/sandbox/src/cli/heartbeat-daemon.ts,packages/sandbox/src/cli/actions/heartbeat.ts,packages/sandbox/src/tools/heartbeat.ts, theoh heartbeat {start,stop,status}command group, theheartbeat-daemonpackage bin, the sevenheartbeat-*.test.tssuites, and.claude/skills/heartbeat/. Heartbeat env vars (HEARTBEAT_ACTIVE_START/_END/_AGENT) stripped from.devcontainer/docker-compose.yml; the legacy install-side daemon-startup blocks and the/usr/local/bin/heartbeat-daemonsymlink in.devcontainer/entrypoint.share gone;install/banner.shno longer advertisesheartbeat-daemon status. The croner runtime (scripts/cron-runtime.ts, US-002/US-003/US-004) is the canonical replacement per SPEC v0.7 §"Croner runtime". (#210) - BREAKING:
@mariozechner/pi-coding-agentis no longer a dependency. The pi agent CLI and Mom Slack bot have been extracted to a separate harness pack atryaneggz/mifune(npm:@ryaneggz/mifune). Install withoh harness add @ryaneggz/mifune. (#208)
Added
context/folder at repo root — orchestrator identity layer per the gist v0.6 spec §"context/" (https://gist.github.com/ryaneggz/8f6811efa144ac94d3cdc383846134d8), adapted for single-user harness scope. Four files:SOUL.md(voice and disposition, ~30 lines),IDENTITY.md(operating principles + append-only lessons learned, ~80 lines),TOOLS.md(environment inventory, ~40 lines),USER.md(working-relationship patterns, living document, ~45 lines).AGENTS.mdbootloader updated with a session-start read instruction;.claude/protected-paths.txtextended with the four new entries so future cleanup passes do not delete them. Implements the session-loaded identity layer the v0.7 convergence (PR #212, US-013) correctly removed fromworkspace/(pack territory) but never created at the orchestrator level. TheIDENTITY.mdlessons-learned section captures the meta-principles surfaced this session (revealed > stated preference, critic-gate before destructive actions, short feedback loops over speculative infrastructure). (#220).claude/protected-paths.txt— list of load-bearing skills, scripts, and config paths that critics must NOT propose deleting without an explicit override note in the finding's RECOMMENDATION column. Wired into.claude/agents/critic.md(general adversarial review) and.claude/skills/ship-spec/SKILL.mdstage 3 (PRD critic gate). Replaces the per-invocation usage-logging proposal (closed as #217) after Critic B's proportionality argument: zero runtime overhead, works on day 1, ~20 LOC vs. an 8-story PRD with hooks. The v0.7 convergence (PR #212, US-012) deleted six load-bearing skills under "defensible 5-skill set" rationale; this list is the gate that would have caught it. (#218)/ship-specv1.1: stages 3 and 5 reordered — critics run BEFORE the GH issue is opened (was: issue first, then critics). Earlier ordering created dangling issues when critics halted; now no GitHub-side state changes until the spec passes the critic gate. Surfaced during the dogfood run on issue #217. (#218)/ship-specorchestrator skill at.claude/skills/ship-spec/SKILL.md. Composes/prd→ 2 critics (parallel, asymmetric: implementer + user lens) → critique gate →/ralph→gh issue→ branch + push →gh pr create --draftinto one durable invocation. Critics write totasks/<slug>/critique.md; pipeline HALTs on any high-severity finding without mitigation. v1 stops at draft PR; loop launch + CI verification stay manual until the critic phase has been proven valuable across 2-3 real runs. Idempotent at every stage. (#215)- v1 Ideal Customer Profile statement at
.claude/ICP.md: primary persona (single working developer, one project, agent-tended), top three jobs-to-be-done, top three needs, and explicit anti-ICP (not multi-tenant SaaS, not non-developers, not production agent operators, not comparison-shopping tinkerers). (#210) - Seeded the two SPEC-reserved crons:
crons/heartbeat.md(hourly pulse,0 * * * *) andcrons/cleanup-tasks.md(Sunday 23:00,0 23 * * 0). Bodies follow SPEC v0.7 §"Croner runtime" + §"Weekly cleanup cron"; substrate stays empty otherwise so users add their own. (#210) .devcontainer/entrypoint.shnow startsscripts/cron-runtime.tsin asystem-crontmux session on container boot, replacing the legacyheartbeat-daemonwatchdog block. Idempotent (tmux has-sessionguard); restart policy is per-container-start per SPEC v0.7 §"Croner runtime". (#210)scripts/cron-runtime.ts— minimal croner-backed runtime (≤120 LOC) that readscrons/*.mdfrontmatter, schedules per-cron tasks, runs the body as a prompt toclaude -p(or$CRON_AGENT_BIN), logs tocrons/.cron.log, and uses a PID-lock atcrons/.pidto prevent double-firing. Replaces the 1,900-LOC heartbeat daemon per SPEC v0.7. (#210)- SPEC v0.7 amendments at
.claude/specs/structure-spec-v0.7.md— additive-only revision resolving seven v0.6 conflicts (canonical image name, docs path,ohCLI deprecation, croner runtime contract, hook execution mechanism, memory directory format, tasks vs.ralphpaths) and adding sections for harness packs, orchestrator-vs-workspace skill scope, and shrunk prose on Docusaurus config / eval flakiness / wiki-sync rubric / blog authorship / weekly cleanup. (#210) - README "Docker only (no installer)" section — concise compose-based deploy path for hosts that have only Docker + git.
- Per-page OpenGraph + canonical link tags via theme.config.tsx head function (#140).
- Blog post draft: Worktree-per-agent — stages for Wed publish (#145).
- Blog section at /blog and About page nav entry (#143).
- Blog post: BYOH — stop installing agent CLIs on your laptop (#144).
- Root
CHANGELOG.mdand Keep-a-Changelog workflow documented in.claude/rules/git.md;/releasenow promotes[Unreleased]to the new version section at tag time. - Generate
sitemap.xmlandrobots.txtduring docs build vianext-sitemap(#141). - Launch runbook consolidating manual cutover steps (DNS, GH settings, OG validation, GSC, promotion).
- Docusaurus v3 docs site at
apps/docsdeployed to oh.mifune.dev (#164).
Changed
- Consolidated cron runtime log onto
crons/.cron.log; the daemon no longer writescrons/cron-runtime.log. Both files were already gitignored undercrons/*.log. (#228) - Aligned the remaining flat-format memory references in tracked docs to the directory-per-day form per SPEC v0.7 §"Memory structure". Updated
docs/guide/workspace.md(tree comment + daily-logs paragraph),docs/guide/identity.md(file table + memory protocol step + how-it-works paragraph), anddocs/architecture/container-runtime.md(workspace tree comment) to usememory/YYYY-MM-DD/log.md. Flatmemory/<date>.mdfiles are deprecated. (#210) - BREAKING:
CLAUDE.mdrewritten fordocker compose-only orchestrator instructions. The Lifecycle (Setup/Validate/Teardown), Permissions, "What You Do", "Exposing apps", and Project Structure sections no longer referenceopenharness sandbox/list/shell/clean/heartbeatoroh expose/oh harness add; commands now usedocker compose -f .devcontainer/docker-compose.yml up -d --build,docker compose ps,docker exec -it -u sandbox openharness zsh, anddocker compose ... down -v. Skills table trimmed to the 4-skill triaged set (/release,/ci-status,/cloudflared-tunnel,/agent-browser); provision/destroy/repair are documented indocs/operations/. The orphanedpackages/sandbox/entry in the project-structure block is removed andscripts/+crons/are added..claude/skills/cloudflared-tunnel/SKILL.mdno longer referencesoh expose(the gateway relationship is now described via.claude/rules/gateway-routing.md);.claude/skills/ci-status/SKILL.mdupdated to the actual workflow nameCI: Harness. Per SPEC v0.7 §"oh CLI" + §"Orchestrator vs workspace agent skills". (#210) - BREAKING:
docs/installation.md,docs/onboarding.md,docs/sandbox-lifecycle.md, anddocs/connecting.mdrewritten to removeohCLI references. Installation is nowdocker compose -f .devcontainer/docker-compose.yml up -d --build; sandbox lifecycle usesdocker compose up/stop/down -v; connecting usesdocker exec -it -u sandbox <name> zsh. Onboarding is now a manual checklist run inside the sandbox (gh auth, claude, optional ssh / cloudflared / pack-supplied Slack). Deleted the entiredocs/cli/reference tree (overview, oh-sandbox, oh-shell, oh-clean, oh-onboard) and aligned strayoh expose/oh cleanmentions indocs/integrations/{cloudflare,github,postgres}.md,docs/architecture/{overview,container-runtime}.md, anddocs/guide/overlays.md. Per SPEC v0.7 §"oh CLI". (#210) - BREAKING:
README.md,docs/intro.md, anddocs/quickstart.mdrewritten for single-project framing. The "run Claude, Codex, Gemini, and Pi side-by-side" tagline is gone; the harness is now framed as a Docker-based agent harness for one project, agent-tended over time. Quickstart leads withdocker compose -f .devcontainer/docker-compose.yml up -d --build; allohCLI references in these three files are gone (multi-agent setups now point at the@ryaneggz/mifuneharness pack). Per SPEC v0.7 §"oh CLI". (#210) - BREAKING:
install.shrewritten as a docker-compose-only installer. Removed--cli,--with-cli,--install-nodeflags and the auto-detect / 3-waychoose_pathprompt; removeddetect_node,install_nvm, the nvm SHA-256 pin, thepnpm setup+pnpm link --global ./packages/sandboxblock, and the local-repopackages/sandbox/package.jsondetection at the repo-resolution step. The script now only checks Docker + git, clones / pulls into~/openharness, prepares host auth dirs (~/.claude,~/.codex,~/.claude.json), writes.devcontainer/.env, runsdocker compose -f .devcontainer/docker-compose.yml up -d --build, and prints next-steps usingdocker exec -it -u sandbox <name> zshanddocker compose ... down -v. Per SPEC v0.7 §"oh CLI". (#210) install.shcurl-pipe target renamed~/.openharness→~/openharnessso the cloned project no longer collides with the in-repo.openharness/config dir (~/.openharness/.openharness/was visually and semantically confusing). One-time migration: existing~/.openharnessclones aremv'd to~/openharnessautomatically when the new path is absent. In-container paths (/home/sandbox/.openharness) are unchanged. (#200)- Reverted the sandbox/orchestrator vocabulary realignment (#170, #172). The in-container Linux user is
sandboxagain (UID/GID 1000 preserved); theORCHESTRATOR_USER/ORCHESTRATOR_HOMEconstants are removed. The package name@openharness/sandbox, compose service, container default,SANDBOX_NAMEenv var,POSTGRES_USER, andsandbox_*Pi tool prefix were never changed.install/banner.shcarries an inverted migration guard for users coming back from a renamed image. (#198) - Consolidate documentation:
/docs/is now the single source of truth, served by Docusaurus fromapps/docs/viapath: '../../docs'. Enabled blog at/blog/. Removed the duplicateapps/docs/docs/tree. (#178) - Installer auto-detects Node 20+; new
--cli/--docker-only/--install-nodeflags;--with-clideprecated. (#176). - README and installation docs now use the short
https://oh.mifune.dev/install.shURL (302 redirect to the raw GitHub install script onmain) instead of the longraw.githubusercontent.comURL. /releasenow executes the[Unreleased]→[$VERSION]promotion (was prose-only) andrelease.ymlsources the GitHub Release body from the promoted CHANGELOG section viabody_pathinstead ofgenerate_release_notes, so the GitHub Release notes match the changelog byte-for-byte.- Revert prior secondary product name; "Open Harness" is the sole brand across README, docs, and onboarding (#157).
- Cloudflare onboarding step now requires an explicit public hostname (no default domain) (#157).
- Slim README to ~110 lines, lead with oh CLI flow (#139).
- Wiki promoted from
workspace/wiki/todocs/wiki/— same structure (pages/,sources/,index.md,log.md), now top-level alongside human-curated docs. - 26 docs pages converted from Nextra MDX to plain markdown rendered by GitHub.
Fixed
make sandboxandscripts/install.shnow expand.openharness/config.jsoncomposeOverridesinto-fflags so the default claude/codex/pi host-auth bind-mounts actually apply on a fresh install. Previously both paths ran the base compose file only, silently falling through to named-volume auth despite the documented intent. Also pre-creates~/.pialongside~/.claudeand~/.codexin the install-time host-auth-dir loop, and removes residual references to the deleted/provisionskill across docs and agent files.- Pi icon on the home-page agent grid no longer renders dim. Restored the inline
PiIconReact component pattern from #168 (commitfbda6a5) socurrentColorinverts cleanly between eggshell light and pure-black dark themes — the previous<img src="/img/agents/pi.svg">wiring sandboxed the SVG document and lost thecurrentColorinheritance.apps/docs/static/img/agents/pi.svgreverted tofill="currentColor"so the file stays theme-adaptive if loaded directly elsewhere. (#233) .claude/hooks/deny-env-dump.shno longer false-denies ongh ... --body "$(cat <<'EOF' ... EOF)"invocations whose prose contains words likehistoryor phrases likecat .env. Heredoc bodies are now stripped viaperl -0777before pattern matching (handles<<DELIM,<<'DELIM',<<"DELIM",<<-DELIM). The Tier-2ASKrule forecho|printfof any uppercase$VARwas also removed — too broad on benignecho "Branch: $BRANCH"patterns; the deny tier above already catches secret-named variables (TOKEN,SECRET,AUTH,KEY,GH_TOKEN,AWS_*, …)..claude/skills/ship-spec/SKILL.mdupdated to note heredoc bodies are now safe (was: "prefer--body-fileover heredoc"). (commit3ec98fe)- Correct the container runtime documentation to show Dockerfile
ENTRYPOINT ["entrypoint.sh"]invoking.devcontainer/entrypoint.shdirectly, withinstall/banner.shonly sourced for interactive shell status. (#213) - Restored six orchestrator skills removed in error during the v0.7 convergence (US-012, commit
fb71365):/ralph(PRD →prd.jsonconverter),/prd(PRD generator),/harness-audit(parallel-sub-agent codebase audit),/skill-lint(skill staleness scorer),/delegate(parallel execution coordinator), and/strategic-proposal(council-based roadmap planning). The triage rationale ("workspace-agent skills, not orchestrator") was wrong: the orchestrator actively uses theprd.jsonschema for its own task management (e.g.tasks/openharness-v07-convergence/prd.json), so removing the tools that produce that schema was a regression.scripts/ralph.sh(the runner) was kept all along — only the wrapper skills were lost. Restored fromfb71365^unchanged; CLAUDE.md skills table updated. install.shnow auto-disables the*-host.ymloverlays in.openharness/config.jsonwhen host UID ≠ 1000 (was: a multi-line warning telling the user to do it by hand). Mode-0600 credential files in~/.claude/~/.codex/~/.pican't be read across UIDs, and mode-0700 dirs (e.g.~/.pi/agent/sessions/) reject writes from non-owner UIDs — the sandbox user (UID 1000) would EACCES onoh/claude/codex/pi. The new logic usesjqto filterclaude-host.yml,codex-host.yml,pi-host.ymlout ofcomposeOverridesso the base named volumes take over. The original warning is retained as ajq-missing fallback. (#202)- Host auth (
~/.claude,~/.codex,~/.pi) is shared into the sandbox again.install.shnow pre-creates the three auth dirs and~/.claude.json(empty if missing) before sandbox bring-up so docker can't auto-create them root-owned, andclaude-host.yml/codex-host.yml/pi-host.ymlare restored to defaultcomposeOverrides. UID-mismatch hosts (UID ≠ 1000) get a loud install-time warning telling them to drop the overlays from.openharness/config.json(mode-0600 credential files can't be read across UIDs). Together this restores the convenience that was lost in #194 / #195 without re-introducing the EACCES failure mode that motivated those PRs. (#196) oh/claude/codexno longer fail withEACCES(or silent re-auth) on freshinstall.shinstalls. Removeddocker-compose.claude-host.yml,docker-compose.codex-host.yml, anddocker-compose.pi-host.ymlfrom the defaultcomposeOverridesin.openharness/config.json. Each overlay's own header documents that it requires host UID 1000 + the source dir to pre-exist; shipping them as defaults broke any host where either condition wasn't met (docker auto-creates missing source dirs as root → sandbox user (UID 1000) can't write). The base compose'sclaude-auth/codex-auth/pi-authnamed volumes now take effect by default;entrypoint.shchowns them to UID 1000 on first boot. Single-laptop developers wanting cross-sandbox auth sharing can opt back in by appending the relevant overlay(s) to.openharness/config.json. (#194)oh sandboxno longer fails on freshinstall.shinstalls with a Caddyfile bind-mount error. Removeddocker-compose.gateway.ymlanddocker-compose.cloudflared.ymlfrom the defaultcomposeOverridesin.openharness/config.json; both are additive overlays now loaded only when needed (oh exposere-adds the gateway viaSandboxConfig.addOverride()). (#192)install.shnext-steps now leads with a numbered "source your shell rc" step that detects$SHELL(bash→~/.bashrc,zsh→~/.zshrc,fish→~/.config/fish/config.fish) so users don't have to exit + reopen their shell to pick upoh/node/pnpm. Curl-piped install runs in a child bash whose exports cannot reach the parent shell — the source step is now Step 1 of next-steps (was a buried "Reminder").install.shbootstrapsPNPM_HOMEbeforepnpm link --globalso the CLI install no longer dies withERR_PNPM_NO_GLOBAL_BIN_DIRon fresh nvm-installed Node. Runspnpm setup(writesPNPM_HOMEexport to~/.bashrc+~/.zshrc) and also exportsPNPM_HOME/PATHinline so the link step succeeds in the current shell regardless of rc state.install.shno longer silently exits when sourcing a pre-existing~/.nvm/nvm.shunderset -euo pipefail. nvm + corepack calls are bracketed with relaxed strict mode and explicit$?checks, anERRtrap surfaces unexpected failures with the failing command and line, and pnpm is pinned to10.33.0(matchespackage.json#packageManager) instead of resolvingpnpm@latestover the network on every install.- Slack bot no longer drops oversized agent replies with cascading
msg_too_longerrors. Main message is capped at 2,900 chars with a_message truncated — full response in thread_footer; full content spills to thread replies;setWorking(false)always clears the working indicator. (#135)
Removed
- Removed legacy
/docs/{plans,wiki,launch-runbook,blog}/andapps/docs/docs/tree (consolidated into/docs/). (#178) - Nextra docs site and the
.github/workflows/docs.ymldeployment — documentation is now plain markdown indocs/, read in the GitHub UI. - Reference Next.js application at
workspace/projects/next-app/, along with its CI jobs (workspace/projects/next-apppaths inci.yml/release.yml) and the release pre-flight gate referencing it. - Root
package.jsonscripts:dev,docs:dev,docs:build,docs:preview.
Deprecated
Security
Release history prior to this file: see git tags and GitHub Releases. Most recent pre-changelog tag: 2026.4.22.