# Changelog All completed features are logged here in reverse-chronological order. --- ## 2026-07-01 01:05 — Root Makefile - Namespaced targets: `go-build`, `go-run`, `go-test`, `go-test-verbose`, `go-check`, `go-cover`, `go-fmt`, `go-vet`, `go-tidy`, `go-install`, `go-lint`, `go-clean` - Umbrella targets (`build`, `test`, `check`, `fmt`, `vet`, `clean`, `all`) delegate to Go now; `py-*` will slot in during the Python port - `help` is the default target; auto-generated from `##` comments - `go-run` accepts `ARGS=` for passing flags, `go-lint` guards for `golangci-lint` - `.gitignore` updated with coverage artifacts --- ## 2026-07-01 00:49 — Integration tests (Go, Step 6) - Extracted `run(args, stdout, stderr) int` from `main()` to make the CLI testable in-process - Fixed TLS classification bug: `TLSHandshakeDone` fires with the error on cert rejection, so `tlsErr` is now captured and checked before the stale `tlsStart.IsZero()` guard - `run_test.go`: 14 in-process tests covering exit codes 0–6, multi-URL, sampling, and JSON structure assertions - `cli_test.go`: `TestMain` builds the real binary; 3 subprocess smoke tests exercise the actual `os.Exit` path - All test servers use `httptest` + stdlib; `.invalid` TLD for deterministic DNS failures; no external network dependency --- ## 2026-07-01 00:38 — Failure handling (Go, Step 5) - Classified network failures into `dns` / `connect` / `timeout` / `tls` with distinct exit codes (2–5) - Partial timing preserved up to the failure point (e.g. DNS phase shown on NXDOMAIN) - `--timeout` flag (default 10s) applied via `context.WithTimeout` - `--fail` flag: HTTP status ≥ 400 → exit code 6 (curl-style) - `-n` sampling continues on network failure; aggregates successes, reports fail count + cause - JSON output extended with `succeeded`, `failed`, `errors[]` fields - Highest exit code across all URLs/failure types is used as the process exit --- ## 2026-07-01 00:08 — JSON output flag (Go, Step 4) - `--json` flag emits a JSON array with one entry per URL - Schema always uses min/avg/max shape (consistent regardless of `-n`) - Phases absent from the request (e.g. TLS on HTTP) are omitted from the JSON object - Failed URLs are excluded from JSON output and reported to stderr - Text output unchanged and remains the default --- ## 2026-07-01 00:08 — Multiple URLs + sampling (Go, Step 3) - `-n`/`--count` flag repeats each URL N times and reports min/avg/max per phase - `probe.Summarize` aggregates a slice of Results into per-phase PhaseStats - Multi-sample output shows an aligned min/avg/max table with a header row - Single-sample output (n=1) is unchanged from Step 2 --- ## 2026-07-01 00:08 — Per-phase breakdown (Go, Step 2) - Instrumented requests with `net/http/httptrace.ClientTrace` - DNS lookup, TCP connect, TLS handshake, Server/TTFB, Transfer, Total phases - TLS row omitted automatically for plain `http://` URLs - Aligned text output with separator before Total --- ## 2026-07-01 00:08 — Simple total latency (Go, Step 1) - `probe.Measure` performs an HTTP GET and records wall-clock total time - `main.go` prints URL, HTTP status code, and total duration for each URL - Exits with code 1 if any URL fails - Multiple URLs accepted as positional arguments --- ## 2026-07-01 00:08 — Project scaffold (Go, Step 0) - Created project structure: `go/`, `python/`, `docs/plans/`, `docs/usage/` - Initialised Go module `latprobe` (`go/go.mod`) - Minimal `go/main.go` that prints usage and exits cleanly when no URL is given - Stub `go/internal/probe/probe.go` defining the `Result` type and `Measure` signature - Foundation files: `README.md`, `CLAUDE.md`, `CHANGELOG.md` - Saved initial plan to `docs/plans/2026-07-01-00-08-go-latency-tool.md`