d9168de43e
Site 1 (config root): under memberHerdrSocket, writeConfig() now places the ephemeral opencode.json directory under worktreeRoot and shares it read-only with worktreeGroup, reusing EnvAllowListScrub#shareWithGroup (widened to package-private and generalized) — the same mechanism #213 built for the ZDOTDIR scrub, rather than a second copy. Unlike the ZDOTDIR scrub's degrade-to-overlay fallback, a missing worktreeRoot/worktreeGroup here REFUSES the spawn (IllegalStateException from buildLaunch): this file is the member's only way to learn where the bridge MCP is, so writing it somewhere unreadable would just produce an undeliverable member with no signal pointing at the cause. memberHerdrSocket absent stays byte-identical. Site 2 (discovery root): under memberHerdrSocket, agentSessionId() now declares session discovery unavailable and logs one WARN per launcher instance instead of silently scanning fleetd's own $HOME (opencode.db lives under the MEMBER's home under this config key). Decision + reasoning for why this is a declare-unavailable rather than a new config key is in defaultDiscoveryRoot()'s javadoc. Widened HerdrPeerLauncher#memberHerdrSocketConfigured/memberScrubParentDir/ memberGroup to package-private so OpenCodeLauncher reuses the exact same config resolution rather than re-deriving it. Same-shape finding (not fixed, out of scope): ClaudeCodeLauncher#writeCharterFile (line ~465) writes the role-charter temp file via Files.createTempFile with no directory argument, i.e. under java.io.tmpdir — the same site-1 shape, unfixed for the Claude Code adapter.