# Changelog

All notable changes to `mgf-apiprobe` are documented here.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
the project follows [SemVer](https://semver.org/spec/v2.0.0.html). Per
the 0.x window ([SemVer 0.x](https://semver.org/#spec-item-4) and rule
**AP-03** from `mgf-standard/docs/standards/API_DESIGN.md`), MINOR
releases MAY break the public API. Pin tightly:

```toml
mgf-apiprobe = ">=0.X.0,<0.Y"   # one minor at a time
```

The release-engineering discipline that produces this changelog is in
[`mgf-standard/docs/standards/RELEASING.md`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/RELEASING.md).

---

## [0.5.9] - 2026-06-26

### Changed
- Release the **mgf-common 0.49** pin (`<0.50`) onto devpi so the full
  fleet co-installs at the 0.49 cornerstone (devpi backfill; no API change).

## [0.5.8] - 2026-06-16

### Changed
- Widened the `mgf-common` pin to `<0.48` — mgf-common 0.47 is the pure-library release (the end-user CLI moved to `mgf-fed[cli]`); the importable API is unchanged.
- Regenerated `.woodpecker` so the supervisor lane calls `mtest` (the moved `mgf-common test` frontend is gone).

## [Unreleased]

### Added
- Retries (EH-11): `Probe.retry(n)` + `APIPROBE_MAX_RETRIES` — transient `TransportError`s retried with
  exponential backoff + full jitter + a finite cap.
- Cancellation (DP-09): `Suite.run(cancel=CancellationToken)` — cooperative skip of not-yet-started probes.

### Changed
- Actionable hints on `TransportError` / `AuthError` messages (EH-09); documented the upstream-load bound (EH-13).

### Added
- Structured logging adoption (LG-02..17): logging configured at the console entry via
  `mgf.common.bootstrap`; `Suite.run` logs operation start/end at INFO + an OTel span; stable
  `event`/`fingerprint` fields on lifecycle + WARNING/ERROR records; `docs/reference/EVENTS.md`.

### Added
- Operability commands: `apiprobe --version` (rich: package + git SHA + Python + key deps, OP-01),
  `apiprobe explain` (effective configuration with provenance, secrets redacted, OP-05),
  `apiprobe bundle` (single diagnostic tarball: system info + redacted config + doctor, OP-07).

(no entries)

---


## [0.5.7] — 2026-06-08 — depth-audit close-out (version-drift fix + public API + CI)

### Fixed
- `_version.py` had drifted to 0.5.5 while pyproject was 0.5.6 (mislabeled release);
  both now 0.5.7, and CI tag-coherence asserts tag==pyproject==`__version__` (AP-13).

### Added
- `PUBLIC_API.json` + generator + CI pin (DOC-08); PUBLIC_API.md master-HEAD banner (REL-03).
- Tag-driven Codeberg publish pipeline + sbom step + import-linter UI-forbid contract;
  SCOPE.md + docs/testing README + [standards] extra (earlier batches).

### Deferred (documented in the conformance ledger — genuine feature work)
- CLI operability commands (rich `--version`, `doctor`, `explain`, `bundle` — OP-01/02/05/07),
  the structured-logging build-out across the probe pipeline (LG family), and async-probe
  cancellation/SIGINT (DP-09) are real feature work, tracked as open MUST gaps.

## [0.5.6] — 2026-06-06 — federation cohort: mgf-common 0.46 walk

Walk the `mgf-common` pin ceiling `<0.46 → <0.47` to accept the
library-API-neutral `mgf-common 0.46.0` (CLI-only release: new
`federation`/`design` commands + a `doctor --standards` gate; no library
surface change).

---

## [0.5.5] — 2026-06-04 — federation cohort: mgf-common 0.45 walk

PATCH: pin widened to `mgf-common>=0.41,<0.46` to accept mgf-common 0.45
(Codeberg cutover — PKG-01..12). `mgf-*` resolve from the `codeberg-mgf`
index (PKG-06). No API change.

---

## [0.5.4] — 2026-06-01 — federation cohort: adopt mgf-common 0.44

PATCH: pin widened to `mgf-common>=0.41,<0.45` to accept the mgf-common 0.44 line
(which extracted the standards corpus + federation contract to the
`mgf-standard` sibling; pulled in transitively). `FEDERATION.md` stub +
`.claude/commands/sync.md` repointed at `mgf-standard`. No API change.

---

## [0.5.3] — 2026-05-28 — federation pin walk (mgf-common 0.42.x)

Federation cohort wave: mgf-apiprobe now accepts **mgf-common 0.42.x** (pin
widened to `>=0.41,<0.43`). mgf-common 0.42.0 is an additive release
(unified-logging waves); no source impact here.

---

## [0.5.2] — 2026-05-26 — federation pin walks (mgf-common 0.41.x + mgf-test-supervisor PyPI)

Federation cohort walk: mgf-apiprobe now accepts mgf-common 0.41.x.
Previously pinned `mgf-common>=0.40,<0.41`, which is what made
PAPER-84 (cross-sibling consistency check in mgf-common 0.41.2)
correctly REFUSE bumps for consumers depending on both mgf-apiprobe
and mgf-common. This release widens mgf-apiprobe's pin to `<0.42`
so the federation cohort gate opens for downstream consumers.

### Changed

- `pyproject.toml`: `mgf-common>=0.40,<0.41` → `mgf-common>=0.40,<0.42`.
- `pyproject.toml`: `mgf-test-supervisor @ git+ssh://...` →
  `mgf-test-supervisor>=0.1.3,<0.2`. mgf-test-supervisor v0.1.3
  is now on PyPI.
- `pyproject.toml`: removed `[tool.hatch.metadata]
  allow-direct-references = true` block.

### Source-level changes

None. mgf-apiprobe has its own LOCAL `build_parser()` in
`cli/__main__.py` which is in its own namespace — unrelated to
the deleted `mgf.common.cli.build_parser` alias in mgf-common
0.41. Audit verified mgf-apiprobe doesn't import any deleted
mgf-common names.

---

## [0.5.1] — 2026-05-18

**Federation sync v2.10** — docs + tooling refresh; no library surface changes.

### Added
- `STARTHERE.md` + `CONTRIBUTING.md` (DOC-01 sibling MUSTs).
- `docs/CLAUDE.md` + `.claude/settings.json` (managed by `mgf-common catch-up`).

### Changed
- `mgf-common` pin: `>=0.36,<0.37` → `>=0.37,<0.38`.
- `_version.py` bumped to keep introspection in sync.

### Federation
- Aligned with mgf-common v0.38.0 contract (DOC-13/14/15/16/17). Catch-up clean.

---

## [0.5.0] — 2026-05-18

PyPI: <https://pypi.org/project/mgf-apiprobe/0.5.0/> · Tag: `v0.5.0`

> **Wave 2 sibling release — P-1 security-header checks +
> bundled inthewords-filed papers.** Four new default checks
> close the deferred SH-05 federation gap (CSP / Referrer-Policy
> / Permissions-Policy / Insecure-Cookie-Attributes). Also
> includes the `[Unreleased]` `Probe.depends_on` + BearerAuth
> scheme= + auto-derived `Probe.id` work (PAPER-39/40/41/42 —
> moved to this sibling's FEEDBACK per v2.8 DOC-13).
> Pin bump: mgf-common ≥0.34 → ≥0.36.

### Added — P-1 security-header checks (4 new default checks)

Closes the deferred SH-05 federation gap. The federation's
security-checks catalogue grows from 4 to 8; `DEFAULT_CHECKS`
goes from 18 → 22 entries.

- **`MissingCsp`** (`security.headers.missing-csp`, **WARN**) —
  browser-shaped responses without `Content-Security-Policy`
  (or `Content-Security-Policy-Report-Only`). Filters on
  `text/html` / `application/xhtml+xml` Content-Type — JSON
  API responses get a pass by default.
- **`MissingReferrerPolicy`** (`security.headers.missing-referrer-policy`,
  **INFO**) — response missing `Referrer-Policy`. Fires
  regardless of Content-Type (Referrer-Policy applies to
  every request the client might initiate downstream).
- **`MissingPermissionsPolicy`** (`security.headers.missing-permissions-policy`,
  **INFO**) — browser-shaped responses missing
  `Permissions-Policy` (the replacement for the deprecated
  `Feature-Policy`).
- **`InsecureCookieAttributes`** (`security.cookies.insecure-attributes`,
  **WARN**) — `Set-Cookie` missing `Secure` (over HTTPS) /
  `HttpOnly` / `SameSite`. Multi-cookie capable; emits one
  finding per cookie missing at least one attribute. Cookie
  parser correctly handles `Expires=Sun, 06 Nov 2024...`
  date-format commas.

15 new unit tests cover all four checks (silent / firing /
edge cases including content-type filtering + HTTPS-only
Secure semantics + multi-cookie parsing).

### Added — `Probe.depends_on` probe-to-probe data threading

Closes [PAPER-39](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
— the 🟡-High consumer-feedback entry filed by inthewords during
its starter-Suite adoption. The auth-bootstrap probe used to
require a pre-Suite httpx call entirely outside apiprobe to
thread its result into subsequent probes' headers; with this
change, the entire chain stays inside the Suite (findings count,
checks fire, retry / redaction posture preserved).

**`Probe.depends_on(probe_id, *, extract={var: jsonpath, ...})`**

Declares a data dependency on another probe in the same Suite.
After the upstream probe runs successfully, each `(var_name,
jsonpath_expr)` pair in `extract` is evaluated against the
upstream response body (parsed as JSON via `jsonpath-ng`) and
the resulting values are added to the Suite's `Context`. The
dependent probe's path / header values / query values can then
reference those variables via Python `str.format`-style
`{var_name}` placeholders, which are substituted at run time.

Auth chaining is the canonical use case (and the one PAPER-39
filed against):

```python
auth = (Probe.post('/auth/token/')
        .id('auth-bootstrap')
        .body({'username': 'alice', 'password': '...'})
        .expect_status(200))
profile = (Probe.get('/me/')
           .id('profile')
           .depends_on('auth-bootstrap', extract={'tok': '$.token'})
           .header('Authorization', 'Token {tok}'))
```

But the primitive subsumes general data threading too —
create-then-verify flows, dynamic-seed scenarios, anything where
probe N+1 needs a value from probe N's response.

**`ProbeDependency`** — public frozen dataclass exposing the
`probe_id` and `(var_name, jsonpath_expr)` tuple. Constructed
indirectly via `Probe.depends_on()`; consumers rarely instantiate
directly.

**`Context.with_added(name, value)`** — new public method on the
existing `Context` for adding a variable. Used internally by the
runner; consumers MAY use it to seed the initial Context.

**`resolve_template(template, context)`** — new public helper in
`mgf.apiprobe.core.context` that substitutes `{var}` placeholders
from a `Context`. Uses Python's `str.format_map` semantics so
literal braces escape via `{{`. Missing variables raise `KeyError`
with a clear message listing available variables.

### Suite changes

- **Topological ordering when any probe has dependencies.** Suite
  construction runs Kahn's algorithm over the dependency graph;
  the resulting order is stashed on the Suite. `Suite.run()`
  checks `any(p.depends_on_probes for p in self.probes)` and
  branches between the existing fast parallel-gather path (no
  deps) and a new serial topological executor (any deps). The
  parallel path is unchanged — all 806 existing tests still
  pass.

- **Dangling-reference + cycle validation at construction.** A
  `Probe.depends_on("typo-id", ...)` where no probe in the Suite
  has that id raises `SuiteLogicError` with the list of
  available ids. Cycles (self-referential or N-node) raise
  `SuiteLogicError` naming the cycle members.

- **Three new synthetic Finding codes** emitted by the
  topological executor on failure:

  - `suite.deps.skipped_due_to_upstream` — a probe was skipped
    because one of its dependencies failed (transport error,
    extraction error, or upstream cascade).
  - `suite.deps.extraction_failed` — the upstream response
    couldn't be parsed (body-not-json) or the JSONPath didn't
    match (jsonpath-no-match, jsonpath-parse-error).
  - `suite.deps.template_unresolved` — a `{var}` placeholder in
    path / header / query references a variable no upstream
    declared in its extract.

  All three carry CRITICAL severity (a missing dependency
  almost always means the suite's invariant is broken).

- **`Suite._run_one_probe` return signature widened** from
  `(findings, had_transport_error)` to `(findings,
  had_transport_error, response_or_None)`. The third element
  lets the topological runner cache responses for downstream
  extraction. The parallel-path call site drops the third
  element; no observable change to the parallel path's
  behaviour.

### Tests

23 new tests in `tests/unit/core/test_paper39_dependencies.py`
covering:

- `Probe.depends_on` builder (3 tests): single dep, accumulating
  multiple deps, returns-new-probe immutability.
- `ProbeDependency` dataclass (1 test): frozen.
- `Context.with_added` (2 tests): adds new variable, overrides
  existing.
- `resolve_template` (4 tests): basic substitution, no-placeholders
  fast path, literal braces, missing-variable KeyError with
  helpful message.
- Suite construction validation (4 tests): dangling probe_id,
  self-referential cycle, two-node cycle, no-deps back-compat.
- Suite topological run (5 tests): linear chain, auth-header
  template, branching (one upstream → two downstreams), query
  param template, nested JSONPath.
- Failure paths (3 tests): transport-error cascade,
  extraction-no-match, extraction-non-JSON-body.
- Back-compat (1 test): suites without deps unaffected.

Total: 806 → 829 tests.

### Docs

- `docs/recipes/how-to-chain-probes.md` — new recipe walking
  through the auth-bootstrap idiom + general data threading,
  with worked examples for each failure mode.
- `PUBLIC_API.md` — `Probe`, `Suite`, `Context` rows updated;
  new `ProbeDependency` row added.

---

### Added — polish sweep from inthewords consumer feedback

Three small additions closing
[PAPER-40](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md),
[PAPER-41](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md),
and [PAPER-42](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
filed by inthewords during its starter-Suite adoption.

- **PAPER-40 — `BearerAuth(scheme="Bearer")` kwarg.** The auth
  flow no longer hardcodes the `Bearer` scheme word. Pass
  `scheme="Token"` for Django REST Framework, `scheme="token"`
  (lowercase) for GitHub PATs, or a vendor-specific string like
  `scheme="AWS4-HMAC-SHA256"`. The default value is `"Bearer"`
  so existing call sites are unaffected. The scheme is
  validated at construction (must be a non-empty printable
  ASCII token per RFC 7235's `auth-scheme` ABNF — no whitespace
  or control characters).

- **PAPER-42 — `Probe` auto-derives a default `probe_id` from
  method + path.** The `Probe.get(path)` / `.post(path)` / etc.
  constructors now seed `probe_id` with an ASCII-safe derived
  label (e.g. `GET /api/v1/me/profile/` → `GET_api_v1_me_profile`,
  `POST /users/` → `POST_users`, `GET /` → `GET_root`).
  Consumers who care about prettier names still override via
  `.id("list-users")` — the builder replaces the default.
  Collisions (two probes with the same method + path producing
  the same derived id) are still rejected by
  `_assert_unique_probe_ids` at Suite construction with a
  message guiding the consumer to set explicit `.id()`.

- **PAPER-41 — `severity_overrides` recipe (docs-only).** New
  recipe at `docs/recipes/how-to-tune-findings-for-local-dev.md`
  showing how to demote noisy `MissingHsts` /
  `MissingRateLimitHeaders` findings on local dev stacks
  (HTTP-only, mostly-unlimited endpoints) without disabling the
  signal in production / CI. Approach A from the filing —
  documents the existing public surface rather than changing
  it.

13 new unit tests in `tests/unit/auth/test_flows.py` and
`tests/unit/core/test_probe.py`: 7 for `BearerAuth` (default
scheme preserved, DRF `Token`, GitHub `token`, vendor-specific,
empty / whitespace / control-char rejected) + 6 for `Probe`
auto-derive. Total 793 → 806 tests; one existing immutability
test updated to match the new auto-derive default (the test's
invariant — that builders return new probes without mutating
the original — still holds).

---

## [0.4.0] — 2026-05-17

PyPI: <https://pypi.org/project/mgf-apiprobe/0.4.0/> · Tag: `v0.4.0`

> **Quality release** — closes every P0/P1 + most P2 tracker
> items. One consumer-visible behaviour change: when a Suite
> runs with `verify_tls=False`, the report now leads with a
> synthetic WARN Finding (SH-06).

### Added — synthetic Finding for `verify_tls=False` (SH-06)

When `ApiprobeSettings(verify_tls=False)` is in effect,
`Suite.run` now inserts a WARN Finding at the front of the
report:

```
Finding(
    code="harness.config.tls-disabled",
    severity=Severity.WARN,
    root_cause=RootCause.INFRASTRUCTURE,
    evidence={"verify_tls": "false"},
    remediation="...",
)
```

Operators reading the report see a visible signal at index 0
that TLS-protection claims are unverifiable.

### Added — regression-protection tests

- **PC-06** — matcher DSL evaluation pin
  (`tests/perf/test_matcher_evaluation.py`). Each public
  matcher (11 total — `equals`, `gt`, `in_`, `matches`,
  `is_uuid`, `is_iso8601_z`, `is_url`, `is_email`, `is_iban`,
  `is_imei`, `is_e164`) pinned at ≤ 5 µs/call across 10 000
  iterations. Parametrised so a regression in one matcher
  fails with its name in the assertion message.
- **PC-08** — report serialisation throughput pin
  (`tests/perf/test_report_serialization.py`).
  `JsonReportWriter.write()` on a synthetic 5000-finding
  Report pinned ≤ 100 ms (local: ~3 ms).

### Fixed

- **Version drift** — `_version.py` now matches
  `pyproject.toml` (was 0.2.1 vs 0.3.0; `test_version_format`
  failing since the 0.3.0 cut).

### Changed — internal-only

- **RA-04** flipped to closed — the cookie CR/LF guard
  (`_assert_no_crlf_in_cookies`) was already shipped in
  `httpx_transport.py:115` + backed by an existing unit test;
  tracker entry was stale.
- **RA-01..03** — defensive wraps in `Suite.run` (return-
  exceptions on gather; per-probe try/except yielding
  synthetic CRITICAL Findings); probe-id + check.code
  uniqueness enforced at suite-build time; severity-override
  audit log emits structured `audit=true` records.
- **SH-01..04** — Finding `evidence` scrubbed through
  mgf-common's Redactor; `BodyEchoesSecret` needle scrubbed
  from in-memory representation; auth credential lifecycle
  documented; CRLF rejection on header + cookie values at
  the transport boundary.
- **PC-01..04** — suite throughput floor; per-probe wrapper
  tax pinned; per-check + chain iteration cost pinned;
  cold-import wall-clock pinned.

### Changed — pin

- **`mgf-common>=0.33,<0.34` → `mgf-common>=0.34,<0.35`**.

### Deferred to v1.1+ (decision-of-record)

- **RA-05** (`follow_redirects=` kwarg pass-through),
  **RA-06** (pool eviction strategy), **RA-07**
  (`allow_empty=` on auth flow constructors) — small kwarg
  additions; better validated post-v1.0 against real
  consumer feedback.
- **SH-05** (CSP / Referrer-Policy / Permissions-Policy
  checks), **SH-07** (federation-wide SBOM).
- **PC-05** (memory baseline), **PC-07** (Finding evidence
  allocation profile) — no clear v1.0 regression trigger.

### Notes for consumers

- Reports emitted from `verify_tls=False` runs now contain
  one extra Finding at index 0. If you index findings by
  position (rare), shift by 1 OR filter by `code`.
- `cause_summary` semantics in the underlying mgf-http
  transport changed in mgf-http 0.3.0 (category-only
  strings); see mgf-http's CHANGELOG.

---

## [0.3.0] — 2026-05-15

PyPI: <https://pypi.org/project/mgf-apiprobe/0.3.0/> · Tag: `v0.3.0`

> **v2.6 + v2.7 standards-compliance release.** Brings
> `mgf-apiprobe` to the federation-sibling compliant shape:
> backfills missing root + bucket docs that v2.6/v2.7 made
> mandatory (LICENSE, CHANGELOG.md, SECURITY.md,
> docs/standards/README.md, docs/cutover/, docs/recipes/) and
> moves docs/CLAUDE.md → docs/claude/CLAUDE.md per **DOC-07**.
> No code changes; documentation-only.

### Added — standards-compliance shape (v2.6 + v2.7)

- **`LICENSE`** at repo root (MIT) — required for any project
  distributed to others per v2.6 universal-four.
- **`CHANGELOG.md`** (this file) — Keep-a-Changelog format
  per **DOC-04**. Backfills the historical record for v0.1.0
  through v0.2.1 from the git log.
- **`SECURITY.md`** at repo root — vulnerability-reporting
  policy per **SC-12** + the v2.6 universal-four root docs.
  Scope (probe execution, auth-flow runners, schema
  validators, factories), out-of-scope notes for transitive
  deps + consumer-authored probe definitions.
- **`docs/standards/README.md`** — pointer + project-specific
  extension stub per **DOC-02** §2.0.4 Shape B (the existing
  `docs/standards/SCOPE.md` becomes a documented Shape-B
  extension). Cross-project standards live in
  `mgf-standard/docs/standards/`; mgf-apiprobe inherits them.
  Conformance target: L1.
- **`docs/cutover/`** — bucket created (empty until first
  breaking change; per **DOC-02**).
- **`docs/recipes/`** — bucket created (empty initial state;
  to be populated with task-shaped how-tos as the surface
  stabilises).

### Changed

- **`docs/CLAUDE.md` → `docs/claude/CLAUDE.md`** — moved per
  **DOC-07**. Each project's AI-agent dense-lookup file lives
  at the canonical path so cross-project tooling
  (`mgf-common catch-up`) finds it consistently. The
  auto-generated block between `BEGIN mgf-common auto-generated`
  / `END mgf-common auto-generated` markers is preserved.

### Engineering

- No code changes. `src/mgf/apiprobe/` byte-identical to
  v0.2.1.
- `mgf-common>=0.33,<0.34` runtime dependency unchanged from
  v0.2.1.

---

## [0.2.1] — 2026-05-11

> Walked `mgf-common` pin to `>=0.33,<0.34` for compatibility
> with the v0.33 federation-leaf shape (post-AP-12 removals:
> `configure`, `make_settings_config`, `EnvironmentError`
> alias).

### Changed

- Bumped `mgf-common` pin from `>=0.30,<0.31` to
  `>=0.33,<0.34`. Source is clean of the AP-12 removals.

---

## [0.2.0] — 2026-05-10

> Federation v0.30 catch-up. Bumped `mgf-common` pin from
> `>=0.12,<0.13` to `>=0.30,<0.31` after the framework-
> adapter sibling extractions stabilised. Also switched
> Codeberg URLs from `/branch/master/` to `/branch/main/`.

### Changed

- Bumped `mgf-common` pin from `>=0.12,<0.13` →
  `>=0.30,<0.31`.
- Codeberg link URLs switched from `master` → `main` branch.

---

## [0.1.0] — 2026-04-30

PyPI: <https://pypi.org/project/mgf-apiprobe/0.1.0/> · Tag: `v0.1.0`

> **Maiden voyage** — REST API verification primitives
> (typed probes, checks, and findings). Sibling of
> `mgf-common`.

### Added

- **Probe runner** — async-aware probe execution engine that
  drives a list of probes through configured auth flows and
  reports typed findings.
- **Probe definitions** — typed probe shapes for the common
  REST verification cases (status-code check, body-schema
  check, header-shape check, latency budget, idempotency
  invariant).
- **Auth flows** — pluggable runners for the common auth
  shapes (bearer token, cookie session, mTLS, OAuth2 client
  credentials).
- **Schema validators** — pydantic-backed schema validators
  for response-body assertions; JSON-schema-backed validators
  for cross-language fixture compatibility.
- **Factories** — built-in factory functions for common probe
  shapes so consumers can compose probes without
  re-implementing the boilerplate.
- **Configuration** — `MgfSettings`-based config with typed
  environment variables; subclass-friendly for project-
  specific extension.
- **PUBLIC_API.md + AP-10 sync** — public-surface
  documentation + the AP-10 mirror test (`tests/public_surface/`).

The 0.1.0 shape was pinned against `mgf-common>=0.12,<0.13`
(the pre-federation cornerstone).
