Skip to content

feat(status): declared waits, stale watchdog, reported_at aging - #45

Merged
matthias-scale merged 11 commits into
fork/pr-basefrom
mat125/declared-waits
Aug 10, 2026
Merged

feat(status): declared waits, stale watchdog, reported_at aging#45
matthias-scale merged 11 commits into
fork/pr-basefrom
mat125/declared-waits

Conversation

@matthias-scale

@matthias-scale matthias-scale commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Implements slice 4 status reporting across the existing v2 hook payload, server state/API/events, watchdog scheduler, and TUI projections.

Acceptance criteria

AC1: ✓ turn-end reports with wait and eta_s are stored server-side and exposed through JSON API/events; absent fields remain unchanged — pane_report_agent_declared_wait_and_reported_at_are_exposed_in_api_and_event, declared_wait_watchdog_uses_deadline_and_fresh_report_clears_stale, and closing-block emitter tests.

AC2: ✓ a declared deadline violation derives STALE and a fresh report clears it — declared_wait_watchdog_uses_deadline_and_fresh_report_clears_stale, stale_watchdog_is_visible_in_api_event_and_cleared_by_fresh_report, and process_exit_clears_supervisor_stale_with_hook_authority.

AC3: ✓ 20-minute silence without a wait derives STALE, while a live declared wait does not — no_declared_wait_watchdog_stales_after_silence_only_while_working, declared_wait_watchdog_uses_deadline_and_fresh_report_clears_stale, and declared_wait_is_ignored_for_non_working_reports.

AC4: ✓ agents cannot self-report STALE; the payload is rejected without corrupting state — stale_is_not_a_self_reportable_agent_state.

AC5: ✓ accepted reports store/expose reported_at, and the TUI continuously ages the projection from the instant that supplied the visible age — pane_report_agent_declared_wait_and_reported_at_is_visible_in_api_and_event, status_report_age_projection, and reported_at_age_refresh_uses_the_reported_instant_on_desktop_and_mobile covering desktop and mobile refresh collectors.

AC6: ✓ a missing v is treated as v2 and strict-decodes malformed gates/items/decisions as invalid_request; foreign v1 and forward-v3 reports skip silently before strict payload decoding, while malformed v2 remains rejected — report_agent_params_without_version_keep_strict_arrays, malformed_missing_version_report_returns_invalid_request, report_agent_params_with_foreign_version_arrays_still_parse, report_agent_params_with_foreign_version_defers_typed_fields (v3 structured state), foreign_version_report_is_a_silent_no_op_without_pty, foreign_version_with_reshaped_state_is_a_silent_no_op, and report_agent_params_with_current_version_keep_strict_arrays.

AC7: ✓ supervisor STALE is a fifth distinct state with a dedicated global stale bucket: GlobalAgentCounts.stale increments from the shared stale-aware status key, contributes to total(), and renders as ! N stale; stale also outranks other states in both legacy priority and declarative attention sorting via the shared helper — global_agent_counts_exclude_supervisor_stale_working_agents asserts stale == 1 and total() == 1, agent_summary_renders_stale_as_a_distinct_segment, distinct_agent_summary_uses_configured_symbols_for_every_state, priority_sort_surfaces_stale_agents_before_blocked_agents, declarative_attention_sort_surfaces_stale_agents_before_blocked_agents, stale_mobile_agent_detail_uses_the_stale_bucket, workspace_activity_summary_excludes_supervisor_stale_working_panes, navigator_working_filter_excludes_supervisor_stale_rows, and process_exit_clears_supervisor_stale_with_hook_authority. No src/detect/ changes; navigator metadata/color and desktop/mobile age accents are also stale-aware.

AC8: ✓ closing-block emitter assets are updated and tested; deployed copies were not touched — 15 Python closing-block tests plus integration asset tests.

Requirement inventory

Design entry Disposition Reason
Declared wait wait + eta_s implemented Additive v2 emitter fields, server storage/API/events, watchdog input, and TUI presentation.
Watchdog: declared deadline violation implemented Headless/runtime scheduled checks with testable thresholds and no keepalive noise.
Watchdog: 20-minute silence without declared wait implemented Server-derived STALE, separate from working and Unknown.
Fresh report clears STALE implemented Hook report authority clears the supervisor-derived flag.
reported_at storage/API/events and aging implemented Wire value is exposed; receipt time drives safe watchdog aging; TUI shows compact age text and refreshes from the visible source instant.
Agent self-reported STALE out-of-scope STALE is supervisor-derived; parser rejects it as a pane agent state.
Remote push without terminal artifact deferred Fleet-side signal is not observable by the Herdr server; explicitly deferred outside this repository.
Telegram push on blocking 0→N dropped Final Matthias scope cut; blocking transitions remain sidebar-only.
Keepalive pings/noise out_of_scope Design requires no pings when there is no watchdog violation.

Validation

  • Focused API schema tests: 4 passed, including absent-v strict rejection of each malformed array.
  • Missing-version server boundary: malformed_missing_version_report_returns_invalid_request passed; foreign-version dispatcher/schema tests: 4 passed, including v1 silent no-op and v3 structured-state silent no-op.
  • Focused stale projection tests: global mobile counts 1 passed with positive stale-bucket/total assertions; stale summary segment, stale mobile detail, workspace activity summary, navigator stale filter, and both attention-sort paths passed.
  • just check-parallel in isolated XDG temp dirs: lint passed; Rust reached the documented parallel work_context_git fixture flake, with no changed-surface failures.
  • Serial flake boundary: cargo test --bin herdr work_context_git -- --test-threads=1 passed 19/19.
  • Two separate concern commits: API version gate, then stale UI projections.

@matthias-scale
matthias-scale merged commit 3a8e4db into fork/pr-base Aug 10, 2026
6 checks passed
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