docs: hxprobe plans, summaries, explanations, usage, and changelog

Plans (docs/plans/):
- 2026-07-01-23-47-py-hxprobe-httpx.md — initial httpx probe design
- 2026-07-02-09-32 through 14-05 — standalone project, toolchain, usage
  doc + Makefile, file input (-f), simplification pass, run-summary footer

Summaries (docs/summaries/): one per completed feature, recording what
was actually built, deviations from the plan, and verification steps

Explanations (docs/explanations/): two deep-dives written during review —
hxprobe concurrency model and worst-exit-code + render-loop analysis

Usage (docs/usage/hxprobe.md): overview with pointer to hxprobe/USAGE.md
for the full runnable reference

Walkthrough (docs/py-latprobe-walkthrough.md): narrative tour of the
latprobe Python package for interview / code-review context

CHANGELOG.md: entries for all hxprobe features (toolchain, usage doc,
file input, simplification, run-summary footer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 13:22:59 +02:00
parent 13a562e966
commit f487a4b1bd
19 changed files with 2367 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
# hxprobe: end-of-run summary footer — summary
Plan: `docs/plans/2026-07-02-14-05-hxprobe-run-summary-footer.md`.
Trigger: a follow-up to
`docs/explanations/2026-07-02-13-25-hxprobe-worst-exit-code-and-render-loop.md`
— does a single worst-code exit even make sense across multiple URLs with
different possible errors? The answer landed on: keep the scalar exit code
(it's a documented cross-implementation contract with `latprobe`/Go — see
`hxprobe/USAGE.md`'s Exit codes table, asserted by 13 existing tests), and
instead add the missing visibility as an end-of-run summary footer.
## What changed
All in `hxprobe/hxprobe/cli.py` unless noted, matching the plan exactly (no
deviations):
1. **`_EXIT_LABELS`** (new, next to `_PHASE_EXIT`): inverse mapping from exit
code → short label (`ok`, `dns`, `connect`, `timeout`, `tls`, `http`), used
to render the footer's `→ exit N (label)` line.
2. **Accumulation loop refactor**: previously computed a single running
`worst` directly inside the per-URL loop via two different idioms
(`if c > worst: worst = c` for network failures, `max(worst, EXIT_HTTP)`
for `--fail`). Now each URL first gets its own `code` (`EXIT_OK` folded up
via `max()` across its failed samples and, if `--fail`, its ≥400 successes),
appended to a new `url_codes: list[int]` (index-aligned with `urls`), and
*then* folded into `worst = max(worst, code)`. Unifies both idioms into one.
3. **`_print_run_summary(urls, url_codes, worst, out)`** (new helper, next to
`_print_failure_summary`): writes a separator line, an
`N URLs — X ok[, Y failed]` header, one `✗ {label:<8}: {count}` line per
non-OK class present (first-seen order, same dedup style as
`_summarize_failures`), and the closing `→ exit N (label)` line.
4. **Call site**: `elif len(urls) > 1: _print_run_summary(urls, url_codes,
worst, stdout)` — added after the per-URL loop, in the `else` branch of the
existing `if ns.json_out:` check (so it's text-mode-only), right before
`return worst`. JSON path (`json.dumps(json_items, ...)`) is completely
untouched — still a bare array, no top-level summary object, preserving the
documented "same shape as `latprobe`'s JSON" contract.
## Design decisions (confirmed with user before implementing)
- **Exit code stays a scalar** (worst/highest severity across URLs) — not
count-of-failed-URLs, not binary 0/1. Both alternatives were presented and
rejected because they'd break the documented 06 table, Go/`latprobe`
parity, and the 13 existing exit-code tests.
- **Summary is a text footer, multi-URL only** (`len(urls) > 1`) — not always
shown, and not also duplicated into JSON as a top-level object (which would
turn the JSON array into an object and break the documented array-shape
parity). Single-URL text output is untouched; JSON output is untouched.
## Tests
`hxprobe/tests/test_cli.py`: new `TestCLIRunSummary` class, 3 tests, reusing
the existing `_OKHandler`/`_start_server`/`_free_port`/`_invoke` harness:
- `test_multi_url_mixed_shows_summary` — one OK URL + one connection-refused
URL: asserts `EXIT_CONNECT`, and the footer strings (`"Summary: 2 URLs"`,
`"1 ok"`, `"1 failed"`, `"connect : 1"`, `"→ exit 3"`).
- `test_multi_url_all_ok_summary` — two OK URLs: asserts `EXIT_OK`,
`"Summary: 2 URLs — 2 ok"`, and no `"✗"` anywhere in output.
- `test_single_url_has_no_summary` — one OK URL: asserts `"Summary:"` is
absent (locks the multi-URL-only rule).
All 33 pre-existing tests pass unedited (33 + 3 new = 36 total).
## Verification
- `hxprobe/.venv/bin/python -m pytest tests/test_cli.py tests/test_probe.py -q`
→ **36 passed**.
- `hxprobe/.venv/bin/ruff check hxprobe/ tests/` → **all checks passed**.
- Manual smoke tests, all matching the plan's expected behavior exactly:
- `hxprobe https://example.com https://example.org` → footer
`Summary: 2 URLs — 2 ok` / `→ exit 0 (ok)`.
- `hxprobe https://example.com http://no.such.host.invalid` → footer
`Summary: 2 URLs — 1 ok, 1 failed` / `✗ dns : 1` / `→ exit 2 (dns)`;
process exit code confirmed `2` via `echo $?`.
- `hxprobe https://example.com` (single URL) → **no** footer, output
byte-for-byte the same shape as before this change.
- `hxprobe --json https://example.com https://example.org` → still a bare
JSON array, no summary object.
- Also captured a 3-URL mixed run (`example.com` ok, DNS failure, TLS
failure against `self-signed.badssl.com`) to confirm severity ordering in
the footer: DNS (2) and TLS (5) both counted, exit reported as `5 (tls)`
— the higher-severity class correctly wins the scalar while the footer
still shows the DNS failure that the scalar alone would hide.
## Docs updated (per CLAUDE.md conventions)
- `hxprobe/USAGE.md`: new "Multi-URL summary footer" section with a real
3-URL mixed-outcome capture; refreshed the pre-existing "Multiple URLs" and
`-f configs/all-ok.txt` / `-f configs/dns-failure.txt` examples, which were
captured before this feature existed and were now stale (missing the
footer) — replaced with fresh live captures; added a sentence to the Exit
codes section pointing at the new section.
- `docs/usage/hxprobe.md`: one-line addition to the exit-codes bullet
pointing at `hxprobe/USAGE.md`'s new section.
- `docs/explanations/2026-07-02-13-25-hxprobe-worst-exit-code-and-render-loop.md`:
appended an "Update (2026-07-02)" paragraph pointing forward to this work,
per the plan's "optional — pointer instead of a new file" option.
- `CHANGELOG.md`: new entry at the top.
- This file.
No deviations from the approved plan.