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
Version policy for the shared Herdr server for parent plan #466. Task 1 (#470 on task-1-attach-user-session) already attaches to the user's session (default $XDG_CONFIG_HOME/herdr/herdr.sock, never a private 2code namespace). Production still pins the sidecar exactly: incompatibility_reasons requires version == PINNED_VERSION ("0.9.0") andprotocol == 22, and report_version requires the exact line herdr 0.9.0. resolve_gui_sidecar only looks at the bundled sidecar. A user running Herdr 0.8.2 (protocol: 20) on that shared socket is therefore rejected without naming herdr update, and a newer compatible server (protocol >= 22) would also be rejected. Wheel scroll needs pane.scroll (protocol 22); protocol 20 is a silent break, not a reason to spawn a second server.
Probe the reachable server's protocol/version instead of treating the sidecar pin as the only acceptable live server. Accept protocol >= 22 (Herdr >= 0.9.0). When the reachable server is older, fail closed with an actionable message that names the found version, the required version, and herdr update — never spawn a private instance. Prefer the user's installed herdr over the bundled sidecar when both exist; keep the sidecar only as the no-installation fallback and still start it on the shared socket. Same clicks and labels.
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 changes which binary 2code trusts and how it classifies a live server, not the protocol layer or which socket it talks to.
This is #466 Task 2, not #394 Task 2 (#402), not #436 Task 2 (#439), and not #463 Task 2. Do not implement or close those tickets here. Do not start Task 3 (bidirectional e2e proof + sharing-contract docs rewrite).
Dependencies: Parent plan #466. Prerequisite: #470 (Task 1, accepted on task-1-attach-user-session). Open #471 is Task 1 line-count tracking only — not a blocker 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
Classify the reachable server by protocol floor, not exact sidecar pin
incompatibility_reasons / classify_status: compatible means protocol >= 22 (Herdr >= 0.9.0). Drop the exact version != PINNED_VERSION reject. A 0.9.1 / 0.10.0 server with protocol 22+ is Compatible. Protocol 20 / version 0.8.2 is Incompatible.
Keep status.compatible == false / endpoint_compatible == false as incompatible. Keep today's endpoint-generation check if current v0.9.x still reports generation 1; do not invent a new generation policy.
report_version on the GUI connect path must not require the exact line herdr 0.9.0. User PATH binaries >= 0.9.0 are allowed. The bundled sidecar pin (pin.json, PINNED_TAG / checksums / scripts/herdr-sidecar.mjs) stays as the fallback binary; do not retarget the pin to latest.
Production still talks to Task 1's shared socket. An incompatible reachable server still fails closed and must not spawn a private 2code session or a second server on another socket.
Prefer the user's installed herdr; sidecar is no-installation fallback
resolve_gui_sidecar / a dedicated resolve helper: after PATH is fixed (fix_path_env::fix already runs at process start), look up herdr on PATH and use it when present. Only if no user herdr exists, use try_resolve_sidecar.
The chosen executable is what ensure_herdr_listener starts (when absent) and what CLI attach uses. Sidecar start remains on the shared default socket, never sessions/2code/.
If a user herdr exists but is older than 0.9.0: fail closed with the herdr update message. Do not ignore it and start the sidecar on the same socket (two versions fighting for one session). Sidecar starts only when herdr is not installed.
Tests must isolate PATH / XDG so the developer's real herdr and ~/.config/herdr do not leak into fixtures. Explicit GuiHerdrConnect.sidecar stays a test override.
Actionable old-server error; UI is not silent
Incompatible (protocol < 22) HerdrServerIncompatible text must name: the found version/protocol, the required floor (Herdr >= 0.9.0 / protocol >= 22), and the command herdr update. Absent vs incompatible stay distinct; absent must not claim herdr update as if a server were found.
HerdrStubAdapter::fail_closed already returns that startup error on later ops. A user-visible surface must show it (existing sonner / mutation error on New Tab or New Profile, or a connect-time toast using that same string). Do not add a Settings runtime health page or picker.
Named UX exception (not a click/label redesign): when the shared server is too old or absent, the same New Tab / New Profile / Git / sidebar controls stay; they surface this actionable error instead of a silent empty catalog or a private 2code server. list_projects may still yield profiles: [] on Herdr-down; the error string must still appear on a fail-closed action or connect toast.
GUI exit / lease drop still must not herdr server stop.
Prove the old-server path; do not swallow Task 3
Fake-herdr: running protocol 20 / 0.8.2 on the isolated default socket → Probe::Incompatible, starts == 0, stop_count == 0, error contains found version, required floor, and herdr update. No sessions/2code/ and no /tmp/2code-herdr-*.sock.
Fake-herdr: protocol 22+ / 0.9.1 (or 0.10.0) on that socket is Compatible and is reused (no second start).
PATH herdr wins over a present sidecar when both exist; missing PATH uses sidecar; old PATH herdr does not fall through to sidecar start.
Update production current-state sentences that say GUI startup only resolves the pinned sidecar / exact 0.9.0 server: docs/herdr-integration.md opening current-state paragraph, docs/architecture.md infra table / diagram, docs/configuration.md if it still implies sidecar-only. AGENTS.md / CLAUDE.md twins only if they still advertise exact-pin-as-the-only-live-server. Keep the v0.9.0 pin as the fallback artifact.
Do not write Task 3's bidirectional command log (herdr workspace list reverse direction) or replace the whole sharing-contract section.
If line count grows past the estimate, open a tracking issue; do not fold Task 3 into this branch.
Update:src-tauri/crates/infra/src/herdr/mod.rs — PATH lookup vs sidecar; report_version floor vs exact line for user binaries; pin stays for sidecar fallback.
Update:src-tauri/crates/service/src/runtime.rs — resolve_gui_sidecar / connect_gui_herdr prefer user herdr; herdr_gui_startup_wires_sidecar_without_local_fallback and missing/incompatible tests.
Update if the error must reach a click:src/features/terminal/hooks.ts (New Tab onError toast) and/or the existing profile-create error path. No new Settings page.
Estimated changes: 250–500 lines total, additions plus deletions, including tests and docs; excluding generated files, lockfile churn and binary artifacts. Material scope growth (bidirectional e2e command log, sharing-contract rewrite, unpinning the sidecar) should become a separate issue.
Acceptance criteria
Live-server compatibility is protocol >= 22 (Herdr >= 0.9.0), not exact PINNED_VERSION / exact herdr 0.9.0. A newer protocol-22+ server is reused. Protocol < 22 is incompatible.
Old reachable server: fail closed, no second start, no private 2code session. Error names found version/protocol, required floor, and herdr update. Absent vs incompatible stay distinct.
User-installed herdr on PATH is preferred over the bundled sidecar when both exist. Sidecar is used only when herdr is not installed, and then starts on the shared default socket. Old PATH herdr does not fall through to a sidecar start on that socket.
Sidecar pin remains v0.9.0 (checksums, pin.json, bundle script). latest is still refused. Contract probe still pins v0.9.0.
A user-visible surface shows the old-server message (existing toast / fail-closed op). No Settings runtime health UI. Same New Tab / New Profile / Git / sidebar clicks and labels.
Fake-herdr tests: old-server path asserts no private server and the error names herdr update; newer compatible server is reused; PATH preference vs sidecar as above. cargo test --workspace from src-tauri (or cargo test --workspace --exclude code where GTK is unavailable). Frontend lint/typecheck/tests only if frontend scope is introduced. bun run typegen only if IPC signatures change (none expected); do not hand-edit src/generated/.
Live current-state docs no longer say GUI startup only accepts the exact pinned sidecar server. Pin-as-fallback and contract-probe isolation notes stay. Task 3 bidirectional proof is not this branch.
No behavior change beyond binary preference and version policy. 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. GUI/lease drop leaves the user's server running.
Named UX exception: old/absent shared server surfaces found/required versions and herdr update instead of a silent empty catalog or a private 2code server. 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).
Never start a private session. Nothing in 2code may spawn a server on a socket the user's own herdr cannot see. An old shared server is not a reason to open sessions/2code/.
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. If the reachable server is too old (protocol < 22) or absent, the UI shows what is wrong and what to run (herdr update for old; distinct copy for absent), and does not fall back to a private instance.
Do not change interaction. Same clicks and labels. If a label genuinely cannot survive, name the exception in this issue (old/absent server shows the upgrade/absent error; no new health page).
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-1-attach-user-session. Do not push. Do not open a PR.
Why this is next
#466 has three tasks. Task 1 (#470) is accepted on task-1-attach-user-session. Task 2 is the version policy / user-binary / herdr update work Task 1 explicitly deferred. Task 3's bidirectional proof depends on attaching to a compatible shared server rather than an exact sidecar pin. The leftover open gaps (#396/#397/#398/#399/#401) and #471 are Linux-unverifiable leftovers or line-count tracking, not in-flight replacements. #402 is #394 Task 2. #439 is #436 Task 2.
Summary
Version policy for the shared Herdr server for parent plan #466. Task 1 (#470 on
task-1-attach-user-session) already attaches to the user's session (default$XDG_CONFIG_HOME/herdr/herdr.sock, never a private2codenamespace). Production still pins the sidecar exactly:incompatibility_reasonsrequiresversion == PINNED_VERSION("0.9.0") andprotocol == 22, andreport_versionrequires the exact lineherdr 0.9.0.resolve_gui_sidecaronly looks at the bundled sidecar. A user running Herdr 0.8.2 (protocol: 20) on that shared socket is therefore rejected without namingherdr update, and a newer compatible server (protocol >= 22) would also be rejected. Wheel scroll needspane.scroll(protocol 22); protocol 20 is a silent break, not a reason to spawn a second server.Probe the reachable server's protocol/version instead of treating the sidecar pin as the only acceptable live server. Accept protocol >= 22 (Herdr >= 0.9.0). When the reachable server is older, fail closed with an actionable message that names the found version, the required version, and
herdr update— never spawn a private instance. Prefer the user's installedherdrover the bundled sidecar when both exist; keep the sidecar only as the no-installation fallback and still start it on the shared socket. Same clicks and labels.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 changes which binary 2code trusts and how it classifies a live server, not the protocol layer or which socket it talks to.This is #466 Task 2, not #394 Task 2 (#402), not #436 Task 2 (#439), and not #463 Task 2. Do not implement or close those tickets here. Do not start Task 3 (bidirectional e2e proof + sharing-contract docs rewrite).
Dependencies: Parent plan #466. Prerequisite: #470 (Task 1, accepted on
task-1-attach-user-session). Open #471 is Task 1 line-count tracking only — not a blocker 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
Classify the reachable server by protocol floor, not exact sidecar pin
incompatibility_reasons/classify_status: compatible means protocol >= 22 (Herdr >= 0.9.0). Drop the exactversion != PINNED_VERSIONreject. A 0.9.1 / 0.10.0 server with protocol 22+ is Compatible. Protocol 20 / version 0.8.2 is Incompatible.status.compatible == false/endpoint_compatible == falseas incompatible. Keep today's endpoint-generation check if current v0.9.x still reports generation 1; do not invent a new generation policy.report_versionon the GUI connect path must not require the exact lineherdr 0.9.0. User PATH binaries >= 0.9.0 are allowed. The bundled sidecar pin (pin.json,PINNED_TAG/ checksums /scripts/herdr-sidecar.mjs) stays as the fallback binary; do not retarget the pin tolatest.2codesession or a second server on another socket.Prefer the user's installed
herdr; sidecar is no-installation fallbackresolve_gui_sidecar/ a dedicated resolve helper: after PATH is fixed (fix_path_env::fixalready runs at process start), look upherdron PATH and use it when present. Only if no userherdrexists, usetry_resolve_sidecar.ensure_herdr_listenerstarts (when absent) and what CLI attach uses. Sidecar start remains on the shared default socket, neversessions/2code/.herdrexists but is older than 0.9.0: fail closed with theherdr updatemessage. Do not ignore it and start the sidecar on the same socket (two versions fighting for one session). Sidecar starts only whenherdris not installed.herdrand~/.config/herdrdo not leak into fixtures. ExplicitGuiHerdrConnect.sidecarstays a test override.Actionable old-server error; UI is not silent
HerdrServerIncompatibletext must name: the found version/protocol, the required floor (Herdr >= 0.9.0 / protocol >= 22), and the commandherdr update. Absent vs incompatible stay distinct; absent must not claimherdr updateas if a server were found.HerdrStubAdapter::fail_closedalready returns that startup error on later ops. A user-visible surface must show it (existing sonner / mutation error on New Tab or New Profile, or a connect-time toast using that same string). Do not add a Settings runtime health page or picker.list_projectsmay still yieldprofiles: []on Herdr-down; the error string must still appear on a fail-closed action or connect toast.herdr server stop.Prove the old-server path; do not swallow Task 3
Probe::Incompatible,starts == 0,stop_count == 0, error contains found version, required floor, andherdr update. Nosessions/2code/and no/tmp/2code-herdr-*.sock.herdrwins over a present sidecar when both exist; missing PATH uses sidecar; old PATHherdrdoes not fall through to sidecar start.incompatibility_reasonsexactPINNED_VERSION,report_versionexactEXPECTED_VERSION_LINEon the GUI connect path,incompatible_sidecar_fails_closed_without_flippingif it only covers a non-herdr binary. Keep contract-probe / pin.json / live sidecar checksum tests on v0.9.0.docs/herdr-integration.mdopening current-state paragraph,docs/architecture.mdinfra table / diagram,docs/configuration.mdif it still implies sidecar-only. AGENTS.md / CLAUDE.md twins only if they still advertise exact-pin-as-the-only-live-server. Keep the v0.9.0 pin as the fallback artifact.herdr workspace listreverse direction) or replace the whole sharing-contract section.Likely files and directories
src-tauri/crates/infra/src/herdr/process.rs—incompatibility_reasons,classify_status, incompatible Display/message, colocated fake-herdr tests.src-tauri/crates/infra/src/herdr/mod.rs— PATH lookup vs sidecar;report_versionfloor vs exact line for user binaries; pin stays for sidecar fallback.src-tauri/crates/service/src/runtime.rs—resolve_gui_sidecar/connect_gui_herdrprefer userherdr;herdr_gui_startup_wires_sidecar_without_local_fallbackand missing/incompatible tests.src/features/terminal/hooks.ts(New TabonErrortoast) and/or the existing profile-create error path. No new Settings page.docs/herdr-integration.md,docs/architecture.md,docs/configuration.md. AGENTS.md / CLAUDE.md twins only if they still say exact v0.9.0 is the only accepted live server.pin.json/ sidecar bundle /scripts/herdr-sidecar.mjspin target, Task 1 socket resolution, 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.Estimated changes: 250–500 lines total, additions plus deletions, including tests and docs; excluding generated files, lockfile churn and binary artifacts. Material scope growth (bidirectional e2e command log, sharing-contract rewrite, unpinning the sidecar) should become a separate issue.
Acceptance criteria
PINNED_VERSION/ exactherdr 0.9.0. A newer protocol-22+ server is reused. Protocol < 22 is incompatible.2codesession. Error names found version/protocol, required floor, andherdr update. Absent vs incompatible stay distinct.herdron PATH is preferred over the bundled sidecar when both exist. Sidecar is used only whenherdris not installed, and then starts on the shared default socket. Old PATHherdrdoes not fall through to a sidecar start on that socket.pin.json, bundle script).latestis still refused. Contract probe still pins v0.9.0.herdr update; newer compatible server is reused; PATH preference vs sidecar as above.cargo test --workspacefromsrc-tauri(orcargo test --workspace --exclude codewhere GTK is unavailable). Frontend lint/typecheck/tests only if frontend scope is introduced.bun run typegenonly if IPC signatures change (none expected); do not hand-editsrc/generated/.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. GUI/lease drop leaves the user's server running.herdr updateinstead of a silent empty catalog or a private 2code server. Existing Herdr-backed exceptions stay (route idsworkspace_id; tab idspane_id; splits flattened as extra tabs; non-git New Profile is folderworkspace.create; Herdr-down New Tab errors).Out of scope
~/.config/herdrsession.profiles/ session-table authority.server stop, flattened-split mapping, IPC command names, or Git/file-tree/notes/tab UI beyond surfacing the existing fail-closed error.Standing constraints
herdrcannot see. An old shared server is not a reason to opensessions/2code/.herdr server stopor kill the shared server process.herdr updatefor old; distinct copy for absent), and does not fall back to a private instance.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-1-attach-user-session. Do not push. Do not open a PR.Why this is next
#466 has three tasks. Task 1 (#470) is accepted on
task-1-attach-user-session. Task 2 is the version policy / user-binary /herdr updatework Task 1 explicitly deferred. Task 3's bidirectional proof depends on attaching to a compatible shared server rather than an exact sidecar pin. The leftover open gaps (#396/#397/#398/#399/#401) and #471 are Linux-unverifiable leftovers or line-count tracking, not in-flight replacements. #402 is #394 Task 2. #439 is #436 Task 2.