Truthful agent lights: classify live background work at turn end, and expire stale active glyphs
#456
Replies: 5 comments
|
all three are separate calls, so taking them one at a time. the classifier: cookbook. same answer as #332 and #382, with two agterm-specific reasons on top of the usual one. first, an installer change wouldn't reach you or anyone else who already installed: second, the rest is the usual bar. The discriminator is Claude Code's own snapshot wrapper shape, undocumented and free to move, and the leaf-shape rule reads the ownership guard: the bug is mine, the tty test is not the fix. a cross-session repaint is a defect and it belongs in the package, same class as #199. But the bundled OpenCode plugin already solves this without asking the OS anything: it tracks the agent's own session ids and fires completed only when that set empties, so a subagent's idle cannot paint completed onto a still-busy parent (#289). Compare against that before you write the PR. If a Claude-side guard is still the answer after that, I want it in a Claude-specific layer, not in the shared wrapper. expiry: no, and the reason is your own case 1. no hook for N minutes is not liveness. The package installs no what is actually missing is read-back: the tree node carries status, pane, blink, color and shape but no timestamp, so your sweeper has to keep its own state to know how old a glyph is. Send an issue for |
|
All three land. Recipe it is — I'll write the classifier up per cookbook/CONTRIBUTING.md, snapshot-wrapper dependency and the leaf-shape misreads stated plainly in Limits, and keep the shape/color vocabulary as something the reader opts into. On the guard: agreed the tty test fails both ways once you name One aside, since it frames what the recipe will be: the running system already enforces your invariants rather than conceding them. Nothing expires on hook silence — the idle-clear fires only when an independent process scan shows zero work under the agent, stuck detection reads transcript progress rather than hook gaps, and the color/shape vocabulary lives in an opt-in layer, not the stock hooks. So the recipe describes a design field-tested against the failure modes you named, not a proposal. statusChangedAt: filed as #460. |
|
Comparison done — measured on a live estate (~20 concurrent sessions), chains and timings, not reasoned from memory. Three results. The OpenCode mechanism doesn't port, but its principle does. The plugin's three Sets live in one Node heap, and one plugin instance sees every session of its OpenCode process — parent and subagents publish onto the same bus, so The guard that survives it: count agent ancestors, in a Claude-specific shim. New The finding that shapes the PR: your classifier objection applies to a naive shim too. Long-term there's a cleaner answer that deletes the whole heuristic: Unless you'd rather sequence it differently, the PR comes as: shim + installer wiring + migration, migration as its own commit so both parts read separately. (One correction to my own filing while I'm here: the stock package installs no SessionEnd hook — the repaint comes from Stop plus the worker's UserPromptSubmit/PostToolUse fires; SessionEnd was our local extension.) |
|
Guard PR is up: #461 — the shape from the comparison above (adapter + byte-exact migration + hermetic script tests), shared wrapper untouched. The classifier recipe follows separately once it clears review. |
Uh oh!
There was an error while loading. Please reload this page.
The stock Claude hook package maps Stop →
completed --auto-resetunconditionally. That makes the sidebar glyph lie in three common agent
workflows:
(a test/CI run via a background tool call, or a second agent as a worker)
goes green at Stop while the work is still running. After the row is
visited once: no glyph at all, indistinguishable from idle.
pushed
activepulsing forever; nothing ever times out.reasoning, and a lane parked in a lock queue all look identical
(
active), so "is it stuck, testing, or just waiting its turn?" can't beanswered from the sidebar.
We run a hook-package extension on 0.19.1 that fixes all three across ~20
concurrent agent sessions; posting to ask whether any of it is
upstream-worthy, and in which shape.
Turn-end classifier (Stop hook). Claude Code tool commands — foreground
and background — run under a recognizable wrapper (
zsh -c source …/shell-snapshots/snapshot-…) that is a child of the agent process, while MCPservers and harness helpers are direct children without it. One
ps -axo pid,ppid,commandpass at Stop classifies what is still alive under the agent:a wrapper subtree containing another agent binary →
active --blink(workersstill running); a subtree doing real work →
active --color '#4A9EFF' --shape square(machinery); a subtree whose only leaves are wait-shaped processes(sleep/flock/tail/grep — a lock queue, a poll loop, a log monitor) → the same
blue with
--shape triangle(the closest the shape set has to a "queued"arrow), so waiting reads differently from executing; nothing →
completed --auto-resetas today. The per-call tint/shape channel turned out to beexactly right for this.
Machinery paint during the turn (PreToolUse). A small matcher paints the
blue square when the command about to run is gate/test-shaped, so a long CI
run reads differently from the model thinking. The next PostToolUse restores
the pulse.
Ownership guard. A worker agent spawned from inside a session (headless
claude -p, a second CLI agent) inherits the spawner'sAGTERM_*environment, so its own Stop/SessionEnd hooks repaint the SPAWNER's row — we
found seven sessions sharing ids live, with a coordinator's row going green
mid-run because a finished worker's Stop fired against it. Fix: the status
hook walks up to the calling agent and stays silent when that process has no
controlling terminal (
ps -o tty=says??); pane-attached agents alwayshave one. This guard belongs in the stock hook package regardless of the rest.
Staleness sweeper. A launchd job (2 min) enforces "no glyph = idle" in
both directions: agent pid dead and hooks dark →
idle; agent alive but zerowork subtrees and no hook fire for 25 min →
idle(the idle-at-prompt lie);live work found under a glyph-less row → re-light it grey/square/triangle. We
first shipped a cputime-probe "stuck" detector and retired it the same day:
measured over 3s windows, a wedged agent process is indistinguishable from a
healthy idle one (runtime timers keep ticking), so process-CPU is a dead end —
honest stuck detection needs progress signals, not process state.
Questions:
would you rather see the whole thing as a cookbook recipe? Happy to PR
either shape.
repaints for anyone who spawns agents from agents — want it as a standalone
PR to the stock hooks even if the rest stays a recipe?
fix may be app-side: agterm could dim/expire an
activeglyph that hasn'tbeen re-set for N minutes (configurable, off by default). Hooks already
re-push
activeon every tool event, so a live session would never decay.Would you take an issue/PR in that direction?
Scripts are small bash, BSD-userland clean, and degrade to the stock behavior
outside agterm.
All reactions