Repository navigation
Measure whether a session manager can already see into a workspace, before anything is built for it #549
Description
Question
Answer #548's four unknowns on real hardware. Every one of them is measurable
today, with no change to dl, because the transport dl uses is ordinary
OpenSSH against an alias devpod already published: anything dl would do with
SendEnv or -R, a hand-run ssh can do first.
Two of these can void #548's design and one can make the whole map unnecessary.
Nothing in #548 should be written until this task has reported.
This task is written for an agent running on the machine. It needs herdr
installed and running, herdr integration install claude done, devpod, docker,
and one real workspace. It cannot be done from a cloud session — which is why the
parent map has sat on assumptions.
Before touching anything
dl mutates real state: metadata.json, the bare clone cache, and live devpod
workspaces. Per CLAUDE.md:
- Point everything at a scratch cache:
XDG_CACHE_HOME=/tmp/dl-measure/cache dl ....
One variable scopes the whole of whatdlstores. - That isolates the bookkeeping, not the machine. Workspaces created below are
real, appear indevpod list, and must be deleted at the end. Record their ids
as you create them. - Do not run
dl --purgeduring this task. Pointed at a scratch cache it
finds every real workspace unrecognised, and its behaviour there is not what you
want while measuring.dl --pruneis safe and read-only until confirmed, but is
not needed here. - Use the released
dl, notdl-next, unless a measurement needs the tree.
Finding the transport by hand
dl hands ssh the config file explicitly as -F, because OpenSSH resolves its
default user config through getpwuid(getuid()) and reads none of the
environment that names the file (devlaunch#421). Resolve it the way dl does, in
order: an include option, then $DEVPOD_SSH_CONFIG, then a path option, then
~/.ssh/config. The alias is <workspace-id>.devpod.
CFG="${DEVPOD_SSH_CONFIG:-$HOME/.ssh/config}"
WS=<workspace-id> # from `devpod list`
ssh -F "$CFG" "$WS.devpod" 'echo inside; hostname'Confirm that round-trips before running anything below. Note that inside the
container the hostname is the workspace id (provision.rs:546), which is a
free check that you are where you think you are.
E1 — Is screen detection already working? Run this first
The cheapest possible outcome: if a manager classifies correctly from screen
content, only the process-name half is broken, and #548 may reduce to one
host-side environment variable.
dl <ws> -- claude pipes the agent's own TUI through the pane, so the pane's
screen holds Claude's real output, not an ssh banner.
- In a herdr pane, start an aid session:
aid <owner/repo>@<branch>and give it
a prompt that takes a while. - Record what herdr's agents tab says while it works, and again when it stops and
wants input. Screenshot or transcribe the row. - Record the host's view of the pane process:
ps -o pid,comm,args -p <pane pid> cat /proc/<pane pid>/comm /proc/<pane pid>/cmdline | tr '\0' ' '
- Repeat with
HERDR_AGENT=claudeexported before the launch — herdr documents
it for "VMs and wrappers", which is exactly whatdlis.
Report: does herdr show working/blocked correctly in either case? If yes with
HERDR_AGENT, say so loudly and stop — #548 collapses to documenting one
variable, and E2 to E4 are moot.
E2 — Does the sshd accept arbitrary environment names?
SendEnv is half a handshake; the server must AcceptEnv the name or it is
dropped silently. GH_TOKEN arriving today proves nothing about a novel name:
it may be arriving because something configures that one specifically.
# What the server actually permits
ssh -F "$CFG" "$WS.devpod" 'grep -i acceptenv /etc/ssh/sshd_config /etc/ssh/sshd_config.d/* 2>/dev/null'
# A novel name, end to end
HERDR_PANE_ID=probe-42 ssh -F "$CFG" -o SendEnv=HERDR_PANE_ID \
"$WS.devpod" 'echo "[${HERDR_PANE_ID:-UNSET}]"'Report: the AcceptEnv lines verbatim, and whether [probe-42] or [UNSET]
came back. UNSET means #548's env half needs the server configured too, which is
a different and larger change — say so.
E3 — Does -R unix-socket forwarding survive the ProxyCommand?
The alias tunnels through devpod ssh --stdio. A ProxyCommand carries the ssh
protocol, so remote forwards should work, but this has never been exercised here.
# Forward a socket in; then reach it from inside.
ssh -F "$CFG" -R /tmp/probe.sock:"$HERDR_SOCKET_PATH" \
-o StreamLocalBindUnlink=yes \
"$WS.devpod" 'ls -l /tmp/probe.sock && nc -U /tmp/probe.sock </dev/null; echo rc=$?'Then the real thing, which is the actual acceptance test for the whole map: lend
the binary in by hand and make the manager's own report call from inside the
container.
ssh -F "$CFG" -R /tmp/probe.sock:"$HERDR_SOCKET_PATH" -o StreamLocalBindUnlink=yes \
"$WS.devpod" 'HERDR_SOCKET_PATH=/tmp/probe.sock /path/to/lent/herdr \
pane report-agent "<pane id>" --source custom:probe --agent claude --state working'Check the herdr UI changed. Three specific ways this can fail, each worth
reporting distinctly rather than as "it did not work":
AllowStreamLocalForwardingdisabled on the sshd (default is yes).- The container user cannot create the remote path.
/tmpis used above for that
reason; if it still fails, report the path and the uid. - Peer credentials. herdr may validate the connecting process with
SO_PEERCRED. The container user is typicallyvscodeat uid 1000 and the host
user may not be, so a refusal here is a uid mismatch and not a transport
failure. Report the container'sid -ualongside the host's.
E4 — Does a forward collide with ControlMaster reuse?
A remote forward belongs to the master connection, so a later trip joining an
existing master gets the first trip's forwards, not its own. This is the failure
control_key's length-prefixing comment describes, one level up.
- Open a master with a forward to socket A and keep it alive (
ControlMaster=auto,
ControlPersist, asdlsets them). - Run a second trip over the same alias asking for a forward to socket B.
- From inside, determine which socket is actually reachable.
Report: whether B was honoured, ignored, or errored. If the second trip
silently inherits A, #548 must take the forward spec as a control_key field, and
the task should say so in as many words.
Recording
Post one comment on #548 with, per experiment: the commands as run, the output,
and a one-line verdict. Where a measurement contradicts something #547 or #548
asserts, say which sentence is wrong — both tickets were written from a cloud
session with no hardware, and #547 has already had its mechanism corrected once.
Cleanup
Delete every workspace created here (dl <ws> --rm, or devpod delete <ws>), and
remove /tmp/dl-measure. Confirm devpod list is back to what it was, and say so
in the report — a measurement that leaves workspaces behind costs the next person
the confusion this task exists to remove.
Activity
blooop commentedon Sep 1, 2026
Reported on #548: #548 (comment)
Headline, since E1 says to say it loudly: screen detection already works through
dl. WithHERDR_AGENT=claudeset host-side, herdr classifies a workspace agent idle, working and blocked correctly and continuously, with no change todlat all. Without it the same pane sitting on an unmistakable blocked prompt reportsagent_not_found. Only agent identification is broken, not the rules.E2 to E4 are moot for herdr, but I ran them anyway and two changed what the design would have to be if it is ever built:
remote software version Go) and nosshd_configto holdAcceptEnv. Novel names and wildcards arrive regardless, with clean controls in both directions. Separately,GH_TOKENnever usedSendEnvat all: it is written into/etc/envfile.jsonat container creation, which is the same staleness dl carries two tokens into a workspace and nothing else, so no session manager can see the agent it started #548 holds against herdr cannot see an agent inside a dl workspace: the hook arrives, its socket, binary and pane id do not #547.-Rand moved the pane's real state. Three hazards, all fixable: a failed forward is silent under the alias'sLogLevel error, the remote listen path is destroyed rather than refused, and a dead socket is left behind when the master goes.control_keystands, for that reason instead.Scratch workspace deleted,
/tmp/dl-measureremoved,devpod listback to the same eight workspaces.dl --purgewas never run.