logdy: live-discover new agent-run dirs without a container restart #421
Labels
No labels
burndown-2026-06
coherence-core
consult
headless
interactive
P0
P1
P2
P3
P4
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
coilyco-flight-deck/infrastructure#421
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split off from #420 (the Logdy filesystem log-viewer role).
The
logdyrole runslogdy follow --full-readover the drained~/.ward/agent-logs/tree.followtakes a fixed file list resolved onceat container start (Logdy has no glob / directory-watch), so runs that drain
after the container comes up are not visible until the next re-converge or
docker compose restart logdy.For the "always-up, always-current" ideal we want live discovery of new run
dirs without a restart. Options to weigh:
fswatch) thatdocker compose restartslogdy when a new
<container>/dir appears (simple, but drops the UI buffer),stdinmode fed bytail -Fwith retry over a shell thatre-globs, or a
find-loop feeder,Until then the documented workaround (re-converge to pick up freshly closed
runs) is in
docs/FEATURES.md. Low priority - closed runs that existed atstart are already browsable, which was the point of #420.
Decision: Option A (watcher-driven logdy restart)
Dug into the three options against logdy v0.17.1. Confirmed upstream still has no runtime directory-follow or glob-rescan:
followresolves a fixed file list once at start, and thelogdy follow *.logshell-glob is expanded by the shell (so it hits the same fixed-at-start wall we already have). So Option C's upstream feature does not exist to lean on today.Between the two local options, the deciding factor is that Option B (stdin feeder) regresses the UX: logdy
stdinmode carries no per-file origin metadata, so we would have to prefix each line with its path, which mangles thetranscript.jsonlJSON and breaks the in-browser TypeScript parsers that make the per-run view readable. Option A keeps nativefollow --full-read, so origin tagging and the JSON parsers stay intact. The only cost is a brief UI reset per restart, and--full-readrebuilds the line buffer on restart so no history is lost.Recommended shape (no docker socket, no host launchd)
Keep it inside the one logdy container rather than adding a socket-mounted sidecar (we deliberately avoid docker-socket exposure) or a host-level launchd job. Wrap the logdy command in a self-contained supervisor loop in
compose.yaml:findwe run today,logdy ... follow --full-read $filesin the background,/logson an interval (10-15s is fine for a browse-closed-runs tool), and when the signature changes, kill logdy and re-loop so it re-globs and re-execs,Poll (not inotify) so we need no extra packages in the upstream-built image. The empty-tree stdin fallback stays as-is. This makes "container restart" an in-container logdy re-exec, delivering Option A's behavior with none of the socket or host-agent cost.
Dispatching a
warded engineerrun to implement this and updatedocs/FEATURES.md+ the compose header comment (the current text points here as the open follow-up).🔒 Reserved by
ward agent --driver claude— containerengineer-claude-infrastructure-421on hostkais-macbook-pro-2.localis carrying this issue (reserved 2026-07-01T08:24:16Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.— Claude (she/her), via
ward agentWARD-OUTCOME: done - logdy now live-discovers new run dirs via an in-container poll/restart supervisor, no re-converge needed
The design decision (Option A, an in-container supervisor loop) was already settled in the thread, so this was mostly a shell-craft exercise rather than a research one. The genuinely fiddly part was the empty-tree fallback: a naive
sleep infinity | logdy &pipeline leaks an orphaned feeder process every time the tree goes from empty to populated. Feeding logdy's stdin from a FIFO held open on fd 3 sidesteps that cleanly, and stubbing out logdy let me actually prove zero orphans across both the populated-restart and empty-then-first-run paths.One thing that fought back but was not my code: the pylint pre-commit hook kept failing on
Permission deniedwriting/opt/uv/pythonin this container. It reproduces on an unmodified tree, so it is environmental, and pointingUV_PYTHON_INSTALL_DIRat a writable dir clears it. Worth noting for anyone else committing from a ward box.Confidence is high on the supervisor logic since I tested the state machine directly, and CI plus pre-commit are green. The one thing I could not verify here is real logdy behavior: no compose plugin or image build in this container, so I am trusting that logdy tolerates a FIFO on stdin in follow mode and releases port 8080 promptly on SIGTERM (the kill-wait-start sequencing is there to cover the latter). If either assumption is wrong it will show up the first time a run drains on a live mac.
Rough edge, as flagged in the issue: each restart drops the in-UI scroll buffer. That is inherent to Option A. If it turns out to grate in daily use, the real fix is still the upstream directory-follow feature request (option C) rather than more supervisor cleverness.