Skip to content

relay#586: adopt a terminal Claude in no project, unsandboxed - #592

Merged
barelyworkingcode merged 7 commits into
epic/585-projectless-adoptfrom
feat/586-projectless-adopt
Oct 22, 2026
Merged

barelyworkingcode merged 7 commits into
epic/585-projectless-adoptfrom
feat/586-projectless-adopt

Conversation

@barelyworkingcode

Copy link
Copy Markdown
Owner

Closes #586

Adopt a terminal Claude session that is in no project. It resumes headless in its original folder with no sandbox, no project policy and no allowed-models check, and every session, terminal and audit row says so. Child of #585; targets epic/585-projectless-adopt.

Non-obvious decisions

  • authorizeUnsandboxedLaunch (cmd/relay/session_unsandboxed.go) is the only builder for a launch with no project. Only adopt, resume and drop-in call it; resume and drop-in reach it only from a ledger record marked unsandboxed. AuthorizeLaunch is unchanged and still refuses project_required.
  • The session host takes the mark only on a local claude launch or a pty drop-in with no project, sandbox or identity. It refuses an unmarked local claude launch with no project (400 invalid_spec). No e2e door reaches the host's internal API; the reviewer checked it.
  • The drop-in terminal's mark lives on the terminal manager's slot (Manager.CreateUnsandboxed), not on terminal.CreateSpec. The wire result is the same.
  • With claude_token_file set, an unsandboxed Claude gets the token on fd 5 through the shim (owner decision), and relay session mode answers resume_required. With no token it restarts in place.

Approved behaviour changes (not weakening)

  • G1.50 drops no_project from the Claude reasons and points to the new row G1.54. G1.45 now says only a Claude adopted in no project gets the token unsandboxed.
  • e2e/features/g1_outside_adopt_test.go: the no project case of TestAdoptReasonsOnRows now expects adoptable: true.
  • fakerelay/prooftest/adopt_test.go: TestAdoptRowFieldsServed, TestAdoptFixtureRefused and TestAdoptRefusals now carry the new rule. The old no project refusal cases are replaced, because a world can no longer express them.
  • cmd/devboxverify/journey_adopt.go: runAdopt becomes adoptFlow(ctx, e, unsandboxed). session-adopt keeps every check and its timeout.

Contract amendments (planner, logged on #586)

internal/sessions/hostapi/server.go (the launchTerminal create call), docs/events.md (the reason lists), fakerelay/internal/sessions/register.go (launchTarget.unsandboxed).

Minor review notes (not fixed)

  • fakerelay/internal/sessions/terminal.go:218: findTemplate's doc comment sits above consoleTemplate.
  • TestAdoptNoProjectTokenDescriptor asserts exit 1 for the mode refusal, not the resume_required code. The CLI prints only the message text, the same pattern as g1_terminals_test.go.
  • Theoretical (on the run report): a symlinked registry folder resolving into a project; an empty project object at the host.

Proof

  • e2e suite on 80ce73b: ok relaye2e/features 862.263s (baseline on 60f4286: ok 848.493s).
  • --stack --phase api on 80ce73b: SUMMARY pass=10 fail=0 blocked=0 notrun=2; session-adopt and session-adopt-unsandboxed PASS.
  • fakerelay go test ./... green.

Fix attempts: one review round (seven minors, one ownership blocker), plus one journey fix (the Chief of Staff send needed a console). Tokens: about 1.2M across subagents.

Review: no blocking findings (round 2, default model)
Journey: session-adopt-unsandboxed PASS on stack r586, head 80ce73b (log /tmp/c585/r586-dbv-head4.log)
Harness weakening: none; session-adopt keeps all its checks and its 240 s timeout
Main harness vs head: main's devboxverify (60f4286) against head 80ce73b on stack r586: SUMMARY pass=9 fail=0 blocked=0 notrun=2
Break-and-restore: dropping no_project from the session_adopt args turned TestAdoptNoProjectAuditRows red, and on a stack session-adopt-unsandboxed FAIL "a session_adopt row lacks no_project: true (intent)"; both green after the restore
Weakening reviewed: prooftest no_project refusal cases and the e2e no-project expectation replaced by the approved rule change in #585/#586; no assertion loosened

🤖 Generated with Claude Code

barelyworkingcode and others added 7 commits October 22, 2026 10:23
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dential

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nup, prooftest case

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@barelyworkingcode
barelyworkingcode merged commit fa33abf into epic/585-projectless-adopt Oct 22, 2026
@barelyworkingcode

Copy link
Copy Markdown
Owner Author

merge-check: merged. approved, target, contract, evidence, owner, weakening

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.

1 participant