Skip to content

fix(config,cli,skillsource): honor a relocated harness config home - #234

Open
NiklasEi wants to merge 3 commits into
TNG:mainfrom
NiklasEi:fix/harness-config-home-redirect
Open

fix(config,cli,skillsource): honor a relocated harness config home#234
NiklasEi wants to merge 3 commits into
TNG:mainfrom
NiklasEi:fix/harness-config-home-redirect

Conversation

@NiklasEi

@NiklasEi NiklasEi commented Aug 14, 2026

Copy link
Copy Markdown
Member

Issue: Closes #233

What

  • Claude Code's config-home override now works: HomeEnv was declared as
    CLAUDE_HOME, a name nothing reads (CLAUDE_CONFIG_DIR is the real one).
  • Every harness's HomeEnv is forwarded into the sandbox, and the sandbox grants
    follow the resolved config home instead of the default one.
  • Global skill discovery follows a relocated config home — and now also finds
    pi's and codewhale's config-home skills dirs, which it never scanned.
  • OpenCode's HomeEnv is removed: OPENCODE_HOME was omac's invention too,
    and OpenCode has no config-home override to honor.

Why

Users relocate a harness's config home to keep separate logins side by side
(~/.claude personal, ~/.work-claude work). omac claimed to model this but
honored it nowhere the harness could see, so the redirect was silently
ignored
: omac resolved every path from the default home, and the sandboxed
harness was never told the redirect existed. With both homes present the user
gets the wrong login; with only the redirect populated, claude finds no
credentials in ~/.claude and prompts a fresh login.

The one profile shape where the var survived — the sanctioned
allow_vars: ["*"] — broke the other way: the harness read the relocated home,
which was never granted, so the sandbox denied it.

How

  1. 5010e06 — config + launch path. ConfigHome() normalizes the value, so a
    different spelling of the default home is not read as a redirect.
    ResolvedSandboxDirs() swaps the SandboxDirs entry naming the default config
    home — appending instead when no entry names it (pi declares ~/.pi, its home
    is ~/.pi/agent). ForwardedEnvVars() adds HomeEnv generically
    (internal/cli/serve.go:713); that forwarding is what makes the swap sound,
    since granting the relocated home while the harness reads the default one is
    worse than not swapping at all.
  2. fc94e08 — discovery. withConfigHomeRoot substitutes the redirected
    skills root for the default one in place, so a redirect moves which config
    home is scanned without reordering the rest of the ladder. It also makes
    GlobalSkillsDir() always a candidate, closing a pre-existing gap: pi and
    codewhale are omac setup targets discovery never looked in.
  3. b5d2a45 — opencode. OPENCODE_CONFIG_DIR only adds a config-search dir
    (docs) and credentials live
    outside it, so it is no HomeEnv: declaring it would swap ~/.config/opencode
    out of the grants while OpenCode kept reading it. The field doc now carries the
    general rule — no upstream variable ⇒ leave HomeEnv empty.

Test isolation came along by necessity: tests fake $HOME, but an override names
an absolute path and survives that, so an ambient CLAUDE_CONFIG_DIR pointed them
at the developer's real config home — 9 failures locally, 0 in CI. A TestMain
per package clears them via the new config.HomeEnvNames().

Verification

Linux, go1.26.6. Every CI lint gate, run locally:

gofmt -l .                               # clean
go vet ./...                             # clean
staticcheck ./...                        # clean
python3 scripts/check-docs.py            # ok
python3 scripts/check-workflow-shell.py  # ok
go test ./...                            # same failure set as main
  • No regressions: go test ./... fails the same 51 tests as main
    pre-existing loopback/network artifacts of the local sandbox in cli, facade,
    intent, netprompt/origin, netproxy, sandboxrun, updater — confirmed by
    diffing both failure lists.
  • Manual: a build with CLAUDE_CONFIG_DIR set reuses that home's login
    instead of prompting — which also confirms it is the name Claude Code reads.
  • Not run: go mod verify (blocked on proxy.golang.org locally) and the
    e2e matrix. This touches launch argv and macOS/Seatbelt is untested, so
    e2e.yml is the gate.

Every HomeEnv name audited

Harness Declared Evidence
claude-code CLAUDE_CONFIG_DIR 147 occurrences in installed claude 2.1.228 (CLAUDE_HOME: 0)
opencode (none, this PR) OPENCODE_HOME: 0 occurrences; OPENCODE_CONFIG_DIR is additive only
pi PI_CODING_AGENT_DIR 3 occurrences in pi-coding-agent/dist/cli.js
codex CODEX_HOME openai/codex, codex-rs/utils/home-dir/src/lib.rs: find_codex_home() — "the Codex configuration directory, which can be specified by the CODEX_HOME environment variable […] defaults to ~/.codex"
copilot COPILOT_HOME github/copilot-cli changelog: "--config-dir is deprecated in favor of COPILOT_HOME"
codewhale CODEWHALE_HOME Hmbown/CodeWhale, crates/paths/src/lib.rs: codewhale_home_override(); codewhale_home_is_explicit() documents it as an "explicit isolation boundary"

Three upstream divergences surfaced by the audit — all narrow, none blocking:

  • codex canonicalizes CODEX_HOME (resolves symlinks) and errors if it does
    not exist; normalizeHomePath only cleans and absolutizes. A symlinked value
    means omac grants the link path while codex uses the target.
  • codewhale rejects a relative CODEWHALE_HOME; omac absolutizes it against
    its own working directory instead.
  • copilot stops loading ~/.agents/skills when COPILOT_HOME is set, but omac
    keeps the shared agents root in copilot's scope — the same discovery/harness
    mismatch this PR fixes for claude, in a spot it does not reach.

Follow-up

  • Document it. docs/CONFIGURATION.md has no harness-config-home section —
    CLAUDE_CONFIG_DIR and the per-harness overrides belong there, together with
    the behavior change that a redirect hides skills installed under the
    default home. Worth a docs/ commit on this branch or a separate PR.
  • Symlinked / relative override values. normalizeHomePath cleans and
    absolutizes but does not resolve symlinks, so it can disagree with codex
    (canonicalizes) and codewhale (rejects relative values). Worth a follow-up issue
    rather than widening this PR.
  • copilot + the shared agents skills root. COPILOT_HOME makes copilot stop
    reading ~/.agents/skills while omac still treats it as in scope for copilot.

🤖 Generated with Claude Opus 5

…eEnv

🤖 Generated with Claude Opus 5

Signed-off-by: Niklas Eicker <niklas.eicker@tngtech.com>
🤖 Generated with Claude Opus 5

Signed-off-by: Niklas Eicker <niklas.eicker@tngtech.com>
🤖 Generated with Claude Opus 5

Signed-off-by: Niklas Eicker <niklas.eicker@tngtech.com>
@NiklasEi
NiklasEi force-pushed the fix/harness-config-home-redirect branch from 5af6366 to b5d2a45 Compare August 14, 2026 12:58
@NiklasEi
NiklasEi marked this pull request as draft August 14, 2026 12:58
@NiklasEi
NiklasEi marked this pull request as ready for review August 14, 2026 13:11
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.

A relocated harness config home (CLAUDE_CONFIG_DIR) is silently ignored on both sides of the sandbox

2 participants