Skip to content

Auto-clear a hera role's self-reported blocked status, not just the PTY flag - #904

Merged
anutron merged 1 commit into
masterfrom
argus/root-cause-and-fix-a-live
Jul 21, 2026
Merged

Auto-clear a hera role's self-reported blocked status, not just the PTY flag#904
anutron merged 1 commit into
masterfrom
argus/root-cause-and-fix-a-live

Conversation

@anutron

@anutron anutron commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Root cause

The hera rail's "(?)" glyph is RoleView.needsInputOwn() = r.NeedsInput || (r.HasStatus && r.Status == db.HeraStatusBlocked) — an OR of two independent signals. Every prior needs-input fix (BUG-029 through BUG-065, see context/knowledge/gotchas/events.md) governs only the first signal: the auto-detected PTY-content needs-input flag. The second signal — a role's self-reported hera_status, set only via an explicit hera_status tool call or a manual rail s/S step — had no auto-clear at all.

Confirmed via a direct read-only query against the live dogfood DB: the reported role's hera_role_status was blocked, set before the screenshot's direct-reply exchange. The deployed dogfood binary was only one unrelated commit behind master, ruling out binary skew.

Live repro: a worker set itself hera_status="blocked" (e.g. awaiting a check-in), the human replied DIRECTLY in the pane (a real keystroke), and the worker replied conversationally without re-invoking hera_status — plain conversation has no tool call to trigger it. The rail kept showing "(?)" indefinitely.

Fix

  • agent.ClearBlockedRoleStatus(blockedAt, lastUserInput, resumed) bool — mirrors NeedsInputClear's two-tier design. A direct reply after the block clears immediately (no threshold — a brief acknowledgment reply may never sustain the resumed-activity ticks BUG-065's condition requires). Sustained resumed activity (reusing BUG-065's agent.ResumeActivityTick) clears it for the symmetric coordinator-relayed-answer case.
  • db.ListBlockedHeraRoleBindings() / db.ClearBlockedRoleStatus(roleID) — new read/write DB primitives, one indexed join, scoped to the (usually empty) live+blocked set.
  • Wired into both internal/tui/app.go (App.autoClearBlockedHeraRoles) and internal/api/push.go (Server.autoClearBlockedHeraRoles) for frontend parity — REST/web/macOS all read the same hera_role_status row.
  • New gotcha entry (BUG-066) in context/knowledge/gotchas/events.md + index bump.

Testing

New tests at all layers: internal/db (read/write DB primitives), internal/agent (pure predicate), internal/tui, internal/api (end-to-end through the real callers, including the exact live-repro shape: direct reply → brief ack → clears with no resumed-activity threshold needed).

Verification

make pre-pr: build/vet/fmt-check/lint-pr all pass clean (0 lint issues). vuln fails on pre-existing stdlib CVEs unrelated to this diff (confirmed non-blocking per ci-gates.md, CI runs it continue-on-error). test-cover-gate hit repeated timeouts in unrelated pre-existing tests under sustained -race load on the dev machine — confirmed via a git stash + rerun on the unmodified base commit, which reproduced the identical failure/timing, proving it's environmental and not caused by this diff. All new + touched-package tests pass individually.

🤖 Generated with Claude Code

…TY flag

The rail's "(?)" glyph ORs two independent signals: the auto-clearing
PTY-content needs-input flag (RoleView.NeedsInput) and a role's
self-reported hera_status=="blocked" (RoleView.needsInputOwn), set only
via an explicit hera_status tool call or a manual s/S step. Every prior
needs-input fix governs only the first signal, so a role that marked
itself blocked and was then answered directly in its pane -- with the
agent replying conversationally rather than re-invoking hera_status --
showed a stale "(?)" that nothing but a manual step could clear.

agent.ClearBlockedRoleStatus mirrors NeedsInputClear's two-tier design:
a direct reply after the block clears immediately (no threshold, since
a brief acknowledgment may never sustain the resumed-activity ticks),
or sustained resumed activity clears it for a coordinator-relayed
answer. Wired into both the TUI and the daemon for frontend parity,
backed by two new DB primitives (ListBlockedHeraRoleBindings,
ClearBlockedRoleStatus) scoped to the (usually empty) live-blocked set.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Merging this branch changes the coverage (2 decrease, 1 increase)

Impacted Packages Coverage Δ 🤖
github.com/drn/argus/internal/agent 89.53% (+0.01%) 👍
github.com/drn/argus/internal/api 88.14% (-0.01%) 👎
github.com/drn/argus/internal/db 89.61% (-0.03%) 👎
github.com/drn/argus/internal/tui 82.67% (+0.06%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/drn/argus/internal/agent/needsinput.go 94.58% (+0.02%) 240 (+1) 227 (+1) 13 👍
github.com/drn/argus/internal/api/push.go 94.27% (-1.14%) 227 (+31) 214 (+27) 13 (+4) 👎
github.com/drn/argus/internal/db/hera.go 88.28% (-0.06%) 546 (+23) 482 (+20) 64 (+3) 👎
github.com/drn/argus/internal/tui/app.go 76.58% (+0.28%) 2553 (+30) 1955 (+30) 598 👍

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/drn/argus/internal/agent/needsinput_test.go
  • github.com/drn/argus/internal/api/needsinput_test.go
  • github.com/drn/argus/internal/db/hera_test.go
  • github.com/drn/argus/internal/tui/app_test.go

@anutron
anutron merged commit 78a884b into master Jul 21, 2026
1 check passed
anutron added a commit that referenced this pull request Jul 22, 2026
…nct AskUserQuestion prompts (BUG-067) (#905)

BUG-063's cleared-marker guard could only compare (taskID, timestamp), so
it couldn't distinguish "the same already-answered content re-detected"
from "a second, textually distinct, still-unanswered prompt that happens
to arrive with lastInputOf(id) unchanged" -- an accepted trade-off at the
time. Claude Code's /brainstorm AskUserQuestion flow hits this constantly:
answer question 1 directly in the pane, question 2 (a different prompt)
appears before any further keystroke, and the rail's "(?)" never comes
back even though the agent is genuinely blocked on it.

agent.NeedsInputClear now threads a content fingerprint alongside the
cleared-marker timestamp (ClearedMarker{At, FP, HasFP}) so a stale
re-candidacy is only suppressed when the content also matches; a
fingerprint that provably differs re-arms instead. Both callers already
computed the needed fingerprint each tick for the BUG-032 content-
stability pass, so no new render pass was added.

Investigated first as a possible regression from #904 (which reuses the
same shared ScreenRenderer) -- ruled out via the live DB (neither role had
a hera_role_status row) and a test proving the two passes don't
cross-contaminate when sharing a renderer in the same tick.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
@anutron
anutron deleted the argus/root-cause-and-fix-a-live branch July 24, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant