Skip to content

feat: ops & UX polish (TUI, healthz, telegram inbound, grafana)#6

Merged
devAsmodeus merged 9 commits intomainfrom
feat/ops-and-ux
May 2, 2026
Merged

feat: ops & UX polish (TUI, healthz, telegram inbound, grafana)#6
devAsmodeus merged 9 commits intomainfrom
feat/ops-and-ux

Conversation

@devAsmodeus
Copy link
Copy Markdown
Owner

PR A of the three-PR push described in
docs/superpowers/specs/2026-05-02-three-pr-roadmap.md.

Closes Level 1 remainder + observability tail. Pure stdlib. No new pip dependencies.

What's in v0.5.0

  • --tui — curses dashboard (EMA hashrate, uptime, shares sent/ok/rej, job_id, pool diff, workers). Quit on q/ESC. Windows graceful skip if windows-curses missing.
  • ASCII banner at startup, gated by --no-banner.
  • /healthz JSON endpoint on the metrics server. ok/degraded (200) vs down (503) for k8s liveness/readiness. Tunable --healthz-stale-after.
  • Telegram inbound commands /stats /stop /restart /help. Long-poll thread, chat_id authz, opt-in via HOPE_HASH_TELEGRAM_INBOUND=1.
  • Grafana dashboard deploy/grafana/hope-hash.json (5 panels, datasource templated).
  • StatsProvider — single thread-safe data bus between mine() and consumers (TUI today, web in PR C).
  • Type annotations completed in miner.py and stratum.py.
  • --log-file PATH — duplicate logs to a file (essential with --tui).
  • Notify timing testnotify_share_accepted is now strictly driven from the pool ack callback, never from the submit path. Regression-tested in 5 scenarios.

Tests

py -3.11 -m unittest discover -s tests -v145 passed (was 101). Net +44 tests across 4 new files (test_banner.py, test_tui.py, test_healthz.py, test_notifier_timing.py) plus inbound dispatch tests appended to test_notifier.py.

Files

See docs/handoff/pr-a-summary.md for the file map, gotchas for the PR B agent, and open questions.

Self-check

  • All tests pass on py -3.11 -m unittest discover -s tests -v
  • No pip install happened
  • docs/handoff/pr-a-summary.md exists with file map
  • CHANGELOG and README updated minimally
  • No third-party imports anywhere in src/hope_hash/
  • Hot-path code in parallel.py/block.py untouched

devAsmodeus and others added 9 commits May 2, 2026 12:33
Adds src/hope_hash/banner.py (render_banner / print_banner) and 6
unit tests. ASCII-only on purpose so the banner survives consoles
without UTF-8 (legacy Win, syslog with C locale).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces a thread-safe StatsProvider that miner pushes to and TUI/
healthz/web consume from — single source of truth for all observers.
TUIApp wraps curses in a daemon thread, redraws every 1s, quits on
q/ESC. On Windows without windows-curses we degrade gracefully (warn,
keep mining).

13 tests cover provider thread safety, snapshot immutability, and
format helpers. curses-loop itself is not unit-tested (TTY-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends MetricsServer with a /healthz route returning JSON. Status
is computed by a pluggable callable registered via
set_health_provider(). Pure build_health_snapshot() encodes the
state machine: ok/degraded (200) vs down (503), with a 30s
hashrate-freshness window and configurable share-staleness.

11 tests cover both the snapshot matrix and the live HTTP layer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds long-poll getUpdates thread (start_inbound/stop_inbound) and a
register_command registry. Authz strictly by HOPE_HASH_TELEGRAM_CHAT_ID;
foreign chat updates are dropped with a warning log. Opt-in via
HOPE_HASH_TELEGRAM_INBOUND=1 — we don't open a network thread without
explicit ack.

test_notifier_timing.py is a regression test for the class of bugs
where notify_share_accepted fires from the submit path before the pool
acks: we now drive it exclusively from on_share_result(accepted=True),
verified across submit-only, ack-true, ack-false, duplicate-ack, and
multi-share scenarios.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cli.main() now:
- prints the banner unless --no-banner / --tui
- creates a StatsProvider shared between mine() and consumers
- starts TUIApp when --tui (with curses-availability fallback)
- registers a health provider on the metrics server
- registers /stats /stop /restart Telegram handlers when opt-in env set
- routes log INFO to --log-file in TUI mode (stderr stays at WARNING+)

mine() accepts stats_provider; supervisor_loop() accepts restart_event
for the /restart command. stratum.py gets full attribute type hints
and dict[str, Any]/list[Any] params.

Bumps __version__ to 0.5.0 and re-exports new public symbols.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Minimal Grafana 10.x dashboard with 5 panels: hashrate timeseries,
pool difficulty, shares accepted vs rejected (stacked bar), workers
gauge, uptime stat. Datasource templated as ${DS_PROMETHEUS} so it
imports clean against any Prometheus connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- CHANGELOG.md: v0.5.0 section (TUI, banner, healthz, telegram inbound,
  Grafana, type annotations, 145 tests).
- README.md: short "What's new in v0.5.0" paragraph above the status
  table; bumped test count 101 → 145. Full README rewrite is PR C.
- ROADMAP.md: ticked TUI (curses), banner, telegram inbound, Grafana,
  healthchecks endpoint.
- docs/handoff/pr-a-summary.md: file map, new flags/env vars, gotchas
  and open questions for the PR B subagent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@devAsmodeus devAsmodeus marked this pull request as ready for review May 2, 2026 19:34
@devAsmodeus devAsmodeus merged commit 680cb09 into main May 2, 2026
10 checks passed
@devAsmodeus devAsmodeus deleted the feat/ops-and-ux branch May 2, 2026 19:34
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