Skip to content

refactor(runtime): move headless permission setup into the agent adapter - #55

Open
FreshlyBrewedCode wants to merge 4 commits into
35-move-chunk-interpretation-into-adapterfrom
37-move-headless-permissions
Open

FreshlyBrewedCode wants to merge 4 commits into
35-move-chunk-interpretation-into-adapterfrom
37-move-headless-permissions

Conversation

@FreshlyBrewedCode

@FreshlyBrewedCode FreshlyBrewedCode commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

Part of #32 · Closes #37

allocateWorkspace and resetClone write opencode.json (permission: {"*": "allow"}) into every run tree — the fix for #24, a real production deadlock where a headless opencode serve parks a turn on a permission ask nobody can answer. That policy is entirely opencode's business; a git-shaped workspace allocator has no way to know it exists and no business knowing it. ADR 0012 §3 draws the line: the agent runtime prepares its own workspace, and lib/workspace.ts goes back to being about git.

What changed

  • AgentAdapter gains prepareWorkspace(dir): Promise<void>; opencodeAdapter implements it by calling writeHeadlessPermissions (unchanged: still writes opencode.json and registers it in .git/info/exclude so writeBack never stages it). The replay/slow-fake test adapters no-op.
  • startRun calls adapter.prepareWorkspace(dir) once, before workflow.run, gated by a new prepareWorkspace?: boolean option — so it runs before the first agent step regardless of which adapter is wired in.
  • lib/workspace.ts and lib/clone.ts no longer import sandbox-config; allocateWorkspace's existsSync guard (only needed because workspace.test.ts's injected fake exec spawns nothing and leaves dir absent) is gone with the responsibility it was guarding.
  • The flag is threaded to cover both clone paths: the daemon sets it when startTrackedRun itself ran allocateWorkspace (workspaceAllocated && kind === "clone"); the CLI and the legacy HTTP body.dir + body.clone path set it explicitly after calling resetClone. Scratch workspaces never set it, matching current behaviour (the pre-refactor code only ever wrote the config past the scratch early-return).
  • sandbox-config.test.ts now drives opencodeAdapter.prepareWorkspace directly instead of going through resetClone; workspace.test.ts's now-obsolete opencode.json assertion is removed since that's no longer allocateWorkspace's job; run.test.ts adds coverage for both the true and absent prepareWorkspace cases.

Notes for reviewers

Verification

  • Tests added/updated: sandbox-config.test.ts (adapter-level), run.test.ts (prepareWorkspace true/absent), inline adapter fixtures across agent-step.test.ts/agent-step.usage.test.ts/replay/adapter.ts updated for the new interface member.
  • CI (gh pr checks 55): check (format + lint + typecheck + bun test) passes, lint-title passes. I did not re-run bun run check locally; relying on CI's green result.

Stack

  1. refactor(cli): replace hand-rolled parsers with effect/unstable/cli #52 — Parse CLI with Effect CLI (issue Parse CLI arguments with effect/unstable/cli #33)
  2. refactor(errors): represent domain failures as Schema.TaggedError #53 — Domain errors as tagged errors (issue Represent domain failures as Schema.TaggedError #34)
  3. refactor(runtime): move chunk interpretation into the agent adapter #54 — Move chunk interpretation into adapter (issue Move chunk interpretation into the agent adapter #35)
  4. refactor(runtime): move headless permission setup into the agent adapter #55 — Move headless permission setup into the agent adapter (issue Move headless permission setup out of the workspace allocator #37) ← this PR
  5. feat(runtime): add Effect composition root and agent runtime service #56 — Agent runtime service (issue Add an Effect composition root and make the agent runtime a service #36)
  6. refactor(daemon): move singletons into per-daemon layers #57 — Move singletons into layers (issue Move the daemon's remaining singletons into layers #38)

Stack created with GitHub Stacks CLI • Give Feedback 💬

@FreshlyBrewedCode
FreshlyBrewedCode added this pull request to stack #58 September 20, 2026 13:00
@FreshlyBrewedCode FreshlyBrewedCode changed the title 37 move headless permissions refactor(runtime): move headless permission setup into the agent adapter Sep 20, 2026
@FreshlyBrewedCode
FreshlyBrewedCode force-pushed the 37-move-headless-permissions branch from 5afc474 to b64a9be Compare September 20, 2026 13:15
FreshlyBrewedCode added a commit that referenced this pull request Sep 23, 2026
A 0-byte .tanstack-projected-* file under a stray data/src/factory/...
path leaked into 2f97451 from a sandboxed run writing through an
absolute path that mirrored the checkout. .gitignore already covers
.tanstack-projected-* and data/ (added in PR #55), which is why
nothing flagged it since; git rm drops it and its now-empty parent
dirs. No other stray data/ or .factory/workspaces/ paths are tracked
on this branch.

Part of #32
FreshlyBrewedCode and others added 4 commits September 23, 2026 06:58
ADR 0012 §3: the adapter prepares its own workspace. The opencode adapter
writes opencode.json with the never-ask permission policy (#24). Test
adapters (corpus replay, slow fake) no-op.

Co-Authored-By: opencode-go/qwen3.7-plus <noreply@opencode.ai>
ADR 0012 §3: workspace preparation is the agent runtime's responsibility.
Called for clone workspaces only — scratch has no git tree to prepare.
Covers both the per-run allocateWorkspace path and the legacy resetClone
path, since both reach startRun.

Co-Authored-By: opencode-go/qwen3.7-plus <noreply@opencode.ai>
…e allocator (#37)

lib/workspace.ts and lib/clone.ts no longer import or call
writeHeadlessPermissions. The existsSync guard in allocateWorkspace
(needed only for the injected-fake-exec test) is gone along with the
responsibility.

The opencode adapter now owns workspace preparation via prepareWorkspace
(ADR 0012 §3), called by startRun when the caller signals a clone
workspace was provisioned. The daemon sets the flag when allocateWorkspace
ran; the CLI/HTTP legacy paths set it when resetClone ran.

sandbox-config.test.ts now tests opencodeAdapter.prepareWorkspace
directly instead of going through resetClone.

Co-Authored-By: opencode-go/qwen3.7-plus <noreply@opencode.ai>
…k artifacts (#37)

The signalAdapter helper in agent-step.test.ts was missing the new
prepareWorkspace method required by the AgentAdapter interface. Also
gitignore .tanstack-projected-* and data/ created by sandbox tooling.

Co-Authored-By: opencode-go/qwen3.7-plus <noreply@opencode.ai>
@FreshlyBrewedCode
FreshlyBrewedCode force-pushed the 37-move-headless-permissions branch from b64a9be to cdf760e Compare September 23, 2026 07:08
FreshlyBrewedCode added a commit that referenced this pull request Sep 23, 2026
A 0-byte .tanstack-projected-* file under a stray data/src/factory/...
path leaked into 2f97451 from a sandboxed run writing through an
absolute path that mirrored the checkout. .gitignore already covers
.tanstack-projected-* and data/ (added in PR #55), which is why
nothing flagged it since; git rm drops it and its now-empty parent
dirs. No other stray data/ or .factory/workspaces/ paths are tracked
on this branch.

Part of #32

This branch has not been deployed

No deployments
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.

Move headless permission setup out of the workspace allocator

1 participant