Skip to content

HUD producers bypass the ADR-003 cascade via copy-paste emitters: unify AFK/HITL/detect-context.sh on write-hud-state.sh and reconcile transport paths #323

Description

@arndvs

Problem

ADR-003 (binding) defines a single transport cascade — named pipe working/runtime/hud.pipe → HTTP localhost:7823/api/event → JSONL working/logs/events.jsonl — implemented once in bin/write-hud-state.sh. Every producer is supposed to emit through it. Three producers instead reimplement the cascade inline, and their diverged copies are silently breaking delivery:

  1. shft/afk.sh writes to a dead JSONL path. _push_afk_event() (shft/afk.sh:71-88) writes the JSONL fallback to $WORKING_DIR/events.jsonl — i.e. working/events.jsonl, one directory above the canonical working/logs/events.jsonl the daemon actually polls (bin/hud-daemon.js:44, created and watched by start-hud.sh:30). With no pipe reachable, an AFK run's info events (~4 per run, lines 164/305/310) are written to a file neither the daemon nor any skill reads (all consumers read working/logs/events.jsonl — e.g. skills/error-audit/SKILL.md:101).

  2. shft/afk.sh pipe writes are unconditional. The inline emitter appends to the pipe whenever [[ -p "$_pipe" ]] (line 83) — without the _can_use_pipe() daemon-alive check (write-hud-state.sh:42-48) — so a stale FIFO left by a crashed daemon blocks the backgrounded writer at the printf open, and a missing working/ at all appends to $CTRL_DIR (a read-only mount in the AFK Docker setup, per hud-daemon.js:14).

  3. shft/once.sh never emits. HITL runs produce only claude stderr; nothing touches the HUD, so sessions the user is actively watching are invisible in the dashboard — a regression from detect-context.sh / hook producers.

  4. detect-context.sh maintains a third inline copy (lines 140-169), already diverged: it omits the HTTP fallback tier that the cascade defines, writes directly to the pipe with no daemon-alive check, and must duplicate _LOG_DIR/_PIPE paths on every cd().

Why this drift keeps happening: subprocess origins (ctrlshft-claude, hud-session.sh, init scripts) run with HOME overridden to the consumer dir, so afk.sh's $CTRL_DIR-derived paths do not resolve to the $DOTFILES dir the daemon uses (bin/hud-daemon.js:39). The two sides have no shared path source and no test bridges them.

Architecture impact

Scope

  1. shft/afk.sh — delete _push_afk_event(); replace the three call sites with sourcing bin/write-hud-state.sh (same pattern hooks/hud-session.sh:19 already uses) and call write_hud_event. If sourcing is unacceptable in the subprocess, shell out once per run to the script's existing CLI mode (write-hud-state.sh bridge-event reads JSON from stdin — reuse it).
  2. shft/once.sh — source bin/write-hud-state.sh and emit info events for session start/end (&& once mode).
  3. bin/detect-context.sh — collapse the inline block (lines 140-169) to write_hud_event "context" "Active contexts: $contexts" with the project/path overrides the function already supports; keep the ide field by extending write_hud_event with an optional ide arg (daemon already reads/stores ide).
  4. Canonicalize the runtime-dir source — resolve _RUNTIME_DIR/_LOG_DIR/_PIPE/_JSONL precedence (DOTFILES first, then DOTFILES_HOME) once in write-hud-state.sh so subprocess origins resolve the same path the daemon computes; add a comment noting the fork trap so future producers reference the single source rather than re-deriving.

Acceptance criteria

  • test/lifecycle.sh (or a new test/hud-afk-paths.sh wired into run-all) asserts that an AFK run (or a simulated _push_afk_event replacement) delivers its events into working/logs/events.jsonl / the pipe, not working/events.jsonl.
  • A test asserts write_hud_event works when sourced from $HOME pointing at a consumer dir with DOTFILES_HOME set (the ctrlshft-claude subprocess case).
  • grep -rn "events.jsonl" shft once.sh over the repo finds no producer writing to a non-logs/ path.
  • detect-context.sh emits the same context payload shape as before (project, projectPath, contexts, ide) — verified through the daemon's handleContext regex branch — after collapsing to the shared function.
  • Live check: with the daemon running, bash shft/once.sh and one manual shft/afk.sh 1 round each surface a session/event row in the HUD (/api/state).

Non-goals

Proposed by: automated source:architecture-review pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    source:architecture-reviewPRDs proposed by the automated architecture-review workflow

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions