You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Prove bidirectional sharing of the user's Herdr session for parent plan #466, then write that contract into docs/herdr-integration.md. Task 1 (#470 on task-1-attach-user-session) already attaches to the default socket and proves HerdrClient::workspace_create is visible in herdr api snapshot on an isolated XDG. Task 2 (#472 on task-2-version-policy) already accepts protocol >= 22, prefers PATH herdr, and surfaces herdr update. What is still missing is the parent-plan e2e: a workspace/pane created through 2code's runtime path (connect_gui_herdr / RuntimeRouter / create_with_runtime / create_session) must show up in the same server's session.snapshot / herdr api snapshotandherdr workspace list; and the reverse — a workspace the user creates with the herdr CLI must appear in 2code's derived profile list (list_with_runtime). Record the exact commands and sanitized output in the docs, and replace leftover production claims of a dedicated 2code namespace / never the user default session with that sharing contract.
Same clicks and labels. Seeing the user's real workspaces (join-keyed to the open project) is the point of this phase, not a redesign. This task is prove + document; do not change socket resolution, version policy, or the sidecar pin unless a live proof finds a real sharing hole Task 1/2 missed, and then fix only that hole.
Herdr is the profile authority. sqlite profiles is not source of truth (the table is DROPped). projects / project_groups / checkout_notes stay in 2code sqlite. Default-runtime switch and deleting Local/PTY/profiles rows already shipped in #436 — do not re-litigate or re-add them. Never let Local and Herdr own the same worktree (Local is gone; do not bring it back). Do not change interaction. Reuse the existing socket client, snapshot/event sync, frame bridge, and worktree/pane RPCs — this task proves which session 2code already talks to, not a new protocol layer.
This is #466 Task 3, not #394 Task 3 (#404), not #436 Task 3 (#441), not #458 Task 3 (#461), and not #463 Task 3 (#467). Do not implement or close those tickets here.
Dependencies: Parent plan #466. Prerequisites: #470 (Task 1, accepted on task-1-attach-user-session), #472 (Task 2, accepted on task-2-version-policy). Open #471 and #473 are line-count tracking only — not blockers and not work for this branch. Open task tickets from #394 / #436 / #458 / #463, leftover gaps (#396, #397, #398, #399, #401), and line-count trackers are out of scope.
Work to complete
Forward: a 2code runtime workspace/pane is visible to the user's herdr CLI
Use the real pinned Herdr v0.9.0 binary. Isolate via fixture XDG_CONFIG_HOME so the proof's default socket is $fixture_xdg/herdr/herdr.sock, not the developer's ~/.config/herdr/herdr.sock. Do not retarget this onto the host's real session. Probe /tmp/2c*.sock isolation remains test isolation, not a production private 2code namespace.
Create a sqlite projects row for a disposable folder, then start a workspace and a pane the way the GUI does: create_with_runtime and/or adopt_existing_checkouts for the workspace (worktree.create / worktree.open / workspace.create --cwd), then create_session (tab.create) for the pane. Returned ids stay workspace_id / pane_id.
On that same default socket, with noHERDR_SESSION=2code and nosessions/2code/ path, show the workspace/pane in:
JSON session.snapshot
CLI herdr api snapshot
CLI herdr workspace list (if the v0.9.0 argv differs, record the actual command; JSON workspace.list is not a substitute for the CLI proof the parent named)
Quote the exact commands and a sanitized snippet (temp paths → /tmp/…) in the implementation reply and in the docs section below. Lease/GUI drop still leaves that default socket live (server stop still absent from exit).
Reverse: an externally created workspace appears in 2code's derived list
On the same isolated default session, create a workspace outside 2code with the user's CLI (herdr workspace create --cwd <folder>, or the v0.9.0 equivalent) pointing at a folder that is a sqlite projects.folder.
list_with_runtime (live read, notadopt_existing_checkouts) must return that workspace_id as a derived profile for that project. Do not sqlite-fill. Join key still applies: only that project's folder, not every session workspace. Git folders join via worktree.listopen_workspace_id; non-git via snapshot pane cwd. One disposable folder is enough; pick the join path that matches it.
Quote the exact CLI create + herdr workspace list / herdr api snapshot commands and the sanitized list_with_runtime ids in the reply and docs. A second, private server must not appear (sessions/2code/ absent, starts stay on the default socket).
Write the sharing contract; lock leftover private-namespace claims
Add a Sharing contract section to docs/herdr-integration.md: one Herdr server, shared; 2code attaches to the session the user's herdr would (Task 1 resolution order); never a private 2code session; never herdr server stop on GUI exit; protocol >= 22 / herdr update (Task 2); bidirectional visibility with the recorded commands/output from steps 1–2.
Replace production current-state sentences that still imply a dedicated 2code namespace or that 2code never uses the user default session. The contract-probe dump line that says the dump uses a fixture socket, never the host's real default session, stays — reword it so it cannot be read as production policy.
docs/architecture.md / docs/configuration.md / AGENTS.md / CLAUDE.md twins only if they still contradict sharing. Extend docs_client_mode.rs so the sharing-contract section cannot regress to "dedicated 2code namespace" as live production text.
Do not retarget herdr_contract.rs onto the developer's ~/.config/herdr. Do not change pin.json / sidecar pin / protocol floor / PATH preference. Do not add a Settings health page or change New Profile / New Tab / Git / sidebar labels.
If line count grows past the estimate, open a tracking issue; this plan has no Task 4 to fold into this branch.
Likely files and directories
New/update live tests: a service-level live test (prefer src-tauri/crates/service/src/runtime.rs or src-tauri/crates/service/tests/) that drives connect_gui_herdr / RuntimeRouter / create_with_runtime / create_session / list_with_runtime against the pinned binary on an isolated default socket. Keep Task 1's transport-level snapshot test; do not treat it as this proof.
Estimated changes: 200–400 lines total, additions plus deletions, including tests and docs; excluding generated files, lockfile churn and binary artifacts. Material scope growth (unpinning the sidecar, retargeting the contract probe onto the host session, leftover capability gaps) should become a separate issue.
Acceptance criteria
Forward proof on an isolated default socket: a workspace and pane created through 2code's runtime path (RuntimeRouter / create_with_runtime and/or adopt + create_session, not only HerdrClient::workspace_create) appear in session.snapshot, herdr api snapshot, and herdr workspace list. No HERDR_SESSION=2code, no sessions/2code/. The implementation reply quotes the commands and a sanitized snippet.
Reverse proof on that same session: a workspace created with the user's herdr CLI against a sqlite projects.folder appears in list_with_runtime as that workspace_id. Live read, not adopt-to-invent. Join key still filters by project folder. Commands and sanitized list ids are quoted in the reply and docs.
docs/herdr-integration.md has a Sharing contract section with those commands/output. Production docs no longer say 2code uses a dedicated 2code namespace and never the user default session. Contract-probe notes may still isolate a fixture socket from the host's real default. docs_client_mode.rs locks the new contract text.
GUI/lease drop leaves the shared server running. No private session spawn. Sidecar pin remains v0.9.0. Protocol floor / PATH preference / herdr update copy stay as Task 2 left them.
No behavior change beyond proving and documenting sharing (and fixing a hole the live proof actually finds). sqlite profiles is not source of truth. projects still round-trip in sqlite. No Local adapter. No second events.subscribe. lib.rs still does not name HerdrRuntimeSync. Join key still filters profiles by project folder.
Named UX exceptions: none new. Existing Herdr-backed exceptions stay (route ids workspace_id; tab ids pane_id; splits flattened as extra tabs; non-git New Profile is folder workspace.create; Herdr-down New Tab errors). Showing join-keyed user workspaces in the sidebar is the phase goal, not a click/label redesign.
Tests: cargo test --workspace from src-tauri (or cargo test --workspace --exclude code where GTK is unavailable), including the new live sharing tests when the pinned binary is present (HERDR_SIDECAR_REQUIRED=1 must not skip them into a green run). Frontend lint/typecheck/tests only if frontend scope is introduced (none expected). bun run typegen only if IPC signatures change (none expected); do not hand-edit src/generated/.
No push. No pull request.
Out of scope
Parent-plan Task 1 (socket/session switch) and Task 2 (version policy / PATH herdr / herdr update); already done on this stack.
Retargeting the sidecar pin, or the contract probe onto the developer's real ~/.config/herdr session.
Reintroducing a Local adapter, Settings runtime toggle, portable-pty spawn, or sqlite profiles / session-table authority.
Never start a private session. 2code as a session name, sessions/<name>/herdr.sock used as 2code's production socket, and the default-socket refusal stay deleted. Nothing in 2code may spawn a server on a socket the user's own herdr cannot see.
Never stop the user's server. GUI exit, disconnect, crash and update must not issue herdr server stop or kill the shared server process.
Fail with an actionable message, never with silence. Old/absent server copy stays as Task 2; this task must not paper over an old server by spawning a private instance.
Do not change interaction. Same clicks and labels. If a label genuinely cannot survive, name the exception in this issue (none new).
Reuse the code that already works — socket client, snapshot/event sync, frame bridge, worktree and pane RPCs.
Do not treat sqlite profiles as source of truth. projects stay in 2code sqlite.
Never let Local and Herdr own the same worktree (Local is already removed; do not bring it back).
One task; stack on task-2-version-policy. Do not push. Do not open a PR.
Why this is next
#466 has three tasks. Task 1 (#470) and Task 2 (#472) are accepted on task-2-version-policy. Task 3 is the remaining bidirectional e2e proof plus sharing-contract docs rewrite that both earlier tickets explicitly deferred. The leftover open gaps (#396/#397/#398/#399/#401) and #471 / #473 are Linux-unverifiable leftovers or line-count tracking, not in-flight replacements. #404 is #394 Task 3. #441 is #436 Task 3. #461 is #458 Task 3. #467 is #463 Task 3.
Summary
Prove bidirectional sharing of the user's Herdr session for parent plan #466, then write that contract into
docs/herdr-integration.md. Task 1 (#470 ontask-1-attach-user-session) already attaches to the default socket and provesHerdrClient::workspace_createis visible inherdr api snapshoton an isolated XDG. Task 2 (#472 ontask-2-version-policy) already accepts protocol >= 22, prefers PATHherdr, and surfacesherdr update. What is still missing is the parent-plan e2e: a workspace/pane created through 2code's runtime path (connect_gui_herdr/RuntimeRouter/create_with_runtime/create_session) must show up in the same server'ssession.snapshot/herdr api snapshotandherdr workspace list; and the reverse — a workspace the user creates with theherdrCLI must appear in 2code's derived profile list (list_with_runtime). Record the exact commands and sanitized output in the docs, and replace leftover production claims of a dedicated2codenamespace / never the user default session with that sharing contract.Same clicks and labels. Seeing the user's real workspaces (join-keyed to the open project) is the point of this phase, not a redesign. This task is prove + document; do not change socket resolution, version policy, or the sidecar pin unless a live proof finds a real sharing hole Task 1/2 missed, and then fix only that hole.
Herdr is the profile authority. sqlite
profilesis not source of truth (the table is DROPped).projects/project_groups/checkout_notesstay in 2code sqlite. Default-runtime switch and deleting Local/PTY/profilesrows already shipped in #436 — do not re-litigate or re-add them. Never let Local and Herdr own the same worktree (Local is gone; do not bring it back). Do not change interaction. Reuse the existing socket client, snapshot/event sync, frame bridge, and worktree/pane RPCs — this task proves which session 2code already talks to, not a new protocol layer.This is #466 Task 3, not #394 Task 3 (#404), not #436 Task 3 (#441), not #458 Task 3 (#461), and not #463 Task 3 (#467). Do not implement or close those tickets here.
Dependencies: Parent plan #466. Prerequisites: #470 (Task 1, accepted on
task-1-attach-user-session), #472 (Task 2, accepted ontask-2-version-policy). Open #471 and #473 are line-count tracking only — not blockers and not work for this branch. Open task tickets from #394 / #436 / #458 / #463, leftover gaps (#396, #397, #398, #399, #401), and line-count trackers are out of scope.Work to complete
Forward: a 2code runtime workspace/pane is visible to the user's
herdrCLIXDG_CONFIG_HOMEso the proof's default socket is$fixture_xdg/herdr/herdr.sock, not the developer's~/.config/herdr/herdr.sock. Do not retarget this onto the host's real session. Probe/tmp/2c*.sockisolation remains test isolation, not a production private2codenamespace.HerdrClient::workspace_create(that one-direction snapshot check already exists aslive_created_workspace_is_visible_on_default_socket). Wireconnect_gui_herdr/RuntimeRouteron that fixture XDG (testGuiHerdrConnect.sidecar/ isolated PATH as today).projectsrow for a disposable folder, then start a workspace and a pane the way the GUI does:create_with_runtimeand/oradopt_existing_checkoutsfor the workspace (worktree.create/worktree.open/workspace.create --cwd), thencreate_session(tab.create) for the pane. Returned ids stayworkspace_id/pane_id.HERDR_SESSION=2codeand nosessions/2code/path, show the workspace/pane in:session.snapshotherdr api snapshotherdr workspace list(if the v0.9.0 argv differs, record the actual command; JSONworkspace.listis not a substitute for the CLI proof the parent named)/tmp/…) in the implementation reply and in the docs section below. Lease/GUI drop still leaves that default socket live (server stopstill absent from exit).Reverse: an externally created workspace appears in 2code's derived list
herdr workspace create --cwd <folder>, or the v0.9.0 equivalent) pointing at a folder that is a sqliteprojects.folder.list_with_runtime(live read, notadopt_existing_checkouts) must return thatworkspace_idas a derived profile for that project. Do not sqlite-fill. Join key still applies: only that project's folder, not every session workspace. Git folders join viaworktree.listopen_workspace_id; non-git via snapshot panecwd. One disposable folder is enough; pick the join path that matches it.herdr workspace list/herdr api snapshotcommands and the sanitizedlist_with_runtimeids in the reply and docs. A second, private server must not appear (sessions/2code/absent, starts stay on the default socket).Write the sharing contract; lock leftover private-namespace claims
docs/herdr-integration.md: one Herdr server, shared; 2code attaches to the session the user'sherdrwould (Task 1 resolution order); never a private2codesession; neverherdr server stopon GUI exit; protocol >= 22 /herdr update(Task 2); bidirectional visibility with the recorded commands/output from steps 1–2.2codenamespace or that 2code never uses the user default session. The contract-probe dump line that says the dump uses a fixture socket, never the host's real default session, stays — reword it so it cannot be read as production policy.docs/architecture.md/docs/configuration.md/ AGENTS.md / CLAUDE.md twins only if they still contradict sharing. Extenddocs_client_mode.rsso the sharing-contract section cannot regress to "dedicated2codenamespace" as live production text.herdr_contract.rsonto the developer's~/.config/herdr. Do not changepin.json/ sidecar pin / protocol floor / PATH preference. Do not add a Settings health page or change New Profile / New Tab / Git / sidebar labels.Likely files and directories
src-tauri/crates/service/src/runtime.rsorsrc-tauri/crates/service/tests/) that drivesconnect_gui_herdr/RuntimeRouter/create_with_runtime/create_session/list_with_runtimeagainst the pinned binary on an isolated default socket. Keep Task 1's transport-level snapshot test; do not treat it as this proof.docs/herdr-integration.md(new Sharing contract + command log).docs/architecture.md,docs/configuration.md, AGENTS.md / CLAUDE.md twins only if they still advertise a private namespace as production.src-tauri/crates/service/tests/docs_client_mode.rssharing-contract strings.pin.json/ sidecar bundle /scripts/herdr-sidecar.mjs, Task 1 socket resolution, Task 2 protocol floor / PATH preference /herdr updatecopy, GUI exit /server stop, flattened-split mapping, IPC names, Git/file-tree/notes/tab UI,lib.rsnamingHerdrRuntimeSync, sqlite catalog schema,src-tauri/migrations/, contract-probe fixture sockets, leftover gaps Herdr v0.9.0 has no Windows live terminal attach (Task 10 gap) #396/Herdr v0.9.0 has no create-time startup command (Task 17 gap) #397/Live Herdr agent detection not verified on v0.9.0 (Task 13 gap) #398/Windows named-pipe JSON transport not verified for Herdr v0.9.0 (Task 5 gap) #399/macOS Unix sockets and terminal session attach not verified for Herdr v0.9.0 (Task 5/10 gap) #401, trackers Task 1 attach-user-session exceeds the 400–700 line estimate (tracking) #471/Task 2 version-policy exceeds the 250–500 line estimate (tracking) #473.Estimated changes: 200–400 lines total, additions plus deletions, including tests and docs; excluding generated files, lockfile churn and binary artifacts. Material scope growth (unpinning the sidecar, retargeting the contract probe onto the host session, leftover capability gaps) should become a separate issue.
Acceptance criteria
RuntimeRouter/create_with_runtimeand/or adopt +create_session, not onlyHerdrClient::workspace_create) appear insession.snapshot,herdr api snapshot, andherdr workspace list. NoHERDR_SESSION=2code, nosessions/2code/. The implementation reply quotes the commands and a sanitized snippet.herdrCLI against a sqliteprojects.folderappears inlist_with_runtimeas thatworkspace_id. Live read, not adopt-to-invent. Join key still filters by project folder. Commands and sanitized list ids are quoted in the reply and docs.docs/herdr-integration.mdhas a Sharing contract section with those commands/output. Production docs no longer say 2code uses a dedicated2codenamespace and never the user default session. Contract-probe notes may still isolate a fixture socket from the host's real default.docs_client_mode.rslocks the new contract text.herdr updatecopy stay as Task 2 left them.profilesis not source of truth.projectsstill round-trip in sqlite. No Local adapter. No secondevents.subscribe.lib.rsstill does not nameHerdrRuntimeSync. Join key still filters profiles by project folder.workspace_id; tab idspane_id; splits flattened as extra tabs; non-git New Profile is folderworkspace.create; Herdr-down New Tab errors). Showing join-keyed user workspaces in the sidebar is the phase goal, not a click/label redesign.cargo test --workspacefromsrc-tauri(orcargo test --workspace --exclude codewhere GTK is unavailable), including the new live sharing tests when the pinned binary is present (HERDR_SIDECAR_REQUIRED=1must not skip them into a green run). Frontend lint/typecheck/tests only if frontend scope is introduced (none expected).bun run typegenonly if IPC signatures change (none expected); do not hand-editsrc/generated/.Out of scope
herdr/herdr update); already done on this stack.~/.config/herdrsession.profiles/ session-table authority.server stop, flattened-split mapping, IPC command names, or Git/file-tree/notes/tab UI.Standing constraints
2codeas a session name,sessions/<name>/herdr.sockused as 2code's production socket, and the default-socket refusal stay deleted. Nothing in 2code may spawn a server on a socket the user's ownherdrcannot see.herdr server stopor kill the shared server process.profilesas source of truth.projectsstay in 2code sqlite.profilesrows already shipped in Plan: 2code as a Herdr client (profiles derived from Herdr) #436; this task does not re-litigate those decisions and does not change them.task-2-version-policy. Do not push. Do not open a PR.Why this is next
#466 has three tasks. Task 1 (#470) and Task 2 (#472) are accepted on
task-2-version-policy. Task 3 is the remaining bidirectional e2e proof plus sharing-contract docs rewrite that both earlier tickets explicitly deferred. The leftover open gaps (#396/#397/#398/#399/#401) and #471 / #473 are Linux-unverifiable leftovers or line-count tracking, not in-flight replacements. #404 is #394 Task 3. #441 is #436 Task 3. #461 is #458 Task 3. #467 is #463 Task 3.