terminal: a herdr id may carry its socket, and one locator grammar for every kind (#1055) - #1168
Open
fujibee wants to merge 2 commits into
Open
terminal: a herdr id may carry its socket, and one locator grammar for every kind (#1055)#1168fujibee wants to merge 2 commits into
fujibee wants to merge 2 commits into
Conversation
…mar for every kind (#1055) A herdr pane id is unique inside one running session and nowhere else; with two sessions both owning a w1:p2, every command that took a pane id reached whichever session the ambient environment named, and a repair resolved in one session landed in another's live pane. tmux has carried the socket inside the id since #1051; herdr now does the same: <socket-path>:wN:pX is accepted everywhere a herdr id is, a bare id keeps meaning the ambient instance, and a colon or control character in the socket is refused by name (the round-trip encoding is #1166). Every call about a pane goes through _herdr_cli <id>, which sets HERDR_SOCKET_PATH from the id and hands the CLI the bare pane; arrange refuses a source and target in different instances. The registry gains the one locator grammar, <kind>:<instance>:<pane>, with agmsg_locator_compose and agmsg_locator_split; each driver splits its own id (terminal_id_split), because where an instance ends inside an id is the driver's grammar. Refusals are one named reason on stderr. A call that bypasses _herdr_cli is silent and lands on the wrong instance, so .github/scripts/check-herdr-cli-routing.sh counts direct herdr calls per function by call position and compares them with a named allowlist in both directions; it runs from the bats suite as well.
…grammar Resolutions: herdr peek and poke keep #1164's forwarded diagnostics with the socket routing re-applied; the registry keeps both the locator section and the enumeration section (the merge had dropped the split's closing brace). Review findings folded in: terminal_pane_process_observe takes a pane id, so it routes through _herdr_cli with the bare pane and leaves the allowlist, with a control that two instances sharing a bare pane answer only from the id's socket; every comparison of a response pane_id against the caller's id now compares the bare pane. The routing checker also counts a call behind an assignment prefix (HERDR_SOCKET_PATH=... herdr ...), the exact form a bypass takes; terminal_enumerate_panes is listed at two, instance-wide by design.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1055 and #1152. Ref grammar only: no self-write, no sweep.
Why
A herdr pane id is unique inside one running herdr session and nowhere else. With two sessions running, both own a
w1:p2, and every command that took a pane id reached whichever session the ambient environment named. On 2026-09-11 a repair resolved in one session landed in another session's live pane. The placement record (herdr:w1:p2) carried no session, so nothing could have known.tmux has had the answer since #1051: the socket travels inside the id (
<socket>:%4). This PR gives herdr the same shape, and gives every reader of a location one grammar.What changes
A herdr id may carry its socket
<socket-path>:wN:pXis accepted everywhere a herdr id is; a barewN:pXkeeps meaning "the ambient instance", unchanged. A socket path may contain spaces. A colon or a control character in it is refused by name rather than mis-split: a colon in a POSIX path is legal, and a round-trippable encoding is deferred to #1166. The driver splits its own id (terminal_id_split), because the boundary inside an id is the driver's grammar: herdr ends in two colon fields, tmux and plain in one.Every call about a pane goes through
_herdr_cli <id> ..., which setsHERDR_SOCKET_PATHfrom a qualified id and hands the CLI the bare pane. Twenty-one calls in eleven functions were moved;terminal_arrangeadditionally refuses a source and target that name different sockets, before any call is made.One locator grammar, one parser
<kind>:<instance>:<pane>—herdr:/run/herdr-a.sock:w1:p7,tmux:/tmp/server with space:%4,plain:iterm:/dev/ttys040.agmsg_locator_compose <kind> <instance> <pane>andagmsg_locator_split <locator>live interminal-registry.shand consult the kind's driver for the boundary inside its id. Four seats read locators today; four parsers would disagree only after the fact. A refusal is one named reason on stderr and nothing on stdout:unknown_kind,instance_malformed(a colon in it, or none present where the id is otherwise valid),pane_malformed,id_malformed(the driver refused the whole id),locator_malformed.The routing is checked by machine
A call that bypasses
_herdr_cliraises no error: it goes to the ambient instance, on a pane id that may name a different seat's pane there. Twenty-one one-line edits are exactly the kind of change where one is missed in review, so.github/scripts/check-herdr-cli-routing.shcounts directherdr <subcommand>calls per function by call position (the start of a statement, or right after$(,if,!,&&,||,|,then,else,do; comment lines skipped; aherdr:inside a message never has a subcommand word after it) and compares each function with.github/herdr-cli-routing-allowlist, a<function> <count>table naming the six instance-wide functions that call the CLI directly. A function not listed fails; a listed count that went up fails; one that went down fails too and says to lower the entry, so a stale table cannot hide the next addition. An earlier count of these calls by plain grep answered 55 where the true number is 34, because comments and prose matched; the script does not repeat that. It runs from the bats suite (tests/test_herdr_cli_routing.bats), so the shards see it without a workflow change.Tests
tests/test_locator.bats(11): compose and split for the three kinds including a socket path with spaces; every composed locator round-trips; refusals by name for a colon in the instance, an unknown kind, a pane outside the grammar, an empty instance, a bare pane, the plain-sentinel, control characters; the herdr split's dependence on the pane grammar having exactly one colon is pinned by a three-field pane that is refused, never split one field to the left; the loaded driver is not replaced by a split for another kind; the herdr id grammar and_herdr_cli(a qualified id reaches its socket and the CLI gets the bare pane; a bare id keeps the ambient socket; the id wins over the environment).tests/test_herdr_cli_routing.bats(6): the driver sits at its allowlist; a new direct call in an unlisted function is red and named; an extra call inside a listed function is red (ABOVE); a vanished call is red (BELOW) and says to lower the entry;herdr:in a message andherdrin a comment are not calls; and end to end, seven pane-addressed ops on a qualified id all reach the id's socket with the bare pane and never the ambient one (argv and env logged by a fakeherdr).Not in this PR
Producing qualified ids at the edges —
terminal_spawn's returned id,terminal_detect,agmsg_terminal_self_env— stays bare (ambient) here; the sweep composes locators withagmsg_locator_compose, and the self-write path records the locator it is handed. When #1155 lands, itsterminal_enumerate_panescallsherdr pane listunder an explicit socket per instance; that function is instance-wide by design and belongs on the allowlist with its count — a one-line entry at merge time.