Files
http-latency-prober/docs/summaries/2026-07-02-09-32-hxprobe-standalone-project.md
Jan Novak f487a4b1bd 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>
2026-07-02 13:22:59 +02:00

118 lines
6.7 KiB
Markdown

# Summary: extract `hxprobe` into a standalone top-level project
Plan: [docs/plans/2026-07-02-09-32-hxprobe-standalone-project.md](../plans/2026-07-02-09-32-hxprobe-standalone-project.md)
## What was built
`hxprobe` moved from `python/hxprobe/` to a new top-level `hxprobe/`
directory (sibling of `go/` and `python/`), structured as a genuinely
independent project: own `pyproject.toml`, own venv (`hxprobe/.venv`), own
`tests/` directory. It imports nothing from `python/latprobe` — verified with
`grep -rn "latprobe" hxprobe/` returning zero matches in code (a few comments
mention Go's `http.DefaultClient` for context, which is fine; no comment
references `latprobe` as a concrete path/module anymore either).
Files:
- `hxprobe/pyproject.toml` — own manifest, `httpx[http2]` dependency,
`packages = ["hxprobe"]`
- `hxprobe/hxprobe/probe.py` — moved from `python/hxprobe/probe.py`, with
`Options`, `Phase`, `Result`, `VerboseDetail`, `CertInfo` dataclasses and
`_parse_cert`/`_parse_cert_date` helpers now defined locally (copied from
`latprobe/probe.py`) instead of imported; all the httpx-instrumentation
logic (`_Trace`, `_TimingStream`, `_TimingBackend`, `_TimingTransport`,
`measure()`) is unchanged
- `hxprobe/hxprobe/aggregate.py`, `duration.py` — verbatim copies of
`latprobe`'s; their imports were already package-relative (`from .probe
import Result`), so copying required zero edits
- `hxprobe/hxprobe/cli.py` — rewritten as a full standalone CLI. Previously a
27-line wrapper delegating to `latprobe.cli.run()` via an injected
`measure_fn`; now has its own exit codes, argparse, and text/JSON
rendering (copied from the shared `latprobe/cli.py`, then stripped of the
`measure_fn`/`prog`/`description`/`protocol_flags` parameterization that
only existed to let two packages share one `run()`)
- `hxprobe/tests/{test_probe,test_cli,test_integration}.py` — moved from
`python/tests/test_hx_*.py` / `test_integration_hx.py`, `hx`-prefix
dropped, imports repointed from `latprobe.*` to `hxprobe.*`
- `python/latprobe/{cli.py,probe.py}` — reverted via `git checkout --` to
their exact pre-`hxprobe` committed state (confirmed zero diff afterward)
- `Makefile``py-deps`/`PY_VENV*`/shared `hx-run`/`hx-test-integration`
removed from the Python section; new standalone `hx-deps`/`hx-run`/
`hx-test`/`hx-test-integration`/`hx-check`/`hx-clean` targets under
`HX_DIR`/`HX_VENV*`; umbrella `test`/`check`/`clean` now run all three
(`go-*`/`py-*`/`hx-*`)
- `docs/usage/py-hxprobe.md``docs/usage/hxprobe.md` (renamed + rewritten
for the new structure)
- Old `python/hxprobe/`, `python/pyproject.toml`, `python/.venv`,
`python/latprobe_python.egg-info/` deleted
## Key design decisions
- **Move, don't rewrite, the already-debugged files.** `probe.py` and the
three test files carry real bug fixes found during the original
implementation (the `mark_dns` present-on-failure bug, verbose detail not
populated on the failure path, `Content-Length` needed once the test
fixtures switched to HTTP/1.1 keep-alive). Rewriting from scratch would
have risked reintroducing them.
- **`aggregate.py`/`duration.py` needed zero import changes** — their
existing relative imports (`from .probe import ...`) already resolve
correctly once copied into a new package with its own `probe.py`. Not
every file needed the same treatment as `probe.py`/`cli.py`.
- **`cli.py`'s starting point was the *current* shared `latprobe/cli.py`**,
not a from-scratch rewrite — it already contained 100% of the needed
logic (including the `--no-http2`/`--no-follow-redirects` flags and the
verbose Protocol row/JSON keys, both added earlier specifically for
hxprobe). The only work was deleting the generalization scaffolding
(`measure_fn`, `MeasureFn`, `prog`/`description`/`protocol_flags` params)
that existed solely to let two packages share one `run()`.
- **Reverting `latprobe` via `git checkout --` rather than hand-editing** —
confirmed first via `git log`/`git diff --stat` that `cli.py`/`probe.py`
had no changes besides the hxprobe-sharing scaffolding since the last
commit, making this a safe, exact, zero-risk revert.
- **Comments referencing `latprobe` by file path were reworded**, not just
the imports. A comment like "already used by latprobe/probe.py" becomes a
dangling reference once this directory is genuinely portable to another
repo. Reworded ~5 comments/docstrings to describe the technique generically
(e.g., "ports the getaddrinfo → connect split" → "splits DNS and TCP
connect into two timed steps") instead of naming the other project.
## Deviations from the plan
None of substance. One judgment call not spelled out in the plan: the old
`test_cli.py`'s docstring said its tests "focus on what's different... since
run() delegates almost entirely to latprobe.cli.run()" — no longer true now
that `cli.py` is a full standalone reimplementation. Rewrote that docstring
for accuracy rather than leaving a stale claim, without expanding the test
suite itself (that would be a larger, separate scope-creep beyond what was
asked — see the coverage note below).
## Notable follow-up worth flagging
`hxprobe/tests/test_cli.py` has ~11 tests versus `latprobe/tests/test_cli.py`'s
~23. That gap was fine when hxprobe's `cli.py` was a thin wrapper around
already-tested shared code; it's a real gap now that `cli.py` is an
independent ~440-line reimplementation with its own copy of every rendering
branch. Existing tests do exercise the core paths (single URL, `--fail`,
JSON, DNS/connect failures) so this isn't uncovered, but reaching
`latprobe`-level depth (multi-URL separator, sampling aggregate output, JSON
error grouping, all-failed multi-header) would be a reasonable next step if
full independent confidence in the standalone project matters. Not done here
— out of scope for a decoupling/move task.
## Verification
- `grep -rn "latprobe" hxprobe/` — zero matches in code; comment mentions
reworded to be self-contained
- `git diff --stat python/latprobe/` — empty (exact revert to last commit)
- `make hx-deps` — creates `hxprobe/.venv`, installs `httpx[http2]` from
`hxprobe/pyproject.toml`
- `make hx-run ARGS="-v http://github.com"` — same output as before the
move (HTTP/2, 1 redirect followed, IP/TLS/cert shown)
- `make hx-test` — all hermetic hxprobe tests pass standalone (own venv,
own `PYTHONPATH=hxprobe`)
- `make hx-test-integration` — live HTTP/2 + redirect tests still pass
- `make py-test``latprobe`'s own suite passes with zero setup (no
`py-deps`/venv needed), confirming the revert didn't leave residue
- `make check` (top-level) — Go + latprobe + hxprobe all green in one gate
- `python -m latprobe --help` output confirmed unchanged from before the
whole hxprobe feature existed (no leftover `--no-http2` etc.)