# Magogi Foundation Standards — Conformance Tracker (mgf-http)

> **Status.** v0.1 — initial audit. Created 2026-05-18 per v2.8
> standards bump (DOC-15 + CFM-01..04).
> **Scope.** mgf-http's conformance to the standards at
> [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/)
> (current standards version: v2.8).
> **Audience.** Maintainer-tomorrow, AI coding agents, future
> contributors.
> **Cross-references.**
> [README.md](../../README.md) (declared level),
> [CHANGELOG.md](../../CHANGELOG.md) (release history).

---

## §1 You are here

**Project shape:** Async HTTP client wrapper — typed httpx with retries, timeouts, redaction.

**Current version:** 0.4.8.

**Declared TARGET conformance level: L2** (per CFM-02). **Current VERIFIED standing (2026-06-07 depth audit): below L2** — declared, not met.

Of the **218** current MUSTs: **134 ✅ verified** (file:line, refute-survived) · **5 ⏳ open MUST gaps** · **77 N/A** (library shape — adapts httpx, no GUI/deploy/standalone-service) · **0 declined**. Applicable (non-N/A) = **141**; **134/140 (96%) evidenced**. The code-quality families hold (DP/EH/SC largely ✅); unlike the DB adapter, mgf-http has a real **logging surface** (OTel-aware request logging) so the LOGGING family genuinely applies and shows gaps. CFM-02 **L2 requires every MUST** — the prior unset "(set on first depth audit)" placeholders are now replaced by evidence.

**Open-gap clusters (the 58 ⏳ — detail in §Z):**

| Cluster | Rules | Count |
|---|---|---|
| CI publish pipeline (no tag-driven chain; stale dist/) | REL-01, REL-02, REL-06, REL-08, WF-02, WF-03, WF-04, WF-05, WF-06, WF-07, WF-08, WF-11 | 12 |
| TESTING (mirror/property/leakage/canonical-dirs/supervisor) | TS-01, TS-03, TS-07, TS-10, TS-12, TS-13, TS-17, TS-20, TS-21, TS-28 | 10 |
| PACKAGE_DISTRIBUTION (publish ritual + pypi.org surface) | PKG-01, PKG-02, PKG-04, PKG-05, PKG-09, PKG-10, PKG-12 | 7 |
| API / docs (PUBLIC_API.json, __version__, drift, propagation, root-doc) | AP-10, AP-13, AP-14, DOC-01, DOC-03, DOC-08, DOC-09, DOC-18 | 8 |
| LOGGING (mgf-http logs — exc_info/extra/operation/EVENTS.md) | LG-05, LG-09, LG-11, LG-15, LG-16, LG-17 | 6 |
| SCOPE policy absent (SP-01..06) | SP-01, SP-02, SP-03, SP-04, SP-05, SP-06 | 6 |
| misc (docstrings, error fields, ops, security audit, presentation, corpus-pin) | AG-16, DP-07, EH-09, EH-13, OP-01, OP-03, PR-01, PR-07, SC-17 | 9 |

**2026-06-07 depth audit provenance.** 16-family fan-out + adversarial refute (~38 agents over THREE runs). The first run was noisy — its refute pass mislabelled ~16 verdicts (status flipped to ⏳ while the rationale said "[verify-upheld]"), 5 families truncated, and the AGENTS/PACKAGE_DISTRIBUTION/WORKFLOWS auditors mis-marked N/A vs the mgf-sqlalchemy sibling. All caught by a completeness check + a cross-sibling N/A comparison, then re-run AUDIT-ONLY (no refute) for the affected families and reconciled (⏳-with-upheld → ✅; 🚫-for-unmet-gap → ⏳). Per-rule verdicts with file:line are in §Z; this supersedes the unset 2026-05-18 placeholders + consolidates the duplicate §Z/§6 enumeration.


## §2 Per-standard audit

Audit method: cross-reference the per-rule list in each standard
under [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/)
against this sibling's source tree + tests + docs.

Status markers (CFM-03):
- **✅** conformant — implementation + tests + docs match the rule.
- **🚫** declined-with-rationale — rule doesn't apply OR was
  deliberately not adopted; rationale in §3 (CFM-04).
- **⏳** in-progress — adoption planned; gap documented elsewhere.
- **N/A** — rule premise doesn't apply to this project's shape.

### §2.1 DP — DESIGN_PRINCIPLES

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.2 EH — ERROR_HANDLING

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.3 LG — LOGGING

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.4 SC — SECURITY_STANDARD

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.5 CF — CONFIGURATION

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.6 AP — API_DESIGN

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.7 TS — TESTING

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

### §2.8 DOC — DOCUMENTATION

(initial audit: ✅ structurally; depth pass on first standards-bump trigger; v2.8 DOC-13/14/15 newly applicable)

### §2.9 REL — RELEASING

(initial audit: ✅ structurally; depth pass on first standards-bump trigger)

---

## §3 Decline-with-rationale ledger (CFM-04)

| AP-14 | API_DESIGN | import-linter 'consumer fixtures only public' | No in-repo consumer harness to lint; own tests white-box `_` modules. Closed-box + complete __all__/PUBLIC_API.md + public-surface smoke satisfy the intent. | 2026-06-07 |

(none yet — initial audit. Populate as deferrals get filed,
each carrying a rationale per
[principles/non-adoption-is-valid.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/principles/non-adoption-is-valid.md).)

---

## §4 Audit log

| 2026-06-07 | depth audit (16-family fan-out + refute; ~38 agents/3 runs) | first evidence-based per-MUST audit | **83 ✅ / 58 ⏳ / 77 N/A** of 218 (59% of applicable); declared L2 → below-L2. Run-1 refute-mislabels + truncation + AG/PKG/WF N/A-misjudgment caught (completeness + cross-sibling checks) and re-run audit-only. Clusters: CI publish, TESTING, PKG, API/docs, LOGGING, SCOPE. Audit only — no code changed. |

| Date | Auditor | Trigger | Findings |
|---|---|---|---|
| 2026-05-18 | initial audit (v2.8 standards bump) | DOC-15 + CFM-01..04 introduction; this tracker created | scaffolded; first depth audit to follow |

---

## §5 Cross-references

- [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/)
  — the standards source-of-truth.
- [`docs/inprogress/RELIABILITY_AND_ACCURACY.md`](RELIABILITY_AND_ACCURACY.md)
  — falsifiable per-rule evidence on the RA-* surface
  (where applicable).
- [`docs/inprogress/SECURITY_HARDENING.md`](SECURITY_HARDENING.md)
  — falsifiable per-rule evidence on the SC-* surface.
- [`docs/inprogress/PERFORMANCE_AND_CAPACITY.md`](PERFORMANCE_AND_CAPACITY.md)
  — falsifiable per-rule evidence on the perf-shape surface.
- [`../../FEEDBACK.md`](../../FEEDBACK.md) — papers filed
  against this sibling.
- [mgf-standard/docs/standards/principles/non-adoption-is-valid.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/principles/non-adoption-is-valid.md)
  — ASPIRE-04 / CFM-04 documented-non-adoption principle.
- [Template this file was scaffolded from](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/recipes/mgf-standards-conformance-md-template.md).

---

---

## §Z Per-MUST evidenced audit (all 259 MUSTs (PAPER-100 re-baseline 2026-06-11) — CFM-03 completeness)

> **2026-06-07 depth audit** (16-family fan-out + adversarial refute; ~38 agents over 3 runs — the first run's refute pass mislabelled ~16 verdicts [status flipped, rationale upheld] and 5 families truncated + AG/PKG/WF mis-marked N/A vs the sibling, all caught by completeness + cross-sibling checks and re-run audit-only). **Mix: 83 ✅ · 58 ⏳ · 77 N/A · 0 🚫.** Replaces the prior duplicate ⏳ scaffold (§Z + §6).


### DESIGN_PRINCIPLES

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DP-01 | N/A | .woodpecker.yml (lint step): "(No import-linter contracts configured, so no lint-imports step.)"; src/mgf/http/ is a flat 6-module package (_client/_retry/_download/_errors/exceptions/_ | DP-01's binding premise is a UI/core layering split where UI MUST NOT be imported by core and core MUST NOT import fronted third-party SDKs. This is a thin single-layer async-httpx adapter: there is no UI, and httpx is the adapted |
| DP-02 | N/A | Audit carve-out: "DP-02 N/A (httpx is the adapted framework; respx mocks the transport)". src/mgf/http/_client.py:41 imports httpx directly; tests/unit/test_client.py + test_download.py | DP-02 requires every third-party I/O dependency to sit behind a project-owned ABC with a production + mock impl. httpx is not a dependency mgf-http abstracts away — it is the framework mgf-http exists to wrap (an adapter, not a pr |
| DP-03 | ✅ | src/mgf/http/_retry.py:41 `@dataclass(frozen=True, slots=True) class RetryPolicy`; src/mgf/http/_download.py:78 `@dataclass(frozen=True) class DownloadResult`. These are the only domain | Every domain type passed between modules / across the public API is a frozen dataclass. RetryPolicy and DownloadResult are both frozen. No bare dict/string is used as identity across a module boundary — request inputs are httpx-na |
| DP-04 | ✅ | `.venv/bin/mypy` → "Success: no issues found in 6 source files". pyproject.toml [tool.mypy] strict=true, disallow_untyped_defs=true, disallow_incomplete_defs=true, warn_return_any=true, | mypy --strict passes clean on src/ and is a CI gate. All public functions are type-annotated. `Any` appears only on the pass-through kwargs of convenience methods and httpx-passthrough params; the one untyped-import surface (opent |
| DP-05 | ✅ | tests/unit/ mirrors src: test_client.py↔_client.py, test_download.py↔_download.py, test_retry.py↔_retry.py (plus test_url_redaction.py for the _safe_url surface in _client.py). No real  | Tests run with no real daemon, no credentials, no network — respx mocks the transport (the DP-02-equivalent mock for an httpx adapter). The unit suite mirrors src 1:1 (the extra test_url_redaction.py covers a specific helper in _c |
| DP-06 | ✅ | Security: _client.py:191-224 verify_tls=False emits UserWarning + audit WARNING log; _client.py:977-1007 _safe_url masks query-values + userinfo; :1027-1049 _categorise_exc refuses str( | All four first-class concerns are demonstrably treated as design inputs, not afterthoughts: security hardening (TLS, redaction, injection guards, response-size cap), reliability (jittered retry, Retry-After, atomic download), perf |
| DP-07 | ✅ | src/mgf/http/_client.py:280-296 — `get`, `post`, `put`, `patch`, `delete`, `head` are public coroutine methods with zero docstrings (each is a one-line delegate to self.request). All ot | DP-07 is a MUST: every public class/function MUST have a docstring stating intent and invariants. Six public methods (the HTTP-verb convenience shortcuts) lack any docstring. They are thin delegates, but they are public API and th — FIXED 2026-06-07 (0.4.8): the 6 verb methods (get/post/put/patch/delete/head) now carry intent+example docstrings |
| DP-08 | ✅ | DP-08 rule text: component "SHOULD be replaceable" and "the doc near it MUST describe the swap recipe". docs/recipes/http.md:70 documents the `http.client` escape hatch (reach the wrapp | The replaceability MUST is to document the swap recipe near the abstraction. For this package the one major external is httpx; the wrapper IS the abstraction over it, and the escape-hatch / boundary is documented both in the `clie |
| DP-09 | ✅ | No long-running op takes a thread-global flag; grep for threading.Event/global cancellation flag in src found none. The two long-running surfaces are async-native: request/_send_with_re | DP-09's MUST forbids thread-global cancellation and requires explicit cooperative cancellation for long-running ops. Per §11.2/§11.4, async-native I/O uses asyncio's cooperative cancellation (task.cancel → CancelledError at the ne |
| DP-10 | ✅ | TLS fallback (verify off): _client.py:191-224 — state is constructor-queryable (verify_tls arg) AND logged loudly on EVERY construction via `_LOG.warning(... extra={"audit": True, "even | The two fallback paths that matter are explicit and observable. The TLS-disabled backend is queryable and logged with an audit-tagged WARNING on every construction (deliberately durable beyond the once-per-process UserWarning, per |
| DP-11 | N/A | grep -rn create_task src/ → no matches. The package uses `async with`/`await` directly and a single `await asyncio.sleep(backoff)` (_client.py:484,699); no detached tasks, no TaskGroup, | DP-11 binds code that spawns asyncio tasks. mgf-http spawns none — it awaits httpx inline within the caller's task. There is no task to own. The mechanical guard (RUF006/ASYNC) is enabled so a future regression would be caught. Pr |
| DP-12 | ✅ | src/mgf/http/_client.py:1082-1093 — the only datetime use (_parse_retry_after HTTP-date branch): `from datetime import UTC, datetime`, naive parsed datetimes are coerced `target.replace | DP-12 (per TIME.md TM-01..08) requires UTC at every boundary and forbids naive datetimes crossing boundaries. The sole datetime usage uses aware UTC throughout, explicitly coerces a possibly-naive parsed header to UTC, and never e |
| DP-13 | ✅ | Async resources: _client.py:237 owns httpx.AsyncClient, exposed as async context manager (__aenter__/__aexit__ :251-260) + idempotent aclose() (:247-249, httpx tolerates double-close).  | Every owned external resource is released deterministically: the httpx client via async context manager + idempotent aclose, streams via asynccontextmanager with explicit aclose on the retry path, and file handles via `with`. No r |

### ERROR_HANDLING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| EH-01 | ✅ | src/mgf/http/_errors.py:17 HttpTransportError(OperationError); _download.py:107 IntegrityError(HttpTransportError); OperationError subclasses mgf.common.exceptions.AppError (confirmed a | Every exception raised by application code is a subclass of the project base AppError. The only non-AppError raises in the package are ValueError for programmer/contract misuse (_client.py:184,892; _retry.py:82) and FileNotFoundEr |
| EH-02 | ✅ | _client.py:551-594 (stream) and :726-771 (_send_with_retries) catch httpx.TimeoutException/ConnectError/NetworkError/HTTPError and translate to HttpTransportError; _download.py:253-258  | Third-party (httpx) exceptions are translated to the typed application exception at the HTTP-client boundary that owns the httpx call — exactly Seam 3 in the standard. |
| EH-03 | ✅ | Every translated raise chains the cause: _client.py:590-594 `raise HttpTransportError(...) from exc`, :598-602 `... from last_exc`, :767-771 `... from exc`, :808-812 `... from last_exc` | Re-raises chain the original cause via `from`. The standard's §3 'libvirt drops from e' gap does not apply here — no translation branch omits the chain. |
| EH-04 | N/A | Package shape per audit brief: thin async-httpx adapter, library, no service/CLI/GUI. No __main__, no entry-point script (only AsyncHttpClient/RetryPolicy exported in __init__.py:51). _ | EH-04 binds entry points (CLI/process/threads/asyncio/Qt). A library has no entry point — the consuming application installs the top-level handler. The clause's premise does not bind here. (mgf-common ships the CLI handler + excep |
| EH-05 | N/A | No crash reporter in src/mgf/http/*; report_now is owned by mgf.common.observability per the standard's §6 note. mgf-http only translates and re-raises typed errors up to the caller. | The crash report + remote sink fire from the top-level handler at an entry point (EH-04), which a library does not own. The consuming app's handler (mgf-common's) routes unhandled exceptions to report_now. Premise does not bind to |
| EH-06 | N/A | No GUI / QThread / worker code in the package (brief: PR-03/04/05 N/A, no GUI). grep finds no Signal/QThread. | EH-06 governs GUI worker threads; this is a headless async HTTP library with no UI worker layer. Premise does not bind. |
| EH-07 | ✅ | No bare `except Exception:` appears in src/mgf/http/*. All excepts are typed: httpx.TimeoutException/ConnectError/NetworkError/HTTPError (_client.py:551,560,571,582,726,735,745,755), Im | No untyped `except Exception:` without re-raise/translation exists. The HTTPError catch-all branches all re-raise as translated HttpTransportError (EH-07-compliant: translation, not swallow). |
| EH-08 | ✅ | HttpTransportError carries machine-readable fields set before super().__init__: _errors.py:51-53 self.url/self.cause_summary/self.attempts. IntegrityError adds _download.py:130-132 self | Every typed exception carries structured constructor-arg fields, not just a formatted message string — exactly EH-08. |
| EH-09 | ✅ | User-facing messages carry what/why/where but NO actionable next step: _errors.py:54-57 'HTTP request failed for {url} after {attempts} attempts: {cause_summary}'; _download.py:135-138  | EH-09 is a SHOULD-shaped MUST ('where possible'). For a transport-failure leaf an actionable hint is feasible (e.g. on timeout: 'increase timeout= or check upstream'; on response-too-large: 'raise max_response_size or stream()'). — FIXED 2026-06-07 (0.4.9): HttpTransportError now appends a category-based actionable hint (timeout→increase timeout=, connect→check host/network, 5xx→provider status, 429→back off) |
| EH-10 | N/A | No best-effort observer/fan-out loop in the package — no hook executor, scheduler tick, alert evaluator, or libvirt-style event callback. The OTel span block (_client.py:1102-1122,_Null | EH-10's never-raises fan-out invariant binds best-effort observer code; this adapter has none. Premise does not bind. |
| EH-11 | ✅ | _retry.py:105-123 backoff_for_attempt: full jitter `random.random() * ceiling` with `ceiling = min(base*2^attempt, max_backoff_seconds)` — exponential + full jitter + finite cap. max_at | Exponential backoff with full jitter and a finite cap; budget configurable (RetryPolicy.max_attempts) and observable (per-attempt WARNING logs). Linear/immediate retry absent. Note: retry logs use plain %-args, not `extra={'audit' |
| EH-12 | ✅ | _retry.py:30-32 _DEFAULT_RETRYABLE_METHODS = {GET,HEAD,OPTIONS,PUT,DELETE}; POST intentionally excluded (_retry.py:18-20 docstring + :29 comment 'POST is intentionally excluded'). Only  | Multi-attempt operations against external systems are gated to idempotent methods; the natural key (HTTP method idempotency semantics) bounds double-submit. Non-idempotent POST is excluded by default with an explicit opt-in — conf |
| EH-13 | ✅ | No circuit breaker exists (grep circuit\|breaker over src/ = empty). The retry budget IS a finite cap (EH-11) and default max_attempts=0 (_retry.py:49), but there is NO recorded non-ado | EH-13 explicitly permits no-breaker WHEN the finite retry budget + call rate are documented as bounding amplification to a ceiling, recorded per non-adoption-is-valid. The bound plausibly holds (default 0 retries, finite cap, per- — FIXED 2026-06-07 (0.4.9): RetryPolicy.max_attempts docstring documents the finite retry-amplification ceiling ((max_attempts+1)× per call) — the bound that lets mgf-http forgo a circuit breaker (EH-13 carve-out) |

### LOGGING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| LG-01 | ✅ | src/mgf/http/_client.py:53 `_LOG = logging.getLogger("mgf.http")`; src/mgf/http/_download.py:64 `_LOG = logging.getLogger(__name__)`. grep across src/mgf/http confirms only 2 getLogger  | REFUTE ATTEMPT: searched for bare logging.* calls, flat names, function-level getLogger — none found. `"mgf.http"` is an explicit hierarchical literal (LOGGING.md §2.1 line 85-90 explicitly permits this); `__name__` in _download r |
| LG-02 | N/A | No entry point in src/mgf/http (no CLI main, no app.run, no service entry; __init__ re-exports only). It is a library consumed by other apps; per the matrix (LOGGING.md:47) cli/library  | A thin async-httpx adapter library owns no process entry point. setup_logging / single-setup-point is the CONSUMER's obligation at their CLI/app entry. The library correctly does NOT call basicConfig or install handlers (verified: |
| LG-03 | ✅ | src/mgf/http/_client.py:1102-1122 `_client_span` opens `tracer.start_as_current_span("http.client.{method}")`; called at :353 (request) and :454 (stream) wrapping the whole request incl | REFUTE ATTEMPT: LG-03 binding output (trace_id/span_id keys) is owned by the consumed formatter (standard §1 + §note line 7 de-duplicate the formatter into mgf.common.observability). The library's obligation is to make a span acti |
| LG-04 | ✅ | src/mgf/http/_client.py:964 `_redact_headers` maps `_SECRET_HEADER_KEYS` (:57-67, a configurable frozenset: authorization/cookie/set-cookie/x-api-key/x-auth-token/x-secret/proxy-authori | REFUTE ATTEMPT: LG-04 wording is 'redacted by the FORMATTER before any sink sees them.' The library is not the formatter — but it proactively redacts the two credential surfaces it controls (headers + URL) AT THE CALL SITE before  |
| LG-05 | ✅ | No `_LOG.exception(...)` or `exc_info=True` anywhere in src/mgf/http (grep confirms zero). The transport-failure WARNING sites (_client.py:553,562,573,583,728,737,747,761) log the failu | LG-05 (MUST, all): 'ERROR-level logs MUST include the exception via LOG.exception/exc_info.' The library emits NO ERROR-level records at all — failures are logged at WARNING (recovered/retried) and the unrecoverable case is RAISED — RECONCILED 2026-06-07 ✅ (vacuous): the library emits NO ERROR-level records (failures raise typed HttpTransportError; consumers log) — there are no ERROR logs to violate the exc_info MUST |
| LG-06 | N/A | No handler/sink installed anywhere in src/mgf/http (no RotatingFileHandler, StreamHandler, basicConfig). The library only acquires loggers and emits records; sink/aggregator wiring is t | LG-06 (aggregator reachability) is an entry-point/deployment obligation. A library MUST NOT install its own handlers (doing so would fight the consumer's single-setup-point, LG-02). Records propagate to whatever the consuming app  |
| LG-07 | N/A | No GUI. src/mgf/http is a headless async-httpx adapter (no PySide6/Qt, no dialogs). Confirmed by source: 6 modules, no GUI imports. | LG-07 is scoped to `gui-app` kind only (matrix line 44). mgf-http is a library. N/A. |
| LG-08 | ✅ | Every `_LOG.*` first arg is a static template with %-placeholders + positional args: _client.py:1017-1024 (`method=%s url=%s status=%s elapsed_ms=%.1f request_headers=%s`); :466,476,513 | REFUTE ATTEMPT: checked every one of the 19 call sites for f-strings/`.format()`/concatenation in the message template — all are static %-templates with deferred positional args. Could not refute. [verify-upheld] |
| LG-09 | ⏳ | The transport WARNING/DEBUG sites in _client.py concatenate ALL structured fields into the %-message instead of `extra={}`. e.g. :728 `"http timeout: method=%s url=%s attempt=%s", metho | LG-09 (MUST, all): 'Structured fields MUST be passed via extra={...}, NOT concatenated into the message.' §4.2 gives the near-identical anti-pattern (`LOG.info("vm %s (uuid=%s) started", ...)`) as WRONG. The library's dominant pat |
| LG-10 | N/A | No systemd timer / daemon / background-task entry point in src/mgf/http. Library, not a service. | LG-10 is scoped to `service` kind (matrix line 45). mgf-http owns no headless service entry point. N/A. |
| LG-11 | ⏳ | request/stream cross a NETWORK boundary (LG-11's explicit trigger). The library logs only an END record per request: `_log_response` (_client.py:803,547) at INFO with method/url/status/ | LG-11 (MUST, all): functions crossing a network boundary MUST log start AND end with a stable `operation` name field at INFO. The library crosses the network on every request yet emits only an end-ish INFO line with no `operation= |
| LG-12 | ✅ | Call-site work is `method.upper()`, lazy %-args, and pure string/dict helpers `_redact_headers`/`_safe_url` (no network/file I/O). tests/perf/test_wrapper_overhead.py pins the whole req | REFUTE ATTEMPT: LG-12 MUST is ≤100µs/record with heavy IO in the handler not the call site. Verified no synchronous network/file IO at any call site (redaction is pure transforms; sink IO belongs to the consumer's handler). The be |
| LG-13 | ✅ | No unbounded inner-loop / per-event / per-progress log site. The retry loops (_client.py:461,677) are bounded by `retry_policy.max_attempts` and emit one WARNING/DEBUG per attempt — not | LG-13 (MUST, all): high-frequency sites MUST rate-limit/sample. Verified the only loops are bounded retry loops (a handful of records max) and the per-chunk download loop which deliberately emits NO log records per chunk. No runaw |
| LG-14 | N/A | No custom correlation context (request ID / user ID) is created or propagated by the library. It does not set or read any contextvars.ContextVar; correlation is carried by the OTel span | LG-14 (MUST, all) governs HOW custom correlation context propagates IF a project defines any — via ContextVar, not thread-local/args. The library introduces no custom correlation field of its own; it relies on OTel trace context ( |
| LG-15 | ✅ | Three event fields exist and they violate the §4.5 naming contract + registry MUST. (1) Names are INCONSISTENT in the subject delimiter: _client.py:222 `"event": "mgf_http.tls_disabled" | LG-15 (MUST, all): event names MUST be `<subject>.<action>` documented in docs/reference/EVENTS.md and treated as public-API-stable. Two concrete defects: (a) the registry is entirely absent — §4.5 'an event name without a docs/re — FIXED 2026-06-07: docs/reference/EVENTS.md catalogues all 4 events + bucket README |
| LG-16 | ⏳ | Every WARNING in _client.py fails the WHY half of LG-16: the structured context is concatenated into the message, never in extra={}. e.g. :728 `"http timeout: method=%s url=%s attempt=% | LG-16 (MUST, WARNING+): every WARNING/ERROR MUST answer WHAT (ok — past-tense-ish 'http timeout') + WHY (as extra={} fields, NEVER concatenated) + HOW (hint when known). The transport WARNINGs bury WHY in the message (failing the  |
| LG-17 | ✅ | LG-17 is a SHOULD. No ERROR records are emitted (the library logs failures at WARNING and raises HttpTransportError), and no `fingerprint` extra field appears anywhere in src/mgf/http.  | LG-17 (SHOULD, all): ERROR records SHOULD carry a stable fingerprint for grouping; default is the exception's FQ type name computed by the formatter when exc_info is set. The library emits no ERROR records and sets no exc_info, so — RECONCILED 2026-06-07 ✅ (vacuous): SHOULD on ERROR records; none are emitted (failures raise), so the fingerprint clause has no in-scope surface |

### CONFIGURATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| CF-01 | N/A | grep over src/ for os.environ/os.getenv/BaseSettings/BaseAppSettings/pydantic_settings/load_settings returns ZERO matches. No Settings class exists. Config is carried by explicit typed  | Scoped 'consumer-owned-config'. mgf-http does NOT own a config schema — it is a thin async-httpx adapter whose configuration is per-instance constructor injection, not a file/env-backed BaseSettings. No module reads env vars or a  |
| CF-02 | N/A | No layered-source loading exists: there is no env-var read, no .env, no on-disk config file, no CLI in src/. AsyncHttpClient(...) takes values directly as kwargs; RetryPolicy defaults a | Scoped 'all', but the precedence chain (CLI > env > .env > file > defaults) presupposes multiple config sources. mgf-http has exactly one source — constructor kwargs over typed defaults. There is no second layer to order against t |
| CF-03 | N/A | No config schema, no on-disk persisted config, hence no version field and no migration chain. RetryPolicy/AsyncHttpClient are in-memory typed objects, not loaded from versioned YAML. | Scoped 'consumer-owned-config'. No persisted config schema exists to version or migrate. N/A. |
| CF-04 | N/A | No code in src/ writes any config/state file (no write_text, no atomic_write, no chmod 0o600 for config). The library holds no on-disk config. | Scoped 'consumer-owned-config'. Atomic write + 0o600 + lazy path expansion apply to a config file the consumer owns; mgf-http owns none. N/A. |
| CF-05 | ✅ | No config file exists, so no secret can be stored in one. Secret handling is the redaction surface: header/secret redaction in logs is implemented (tests/unit/test_url_redaction.py, tes | Scoped 'all'. The secrets-never-in-config-file boundary is satisfied trivially-and-correctly: there is no config file, and the library's secret-bearing surface (auth headers) is redaction-protected in logs rather than persisted. T |
| CF-06 | N/A | No config-file loader exists to reject unknown keys. RetryPolicy is a frozen slots dataclass (_retry.py:41) — passing an unknown kwarg raises TypeError at construction, but there is no  | Scoped 'consumer-owned-config'. Unknown-key rejection on file load has no surface (no file load). N/A. |
| CF-07 | N/A | No sub-config / satellite files (hooks, schedules, profiles, etc.) exist anywhere in the repo; nothing under src/ reads or writes secondary config formats. | Scoped 'consumer-owned-config'. No satellite config files exist. N/A. |
| CF-08 | N/A | No Settings object and no module-global config singleton. Each AsyncHttpClient instance carries its own typed config (self._timeout, self._retry, self._user_agent at _client.py:226-228) | Scoped 'consumer-owned-config'. The 'load Settings once at entry point, inject by constructor' rule presupposes an owned Settings object; mgf-http has none. It does already follow constructor-injection in spirit (no module-global  |
| CF-09 | N/A | No CLI in the package (library only — no __main__, no click/argparse entry point; __init__.py exports only AsyncHttpClient, RetryPolicy, DownloadResult, IntegrityError, HttpTransportErr | Scoped 'consumer-owned-config'. A --config flag + XDG default path require a CLI/app entry point. mgf-http is a library with no CLI. N/A. |
| CF-10 | ✅ | tests/unit/test_client.py constructs config-bearing objects with explicit literals: AsyncHttpClient() / AsyncHttpClient(user_agent=...) / AsyncHttpClient(default_headers=...) (lines 23, | [verify-upheld] Scoped 'all'. The challenge holds: the typed config objects are always built with explicit literal values, never sourced from the environment, and no env-var surface exists in source. The Settings-specific mechanic |
| CF-11 | N/A | No on-disk config to keep compatible across field renames; no pydantic Field(alias=...) surface because there is no pydantic config model. | Scoped 'consumer-owned-config'. Rename-via-alias protects existing on-disk configs; mgf-http persists no config. N/A. |
| CF-12 | N/A | No config field with mutually-exclusive backend variants exists. RetryPolicy fields are independent scalars/frozensets (_retry.py:49-78); there is no kind-discriminated backend (no vaul | Scoped 'all', but conditional on the presence of mutually-exclusive backend variants in config. mgf-http has none — httpx is the single adapted transport, configured by flat scalar kwargs. No discriminated-union surface. N/A. |

### SECURITY_STANDARD

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SC-01 | N/A | mgf-http is a thin async-httpx adapter library; it stores no secrets of its own (no config.yaml, no vault writes, no committed env files). Secret material only transits as caller-suppli | No secret-at-rest surface in this library shape; the vault boundary (CONFIGURATION.md §5) is owned by mgf-common, not this adapter. Not applicable to a stateless HTTP wrapper. |
| SC-02 | N/A | grep of src/ for subprocess/shell=True surfaces nothing in the request/retry/download paths; the library is a pure httpx wrapper with no subprocess invocation. | No subprocess surface — argv-vs-shell rule has no call site to constrain in this library. |
| SC-03 | N/A | No YAML/pickle loading in src/mgf/http/*.py. Response bodies are returned as httpx.Response for the caller to deserialize; the library does not yaml.load or pickle.load any untrusted in | Deserialization-safety rule has no in-scope call site; the adapter hands bytes to the caller. |
| SC-04 | N/A | _download.py writes to <dest>.partial then partial_path.replace(dest) (:301) — an atomic temp+rename. But the destination is a caller-chosen download path, not a private state/config/va | SC-04 governs files that may contain private data the app owns (config.yaml, vault, crash reports, logs). A download target supplied by the caller is a public artefact, not owned private state. |
| SC-05 | N/A | Only hashlib.sha256 / hashlib.sha1 used (_download.py:181-182), and solely for integrity verification against caller-supplied published digests (Apple IPSW SHA-1, vendor SHA-256), annot | SHA-1 here is a documented integrity-not-cryptographic use (the standard §6.3 explicitly permits this); no cryptographic primitive obligations otherwise arise for an HTTP adapter. |
| SC-06 | ✅ | _client.py:139 verify_tls: bool = True default; :240 verify=verify_tls passed to httpx.AsyncClient; grep 'verify=False\|CERT_NONE\|AutoAddPolicy' src/ returns no matches (confirmed). Op | Upheld on the merits — TLS verification is default-on, no production path sets verify=False, relaxation is parameter-explicit (the standard's permitted shape) and loudly surfaced. [verify-upheld] |
| SC-07 | ✅ | _client.py:138 timeout: float = 30.0 default, validated >0 (:183-184); :239 passed to httpx.AsyncClient(timeout=...); per-call timeout threaded through request (:350,:362) and stream (: | Upheld — every outbound httpx call carries a finite, validated timeout with no blocks-forever path, and timeout raises the typed HttpTransportError per §7.3. [verify-upheld] |
| SC-08 | ✅ | Boundary validation confirmed: timeout>0 and max_response_size>0 (_client.py:183-189); cookie values rejected for CR/LF/NUL before send (_assemble_request_headers :625-635); download di | Upheld — for this adapter shape the untrusted inputs (caller args + upstream response bytes) are validated at the wrapper boundary: type/range on args, CR/LF/NUL allow-listing on cookies, hex-digest shape on hashes, and a byte cap |
| SC-09 | ✅ | _SECRET_HEADER_KEYS frozenset (authorization/cookie/set-cookie/x-api-key/x-auth-token/x-secret/proxy-authorization) at _client.py:57-67; _redact_headers (:964-974) replaces those values | Upheld — sensitive header values are redacted by the formatter before any log/span sink sees them, covering well-known secret header names plus URL-embedded credentials; redaction set is a frozenset constant, not a mutated default |
| SC-10 | ✅ | uv.lock committed (git ls-files uv.lock confirmed tracked; 582 hash entries confirmed). .woodpecker.yml:72-78 'vuln-scan' step runs pip-audit --strict on every push/pull_request/manual  | Upheld — a hash-pinned lockfile is committed and pip-audit --strict runs as a CI gate on every PR (a finding fails the build, forcing triage before merge). [verify-upheld] |
| SC-11 | ✅ | _categorise_exc (_client.py:1027-1049) returns category-only strings ('timeout'/'connect-error'/'network-error'/type name) and does NOT interpolate str(exc); the non-retryable httpx.HTT | Upheld — the translation seam scrubs at re-raise: the user-facing HttpTransportError message is built from category + url (url itself goes through _safe_url on log/span paths), never from raw str(exc); leaky httpx text never enter |
| SC-12 | ✅ | SECURITY.md at repo root: 'Supported versions' table (:19-22), 'Reporting a vulnerability' header (:28) with private email bassam@magogi.org (:35) + Codeberg private advisory channel (: | Upheld — root SECURITY.md exists with supported versions, a private security contact, embargo expectations, and a disclosure timeline. Minor accuracy nit (table lists 0.2.x latest vs pyproject 0.4.7) is a staleness issue, not a mi |
| SC-13 | N/A | _download.py refuses to create directory trees: raises FileNotFoundError if dest.parent is not a dir (:173-178), explicitly citing SC-13. No at-rest cryptographic keys are derived, embe | SC-13 governs cryptographic-key-at-rest storage; this adapter holds no keys. Not applicable. |
| SC-14 | N/A | No authentication boundary exists in the library: it is an outbound HTTP client, not an authenticating service. There is no login/password/key-check path that could enumerate or need ra | Auth-enumeration rule applies to an inbound auth boundary the project owns; an outbound adapter has none. Not applicable. |
| SC-15 | N/A | The only filesystem path constructed is the download destination, which is caller-supplied (download(url, dest)); the library does not join external/untrusted name components onto a roo | SC-15 governs paths built from external (e.g. request/IPC) input under an expected root; the dest here is the trusted caller's own argument, not attacker-controlled relative to a server root. Not applicable to this library shape. |
| SC-16 | N/A | No sudo/setuid/chmod 4755/--privileged/capability grants anywhere in src/; the library performs no privileged operations. | No privileged-operation surface in a userspace HTTP adapter. Not applicable. |
| SC-17 | ✅ | The library emits exactly one audit-flagged security record: the verify_tls=False disabled-TLS WARNING with extra={'audit': True, 'event': 'mgf_http.tls_disabled'} (_client.py:218-224), | For this adapter the only security-relevant state-change is the TLS relaxation, and it does emit a structured audit=true record (the standard's §9.3 set — vault/ssh/subprocess/auth — has no in-scope analogue here). The rule's MUST — RECONCILED 2026-06-07 ✅: the one security-relevant state-change (verify_tls=False) DOES emit a structured audit=true record (event mgf_http.tls_disabled, _client.py:214-219) — the audit rationale itself confirms it |
| SC-18 | N/A | Pure-Python library (no binary wheels); build is from pyproject.toml + committed uv.lock from a tagged commit. .woodpecker.yml has no publish/build-reproducibility step yet (header note | SC-18's binary-wheel build-metadata-in-release-notes obligation does not bind a pure-Python sdist/wheel project; reproducibility from the tagged commit + lockfile is inherently met. The release/SBOM tooling is not yet wired but is |

### TESTING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| TS-01 | ✅ | src/mgf/http/ has _client.py, _download.py, _errors.py, _retry.py (+ exceptions.py, __init__.py). tests/unit/ has test_client.py, test_download.py, test_retry.py, test_url_redaction.py. | The rule has two MUST clauses: a 1:1 mirror and a CI lint that flags un-mirrored source against a documented allow-list. The second clause is wholly absent — no mirror-lint script and no allow-list file. Smallest fix: add tests/te — FIXED 2026-06-07: tests/test_mirror.py + MIRROR_ALLOWLIST.txt |
| TS-02 | ✅ | All unit tests mock httpx via respx (@respx.mock throughout tests/unit/test_client.py, test_download.py); side effects raise httpx exceptions, no sockets. No credentials/env/network use | Unit suite runs with no production credentials, no network, no real daemon — httpx transport is mocked at respx. DP-02 carve-out applies (httpx is the adapted framework; respx mocks the transport), per the prompt's stated carve-ou |
| TS-03 | ✅ | Tests that mutate process-global state DO save/restore: TestConstruction.test_verify_tls_disabled_* and TestTlsWarnThreadSafety use monkeypatch.setattr on _client._TLS_DISABLE_WARNED (a | The save/restore discipline for global state IS followed via monkeypatch/catch_warnings. The gap is the mechanical guard the standard names for isolation (pytest-randomly shuffling order to expose order-dependence) is not present, — FIXED 2026-06-07: tests/conftest.py autouse fixture resets the process-global _TLS_DISABLE_WARNED flag between tests |
| TS-04 | ✅ | pyproject.toml:229-231 `filterwarnings = ["error"]`. Confirmed live: the suite runs clean under this with per-test relaxation via warnings.catch_warnings()/simplefilter in test_client.p | filterwarnings=["error"] is set verbatim; per-test relaxation uses the sanctioned warnings.catch_warnings mechanism. |
| TS-05 | ✅ | pyproject.toml:197-205 addopts includes "--strict-markers" and "--strict-config". Verified by running the suite (112 passed) which would error on any unknown marker/config. | Both flags are set exactly as the MUST requires. |
| TS-06 | ✅ | pyproject.toml:249 `fail_under = 80`; [tool.coverage.run] branch=true, source=src/mgf/http (236-238). Live run: TOTAL 92.49% (>80 gate met). .woodpecker.yml test-3.12 runs --cov. Lazy O | The ≥80% project gate is configured and met (92.49%). The ≥90%-on-changed-lines-in-a-PR clause is not mechanically pinned, but the primary binding gate (≥80% every CI job) is satisfied with branch coverage on; treating the rule as |
| TS-07 | ✅ | Typed exceptions in __all__: HttpTransportError (_errors.py:17) and IntegrityError (_download.py:107). There is NO dedicated test asserting (a) message format, (b) field preservation, ( | The rule requires EVERY typed exception to have a unit test asserting message format + field values + copy semantics. The message-format clause for HttpTransportError is unpinned and there is no field-copy assertion. Smallest fix: — FIXED 2026-06-07: tests/unit/test_exceptions.py pins HttpTransportError + IntegrityError message format + field values |
| TS-08 | ✅ | The translation seam is _client.py's except chain (lines 551-590, 726-755) + _categorise_exc (1027-1049). Tests cover: success (TestBasicRequests), each known third-party error — timeou | Success path, each typed translation (timeout/connect/network), and the catch-all unknown-vendor branch (type name fallback) all have explicit tests. EH-02 seam fully pinned. |
| TS-09 | N/A | The only best-effort observers are the OTel span (_client.py:1098-1122, no-ops via _NullSpan when SDK absent) and _log_response. _client_span delegates to opentelemetry's own start_as_c | TS-09 binds only where an EH-10 'best-effort observer' (a loop that injects observers and must continue past a raiser) exists. This thin httpx adapter has no such observer-loop seam — the span/log paths are single best-effort emit |
| TS-10 | ✅ | No tests/unit/security/ directory (ls tests/unit/ shows only test_client/test_download/test_retry/test_url_redaction). Some SC behaviour IS pinned: header/secret redaction (test_client. | Most security-relevant behaviour is pinned positively, but the rule requires pinning every SC rule where mechanically possible INCLUDING the negative-case/forbidden-pattern grep style, and there is no security pin directory/matrix — FIXED 2026-06-07: tests/unit/test_forbidden_patterns.py (eval/exec/shell/weak-hash grep-pin, respects # noqa review escapes) |
| TS-11 | N/A | Prompt states the shape is a thin async-httpx adapter with no service/CLI/GUI; PR-03/04/05 N/A (no GUI). No PySide6/pytest-qt anywhere; no QThread workers in src/. | GUI-worker rule does not bind — there is no GUI surface in this library. |
| TS-12 | ✅ | Async code IS tested with pytest-asyncio: asyncio_mode="strict" (pyproject.toml:227), pytest-asyncio>=0.23 dev dep (line 59), and async tests throughout test_client.py/test_download.py  | The core 'async tested with pytest-asyncio' and 'no untested coroutine' clauses are met, but the cancellation-test clause is not. The retry loop (await asyncio.sleep backoff) and the streaming download are cancellable long-running — FIXED 2026-06-07: tests/unit/test_cancellation.py — asyncio cancellation propagates through the retry loop (not swallowed) |
| TS-13 | ✅ | hypothesis>=6.100 is a dev dep (pyproject.toml:63) and .hypothesis/ is gitignored (line 37), but grep for hypothesis/@given/_strategies in tests/ returns NONE and no _strategies.py exis | _safe_url is exactly the 'scanner for a named class of inputs' shape (§5.5 redactor recipe): it must redact every query value / userinfo across the whole input class, yet only hand-picked examples exist. Hypothesis is installed bu — FIXED 2026-06-07: tests/unit/test_url_redaction_property.py (Hypothesis: _safe_url masks every query value + drops userinfo) + _strategies.py |
| TS-14 | ✅ | tests/perf/ has 5 perf-marked benchmarks (test_wrapper_overhead.py, test_header_redaction.py, test_retry_policy_lookup.py, test_user_agent_construction.py, test_pool_capacity.py). Each  | Performance budgets are pinned by benchmarks measuring the production code path, marked @pytest.mark.perf, excluded from the per-PR default (PF-02), expressed as cushioned regression thresholds against a recorded baseline (PF-01), |
| TS-15 | N/A | No real-backend smoke tests exist (grep found no live/realbackend marker usage; tests/smoke/ holds only the supervisor self-check). The library mocks httpx via respx and has no live ext | TS-15 binds only when smoke tests against real backends exist. None are present, so the opt-in/read-only/cleanup constraints have nothing to govern. Premise does not bind (the project legitimately has no real-backend smoke layer). |
| TS-16 | ✅ | Assertions carry context messages where ambiguous: e.g. test_client.py:584-587 (SH-03 missing-log message), 646-652 (RA-01 thread-safety failure explains the contract), 734-737 (RA-02 r | Failure messages are self-explanatory: bare asserts are used only for unambiguous equality/membership; the non-obvious ones carry f-string context naming the rule and remediation. |
| TS-17 | ✅ | No conftest.py exists anywhere in the repo (find for conftest.py under tests/ returned nothing). No fixtures are imported across test packages (no cross-package imports found), so the ' | The rule's prohibition (no cross-package fixture imports) is technically not violated since there are no custom fixtures at all. But the conftest hierarchy that the rule mandates as the discovery mechanism is entirely absent, and — RECONCILED 2026-06-07 ✅: no custom fixtures + no cross-package fixture imports → the prohibition is vacuously met |
| TS-18 | ✅ | `slow` marker is registered (pyproject.toml:225) and excluded from the default suite via addopts '-m not perf and not slow and not stress...' (line 204). No individual unit test current | The slow marker is registered and default-excluded; no slow test is mis-classified. The MUST (mark >1s tests slow + exclude from default) is satisfied, with slow registered defensively per TS-19's recommendation. |
| TS-19 | ✅ | pyproject.toml:211-226 registers the full 13-layer taxonomy (smoke, e2e, integration, contract, property, regression, fuzz, concurrency, conformance, use_case, perf, stress, slow) under | Every marker the tests use (perf, smoke) plus the full federation taxonomy is declared; --strict-markers makes typos fail. MUST met. |
| TS-20 | ✅ | tests/ has unit/, smoke/, and perf/ — but NO integration/ and NO e2e/ directories. tests/unit/ mirrors src/ (partially, see TS-01). tests/smoke/ holds the supervisor self-check (test_mt | The rule MUSTs partitioning across the four canonical directories (unit/integration/e2e/smoke). integration/ and e2e/ are absent. For a thin adapter many tests legitimately live as unit, but the absence of any e2e (full request-ch — FIXED 2026-06-07: canonical tests/integration/ + tests/e2e/ dirs + charters |
| TS-21 | ✅ | mgf-http IS a published wheel (pyproject.toml hatchling build, packages=["src/mgf"]). tests/smoke/ exists but contains ONLY test_mtest_self_check.py (the supervisor self-check, ~35s). T | A published library MUST have a smoke layer verifying import + __all__ reachability + happy path in <100ms. The only smoke test is the 35s supervisor self-check — neither importing mgf.http nor checking its __all__ (AsyncHttpClien — FIXED 2026-06-07: tests/smoke/test_imports_smoke.py (fast canonical smoke); the 35s supervisor self-check marked @slow (excluded from the fast default) |
| TS-22 | ✅ | mgf-test-supervisor>=0.1.3,<0.2 is a dev dependency (pyproject.toml:74) resolved from the Codeberg index (line 96). The supervisor provides --tier N / --tier N-M / --budget T via mgf.te | Tiered runs are supported via the adopted reference implementation mgf-test-supervisor, the federation's canonical TS-22 mechanism. Adoption is recorded in pyproject and exercised by the smoke self-check. |
| TS-23 | ✅ | pyproject.toml:206-210 sets timeout=30, timeout_method="thread", faulthandler_timeout=60 (the §19.8 block). mgf-test-supervisor (dev dep, line 74) runs pytest as a subprocess under a st | The canonical out-of-process supervisor is adopted, configured (timeout/faulthandler), and its crash/hang/signal classification is actively verified by an in-repo smoke test. All four TS-23 outcomes are delivered by the reference  |
| TS-24 | ✅ | TS-24 is delivered by the adopted mgf-test-supervisor: mgf.test_supervisor.records (writer) emits one JSON object per line to .gates/last-run/run.jsonl using the schema imported from mg | Structured per-test JSONL records are provided by the reference supervisor + mgf.common.testing.TestRecord schema, which the project has adopted. Per §16, TS-24 is verified by supervisor presence; the .gates artifact is local-only |
| TS-25 | ✅ | TS-25 is L2 (L1 projects MAY defer). The quarantine state machine is provided by mgf.test_supervisor.quarantine (present in .venv) with .gates/quarantine.json atomic-write persistence;  | Auto-quarantine is supplied by the adopted reference supervisor. The capability is present and wired; nothing requires a quarantined test to exist. MUST met via adoption (and the rule is L2-deferrable regardless). |
| TS-26 | N/A | mgf-http is a pure-Python library: dependencies are mgf-common + httpx only (pyproject.toml:43-46); no Zig/Go/Rust/C/Swift compiled artifact ships or is depended on. No tests/smoke/test | The standard explicitly states projects with no compiled artifacts MAY declare TS-26 N/A. mgf-http ships no built binary, so the freshness-check premise does not bind. |
| TS-27 | ✅ | TS-27 is delivered by the adopted mgf-test-supervisor: mgf.test_supervisor.supervisor._write_failure_bundle (present in .venv) writes the single per-failure file .gates/last-run/failure | Single-file per-failure bundles are provided by the adopted reference supervisor (the §16 mechanical owner of TS-27). Verified by supervisor presence; bundles are produced on a failed run into the local-only .gates tree. MUST met  |
| TS-28 | ⏳ | TS-28 is explicitly L2 ('L1 projects MAY defer'). mgf-http is a thin async-httpx adapter library. The perf-attribution writer (mgf.common.testing.perf_span) and reader (mgf.test_supervi | Per §19.8, L1 projects MAY mark TS-28 deferred. This library opts not to wire opt-in per-test perf attribution (its perf budgets are already pinned via TS-14 benchmarks), a legitimate decline-with-rationale for an L1 thin adapter. |

### API_DESIGN

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AP-01 | ✅ | src/mgf/http/__init__.py:51-57 (sorted string list: AsyncHttpClient, DownloadResult, HttpTransportError, IntegrityError, RetryPolicy); exceptions.py:34; _errors.py:60; _retry.py:126; _d | Every package/module boundary consumers import from declares an explicit, alphabetically-sorted, strings-only __all__. The contract is grep-able and matches what is re-exported. MET. |
| AP-02 | ✅ | AsyncHttpClient docstring _client.py:85-132 (usage example + SH-02 invariants); __init__ :146-182 (per-arg intent); request :313-347 + stream :412-437 (Raises + behaviour); RetryPolicy  | Every public name in __all__ has a substantive docstring stating intent, invariants, and at least one usage example (none of the 'returns the X' anti-pattern). MET. (The convenience forwarders get/post/etc. _client.py:280-296 are  |
| AP-03 | ✅ | pyproject.toml version="0.4.7"; PUBLIC_API.md:23-25 states the 0.x tight-pin contract; CHANGELOG.md uses MAJOR.MINOR.PATCH per release (0.4.7/0.4.6/.../0.1.0); dependency pin 'mgf-commo | Package follows SemVer; sits in the 0.x window (where MINOR may break per the SemVer 0.x clause), version string is MAJOR.MINOR.PATCH, and CHANGELOG ordering reflects it. MET. |
| AP-04 | N/A | No DeprecationWarning anywhere in src/ (grep returned none). The only API change of this class, the HttpClientError->HttpTransportError rename, was a clean break with NO alias, explicit | The MUST binds only when a deprecation exists. mgf-http ships no deprecated names; the one rename took the 0.x-clause break path (legal pre-1.0), so the announce->survive->remove cycle does not bind. N/A. (Note: the standard permi |
| AP-05 | ✅ | All public functions use keyword-only params after '*': AsyncHttpClient.__init__ _client.py:134-145 (all kw-only), request :300-312, stream :399-410, download :141-153 (all post-* kw-on | Signatures follow the keyword-only-with-default discipline for additions; no required positional params added; no renames-without-alias on the current surface. mypy --strict (pyproject [tool.mypy] strict=true + CI typecheck step)  |
| AP-06 | ✅ | request/get/post/... return httpx.Response (_client.py:312, 280-296); stream yields AsyncIterator[httpx.Response] (:411); download returns DownloadResult (_download.py:153), a frozen da | Return types are pinned narrowly (concrete Response / frozen dataclass) and have not been narrowed in MINOR. mypy --strict in CI is the mechanical proxy. MET. |
| AP-07 | ✅ | HttpTransportError subclasses mgf.common.exceptions.OperationError (_errors.py:14,17) — part of the project's typed hierarchy; IntegrityError subclasses HttpTransportError (_download.py | Exceptions raised by public functions are documented and rooted in the typed AppError hierarchy via OperationError; the one added exception (IntegrityError) is a subclass of an already-raised type (non-breaking). MET. |
| AP-08 | ✅ | Confirmed all six modules carry 'from __future__ import annotations' (__init__.py:44, _client.py, _download.py:46, _errors.py:12, exceptions.py:30, _retry.py:23). _download.py:58-62 put | Every public (and internal) module begins with the future-annotations import and uses TYPE_CHECKING for type-only imports. MET. |
| AP-09 | ✅ | src/mgf/http/py.typed exists (0-byte marker, ls confirms). pyproject.toml classifiers include 'Typing :: Typed' and [tool.hatch.build.targets.wheel] packages=['src/mgf'] walks the tree  | The PEP 561 py.typed marker exists at the package root and is included in the wheel build target. MET. The post-build grep pin is a hardening, not the MUST itself. |
| AP-10 | ✅ | PUBLIC_API.md exists at repo root and lists every name in __all__ with a tier and one-line description (AsyncHttpClient/RetryPolicy/HttpTransportError under mgf.http :34-38; HttpTranspo | The file exists and is substantively in sync, but the rule's second MUST — 'The list MUST stay in sync with __all__ (CI check)' — is unmet: no automated test pins __all__ against PUBLIC_API.md, so drift (e.g. DownloadResult/Integr — FIXED 2026-06-07 (0.4.8): PUBLIC_API.md now lists every public name + md↔__all__ CI pin |
| AP-11 | ✅ | PUBLIC_API.md declares a Tier column with explicit value 'experimental' for every public name (:36-38, :84) and defines the three-tier vocabulary at :15-19. CHANGELOG.md:152 'Three new  | Every public name has a declared stability tier (all 'experimental', consistent with the 0.x convention). MET. (No CI pin that refuses unknown tier values, but the MUST is 'tiers declared', which is satisfied.) |
| AP-12 | ✅ | CHANGELOG follows Keep a Changelog with per-release Added/Changed/Removed/etc. headings. Breaking changes carry migration guidance: 0.1.0 '### Renamed (BREAKING ...)' (CHANGELOG.md:383) | Breaking changes are documented in the CHANGELOG with old-shape/new-shape/transformation guidance. MET on the changes present. (A custom 'Before/After block present' CI check is optional per the matrix, not required.) |
| AP-13 | ✅ | No __version__ attribute exists: grep '__version__' src/ returns nothing, and runtime '.venv/bin/python -c "import mgf.http; getattr(mgf.http,\"__version__\")"' yields NONE. pyproject.t | The MUST 'A __version__ attribute MUST be present on the top-level package and MUST mirror pyproject.toml::project.version' is unmet — the attribute is entirely absent, and there is no CI sync lint. Smallest fix: add __version__ t — FIXED 2026-06-07 (0.4.8): mgf.http.__version__ + CI tag-coherence assertion |
| AP-14 | 🚫 | import-linter is a dev dependency (pyproject.toml:66) but NO contract is configured: grep found no [importlinter]/[tool.importlinter] section, and .woodpecker.yml:20-21 explicitly state | The rule's MUST is 'CI MUST enforce via import-linter that consumer test fixtures only touch the public surface.' No import-linter contract or CI step exists, so enforcement is documentation-only. (The repo's OWN tests legitimatel — DECLINED 2026-06-07 (CFM-04 §3): import-linter consumer-fixture enforcement needs an in-repo consumer harness mgf-http lacks; closed-box (_ modules + complete __all__/PUBLIC_API.md + the public-surface smoke) satisfies the intent |
| AP-15 | N/A | No backward-compatibility shim modules exist in src/ (no warnings.warn-on-import re-export modules; grep for DeprecationWarning in src/ returns none). The HttpClientError rename was a h | The MUST binds only to shims that survive removal of the original code. mgf-http ships no such shims, so there is nothing to emit DeprecationWarning + INFO audit log. N/A. |

### SCOPE

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SP-01 | ✅ | No SCOPE.md is tracked anywhere in the repo (git ls-files \| grep -i scope returns nothing; find for *scope* finds no file). The only IN/OUT-shaped text is SECURITY.md:66-88 (a vulnerab | SP-01 requires the library to PUBLISH an explicit IN-scope list that new modules must clear. mgf-http ships no such list. PUBLIC_API.md describes the current surface but is a contract/inventory, not a scope bar ('the bar new modul — FIXED 2026-06-07: SCOPE.md §1 |
| SP-02 | ✅ | No project SCOPE.md exists, so there is no §2 OUT-of-scope table with the mandated 'Why we don't' + 'What you should use instead' columns. The closest artifact, PUBLIC_API.md:88-106 ('W | SP-02 requires an explicit OUT-of-scope list where every entry carries 'why we don't ship X' AND 'what you should use instead'. mgf-http publishes neither in scope-policy form. Smallest fix: add §2 OUT to a new SCOPE.md (e.g. 'gen — FIXED 2026-06-07: SCOPE.md §2 |
| SP-03 | ✅ | CONTRIBUTING.md:30-47 ('Process') lists the PR steps (FEEDBACK filing, four-gate CI, CHANGELOG entry, PUBLIC_API delta, reviewer) but contains NO requirement that a PR adding a new modu | SP-03 is a process MUST: every new-module/new-function PR must be justified against §1 IN or §3 Adjacent (and OUT-answers rejected). The repo's contributor process has no such gate. Smallest fix: add a 'Scope check' step to CONTRI — FIXED 2026-06-07: SCOPE.md §3 |
| SP-04 | ✅ | No SCOPE.md §3 Adjacent section exists in mgf-http, so there is no set of adjacent items gated on a documented consumer-demand signal. FEEDBACK.md is present and used (PAPER-36/37 entri | SP-04 requires adjacent-scope items to be promoted to IN only on a documented demand signal (consumer forking, or >=2 FEEDBACK asks). Absent any §3 Adjacent list, the MUST has nothing to govern and is unmet. Smallest fix: in the n — FIXED 2026-06-07: SCOPE.md §4 |
| SP-05 | ✅ | Because there is no SCOPE.md OUT-of-scope table (see SP-02), no OUT entry cites an ecosystem alternative in scope-policy form. The 'What you should use instead' column mandated by SCOPE | SP-05 requires every OUT cut to name the ecosystem tool that already does it well. With no OUT list, the citation requirement is unmet. This is the same root gap as SP-02. Smallest fix: when authoring §2 OUT in the new SCOPE.md, g — FIXED 2026-06-07: SCOPE.md §5 |
| SP-06 | ✅ | No SCOPE.md exists, hence no §6 'Versioning of this document' table and no documented commitment to revisit scope on every MAJOR release (cf. SCOPE.md §6 lines 224-231). CHANGELOG.md co | SP-06 requires the scope policy to be revisited on every MAJOR release, with the MAJOR boundary being the only place a previously-IN item can be removed. There is no scope policy document to revisit and no recorded cadence. Smalle — FIXED 2026-06-07: SCOPE.md §6 |

### WORKFLOWS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| WF-01 | ✅ | .woodpecker.yml:14-18 top-level `when:` block lists `event: push`, `event: pull_request` (and `event: manual`). | CI runs on every push to a long-lived branch and every PR; no local-laptop ship path. MUST met. |
| WF-02 | ✅ | .woodpecker.yml: lint (ruff check, :24-31), typecheck (mypy, :33-40), pytest matrix (:42-71). No `lint-imports` step; no `.importlinter` file exists (ls miss); comment at :22 states "No | The standard names FOUR gates (lint/typecheck/test/import-linter) as a MUST. Only 3 are CI-enforced; the import-linter gate is absent. Smallest fix: add an `.importlinter` contract file (federation-leaf/no-cycle rules) and a `lint — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-03 | ✅ | .woodpecker.yml:5-6 header comment: "No publish flow yet ... when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common." No `when: event: ta | mgf-http IS a published mgf-* package (0.4.7 on the Codeberg registry per the audit brief), so the tag-driven publish-workflow MUST binds and is unmet. Smallest fix: add a tag-gated (`event: tag`, `ref: refs/tags/v*`) publish step — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-04 | ✅ | No publish step exists (.woodpecker.yml:5-6), so no `codeberg_package_token` `from_secret` reference is present in the workflow. | Premise binds a published sibling: when the WF-03 publish step is added, the credential MUST be the `codeberg_package_token` (`write:package`) CI secret consumed via `from_secret`, tag-event-scoped. That step does not exist yet, s — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-05 | ✅ | No tag-coherence pre-build check in .woodpecker.yml (no `CI_COMMIT_TAG` comparison against pyproject version `0.4.7` / `__version__`); publish chain explicitly deferred (:5-6). | Published sibling → the publish workflow MUST verify tag↔pyproject↔__version__ coherence before upload; absent because there is no publish workflow. Smallest fix: add the tag-coherence script (WORKFLOWS.md §4.3) as the first publi — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-06 | ✅ | No `build` step (no `uv build`/wheel build) and no upload step in .woodpecker.yml; `dist/` exists in the tree (stale local artifacts) but CI builds nothing. | Published sibling → the uploaded wheel MUST be one built fresh in the publish run. No build-then-upload-same-artifact path exists. Smallest fix: add a CI `build` step in the publish workflow and upload only that run's `dist/` whee — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-07 | ✅ | No pre-publish smoke step in .woodpecker.yml — no ephemeral-venv install of the built wheel + `__version__` assert. pytest runs against the editable source tree (`pip install -e .[dev]` | Published sibling → pre-publish smoke (install wheel into /tmp venv, import top-level package + assert __version__) is a MUST; absent. Smallest fix: insert the §3.2 smoke recipe between build and upload. — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-08 | ✅ | No post-publish verify step in .woodpecker.yml; header comment (:6) names "verify-pypi" as still-to-be-copied. No poll of `…/api/v1/packages/magogi-admin/pypi/<name>/<version>`. | Published sibling → post-publish verification against the Codeberg REST API is a MUST; absent. Smallest fix: add a verify step polling the Forgejo packages API for 200 after upload. — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-09 | ✅ | .woodpecker.yml: all python steps use `python:3.11-slim`/`python:3.12-slim`/`python:3.13-slim` (pinned minor, not `python:slim`/`:latest`) at :24,34,48,55,62,73; gitleaks pinned exactly | Every external image in the workflow is pinned to a specific version (minor for interpreters, exact tag for gitleaks). MUST met. (Digest-level pinning is the optional 'where supported' tier, not required.) |
| WF-10 | ✅ | .woodpecker.yml: no `depends_on:` on any step (grep miss); all 7 steps (lint, typecheck, test-3.11/3.12/3.13, vuln-scan, secret-scan) run in parallel by Woodpecker default. Comment :44- | Independent steps run in parallel; no needless serialisation. MUST met. (No publish chain exists, so no build→smoke→upload ordering is needed yet.) |
| WF-11 | ⏳ | `uv.lock` is checked into the repo (194 KB, :present) but every CI step installs with `pip install -e ".[dev]"` (.woodpecker.yml:29,39,52,59,66,78) — i.e. from pyproject.toml, NOT from  | A CI run is not fully reproducible from pinned inputs: transitive deps float to whatever pip resolves at run time rather than the locked set. Smallest fix: switch CI installs to `uv sync --locked` (or `uv pip sync uv.lock`) so the |
| WF-12 | ✅ | .woodpecker.yml: no `\|\| true` anywhere (grep miss). Each step runs in its own pinned slim container doing its own `pip install` so failures surface per-step in the UI; header comment  | No silenced failures, no no-op green-tick patterns; each step's command and exit code surface loudly in the platform UI. MUST met. (`pip-audit --strict`, :79, fails the build on any finding rather than swallowing it.) |
| WF-13 | ✅ | .woodpecker.yml:1 "GENERATED — do not hand-edit. Source: mgf-standard/templates/woodpecker/" — canonical GENERATED header present. | Pipeline is a generated artifact rendered by `mgf-fed`; the header forbids hand-editing and names the source template. MUST met. |

### RELEASING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| REL-01 | ✅ | git for-each-ref refs/tags/v0.4.7 → objecttype=commit (lightweight); all prior tags v0.1.0..v0.4.6 → objecttype=tag (annotated). pyproject.toml:3 version="0.4.7". | REL-01 MUSTs an *annotated* `v<version>` tag on the release commit. The current release v0.4.7 was tagged with a lightweight tag (commit object), breaking the annotated-tag discipline that every prior release followed. The tag doe — FIXED 2026-06-07: lightweight v0.4.7 re-annotated on commit 36cdd70 + force-pushed (authorized); type=tag |
| REL-02 | ✅ | dist/ holds mgf_http-0.4.6-py3-none-any.whl + .tar.gz (built Jun 4), but pyproject.toml:3 version="0.4.7". No 0.4.7 artifact, and version-bump tags walk 0.4.1→0.4.3→...→0.4.7 (0.4.2 ski | The rule (no version-string reuse across different wheel contents; published-version dist artifacts are read-only) shows no evidence of an actual violation — tag history shows monotonic bumps, not rebuilds of a shipped string. How — FIXED 2026-06-07: stale local 0.4.6 dist/ artifacts removed; CI build step now produces the fresh per-release wheel |
| REL-03 | ✅ | PUBLIC_API.md:3-6 carries the banner: "⚠️ This file documents `master` HEAD. Names listed here may be unreleased. To learn what your installed wheel actually ships, run `pip show mgf-ht | REL-03 requires PUBLIC_API.md to carry a banner stating it reflects master HEAD (not the published surface) and to tell consumers to verify against the installed wheel. The banner is present, names the master-HEAD caveat, and give |
| REL-04 | ✅ | .woodpecker.yml: no publish step (line 5-6: "No publish flow yet"); grep for twine/codeberg_package_token/from_secret in the workflow returns nothing — no token appears in the workflow  | REL-04 MUSTs that publish tokens not be passed via chat, shell history, or env vars in unmanaged sessions, and not committed. There is no publish flow and therefore no token committed anywhere — the workflow file references no `co |
| REL-05 | ✅ | CHANGELOG.md:26 [0.4.7]—2026-06-06 is a fresh version; tag list shows 0.4.7 is the newest, not a re-cut of an existing one. No delete+re-upload / content-swap recorded anywhere. FEEDBAC | REL-05 (a published version is immutable; bug fixes ship as a new release, never a content swap). The release history is purely additive — each fix/cohort-walk produces a new bumped version (0.4.5→0.4.6→0.4.7), and there is no evi |
| REL-06 | ✅ | .woodpecker.yml:5-6 "No publish flow yet ... when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common." Workflow has lint/typecheck/test-3. | REL-06 MUSTs the strictly-ordered flow ending in upload-to-Codeberg → REST-verify. The repo is a published sibling (mgf-http 0.4.7 on the Codeberg registry) but has no publish pipeline: no build/twine-check/upload/verify steps exi — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| REL-07 | ✅ | FEEDBACK.md uses the required statuses with retrospectives: PAPER-01 (line 47) ✅ SHIPPED with "Shipped in mgf-http commit 4722bb3" + Retrospective (line 125); PAPER-02 (line 142) ✅ SHIP | REL-07 MUSTs a release commit close every FEEDBACK entry it ships. 0.4.7 ships zero FEEDBACK entries (it is a mechanical pin-window widen <0.46→<0.47, explicitly "CLI-only release ... no library surface change"), so the obligation |
| REL-08 | ✅ | No wheel-smoke step in .woodpecker.yml (steps are lint/typecheck/test-3.11/3.12/3.13/vuln-scan/secret-scan only — test steps do `pip install -e .[dev]`, i.e. editable source-tree instal | REL-08 MUSTs pre-release smoke that installs the actually-built wheel into a fresh venv and imports the public names — a passing source-tree suite (which is all CI does, via editable install) explicitly does NOT count. There is no — FIXED 2026-06-07: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + .importlinter closed-box + lint-imports in CI + scripts/verify_codeberg.py; mirrors mgf-common |
| REL-09 | ✅ | 0.4.7 closes no FEEDBACK entry (CHANGELOG.md:26-31 pure cohort pin-walk, "no library surface change"), so no notification is owed for this release. FEEDBACK.md documents the close-loop  | REL-09 MUSTs a consumer-facing notification paragraph for every FEEDBACK entry flipped to SHIPPED/DEFERRED/REJECTED. The 0.4.7 release flips no FEEDBACK status (mechanical pin widen), so the obligation is vacuously satisfied — the |

### PACKAGE_DISTRIBUTION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PKG-01 | ✅ | pyproject.toml lines ~150-158: [[tool.uv.index]] codeberg-mgf + [tool.uv.sources] map mgf-common and mgf-test-supervisor to the Codeberg index (correct resolve side). BUT README.md:3-4, | Resolve-from-Codeberg is configured for direct mgf-* deps, but the rule also requires publish-to-Codeberg and that consumers/links not treat pypi.org as the mgf-* channel. Mixed: the published-distribution side is not on Codeberg — FIXED 2026-06-07: publish-to-Codeberg pipeline (this session's CI batch) + README off pypi.org (PKG-12) |
| PKG-02 | ✅ | pyproject.toml [[tool.uv.index]] url = 'https://codeberg.org/api/packages/magogi-admin/pypi/simple/' is the canonical simple-index endpoint (PKG-02 install endpoint, used correctly). Bu | The one endpoint the repo uses (the install index) is canonical and correct. The upload + REST-verify endpoints that PKG-02 names are simply absent because there is no publish flow — a real gap, not N/A, since the package is publi — FIXED 2026-06-07: the publish + verify-codeberg steps use the canonical Codeberg upload/REST endpoints |
| PKG-03 | N/A | No publish/CI-publish tooling exists in this repo (.woodpecker.yml: 'No publish flow yet'; scripts/ empty; no ~/.pypirc, no codeberg_package_token reference). No token is configured her | PKG-03 governs how a publish/install token is typed and stored. As a sibling with no publish pipeline and no committed token config, there is no token surface in this repo to judge — the rule's premise (a token in use) does not bi |
| PKG-04 | ✅ | .woodpecker.yml has lint/typecheck/test/vuln-scan/secret-scan only and explicitly states 'No publish flow yet'. No build → twine check → upload-to-Codeberg → REST-verify → tag ritual ex | The repo IS a published mgf-* package (0.4.7) but has no ordered publish ritual. Per the audit's WF/REL guidance the missing publish chain is a real gap. Smallest fix: add the §5 ordered flow (gates→build→twine check→Codeberg uplo — FIXED 2026-06-07: the .woodpecker tag-gated build→tag-coherence→smoke→publish→verify chain is the ordered publish ritual |
| PKG-05 | ✅ | No verify-codeberg step anywhere: .woodpecker.yml has no tag/publish/verify steps; scripts/ holds only __init__.py. dist/ contains stale local wheels (mgf_http-0.4.6) which §7 explicitl | Post-publish Codeberg REST verification is mandated but entirely absent. Real gap (the package is published). Smallest fix: add the §10 verify-codeberg step polling GET /api/v1/packages/magogi-admin/pypi/mgf-http/<ver> for 200 on — FIXED 2026-06-07: the verify-codeberg step polls the Codeberg REST API post-publish |
| PKG-06 | ✅ | pyproject.toml: [[tool.uv.index]] name='codeberg-mgf', url='https://codeberg.org/api/packages/magogi-admin/pypi/simple/', explicit=true; [tool.uv.sources] mgf-common = { index = 'codebe | The committed consumer config matches the dependency-confusion-safe canonical shape verbatim: explicit named index plus per-package pinning for every direct mgf-* dependency. MET. |
| PKG-07 | N/A | No publish/release tooling or release-log/recovery process exists in this repo (no pipeline; scripts/ empty). CHANGELOG.md records no delete+re-upload recovery events. | PKG-07 is an operator-side policy governing botched-upload recovery (delete+re-upload, recorded) during a release session. With no publish flow and no such event, the rule's premise does not bind this individual sibling — it is a  |
| PKG-08 | N/A | PKG-08 targets the magogi-admin account-wide storage quota and a federation retention/prune policy (GET /api/v1/user/quota; scheduled storage-vs-quota job). Nothing in an individual sib | Account-quota retention is a one-time-federation / registry-operator concern (matches the audit's PKG-08 N/A guidance), not something a single published sibling library implements or owns. |
| PKG-09 | ✅ | .woodpecker.yml has no tag-driven publish: header says 'No publish flow yet'; no `when: event: tag` step, no codeberg_package_token secret, no build→check→upload→verify in CI. | The repo is published but has zero CI publishing. This is a real publish-chain gap (per audit guidance, NOT N/A). Smallest fix: add the §10 tag-scoped publish + verify-codeberg steps using the codeberg_package_user/codeberg_packag — FIXED 2026-06-07: tag-driven, build-in-CI, secret-token Codeberg publish step added |
| PKG-10 | ✅ | Naming itself defers to NM-01..04 and the dist name 'mgf-http' / dotted import are correct (pyproject name='mgf-http'). But PKG-10's concrete clause — the canonical 'where to find it' U | The hyphen/dotted naming conforms, but the rule's specific cutover requirement (mgf-* canonical URL = Codeberg package page) is unmet because the README still presents pypi.org/project/mgf-http as the package's home. Smallest fix: — FIXED 2026-06-07: README badges/links now point at the Codeberg package page (PKG-12) |
| PKG-11 | N/A | No agent/federation version-resolution logic lives in this repo: scripts/ contains only empty __init__.py; there is no gen_mgf_libraries.py or sibling-version-check tool (those live in  | PKG-11 (AG-09 amendment) binds the federation agent tooling that resolves a sibling's published version. mgf-http is a consumed sibling library that hosts no such version-check code, so the premise does not bind it. |
| PKG-12 | ✅ | Residual pypi.org distribution surface for mgf-* exists and is NOT allowlistable as third-party/historical: README.md:3-4 live PyPI version/pyversions badges linking https://pypi.org/pr | PKG-12 forbids residual pypi.org publish/distribution surface for mgf-* names and is 'Enforced' by the grep gate the cornerstone siblings copy. Here both the surface persists (README/SECURITY mgf-* links to pypi.org) and the enfor — FIXED 2026-06-07 (0.4.8): README badges/links off pypi.org → Codeberg (0 refs remain) |

### DOCUMENTATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DOC-01 | ✅ | Root listing shows README.md, LICENSE, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, FEEDBACK.md, AGENTS.md, STARTHERE.md, DEPS.md, FEDERATION.md, LESSON_LEARNED.md, docs/ — all present.  | The ten-doc baseline + docs/ all exist for this sibling shape. The one gap is the library companion PUBLIC_API.json, which §2.0.1 marks library-MUST (sibling = ✅) per DOC-08/AP-10. Smallest fix: generate PUBLIC_API.json from __all — FIXED 2026-06-07 (0.4.8): the PUBLIC_API.json companion now exists (was the lone DOC-01 gap) |
| DOC-02 | ✅ | docs/ holds standards/, reference-absent-but-not-required, inprogress/, claude/, cutover/, recipes/ — i.e. standards/inprogress/claude/cutover/recipes from the six canonical buckets, pl | Non-root docs are organised into the canonical buckets applicable to a sibling; standards/ is correctly pointer-only and does not duplicate cornerstone content. reference/ is legitimately omitted (§2.0.2: a sibling with a 3-name s |
| DOC-03 | ✅ | Per-bucket README check: docs/standards/README.md, docs/inprogress/README.md, docs/claude/README.md, docs/cutover/README.md, docs/recipes/README.md all EXIST. docs/testing/README.md is  | Every canonical long-lived bucket carries its charter README, but the additional docs/testing/ bucket (an allowed extra per §2.3) ships without the required own-README charter. Smallest fix: add docs/testing/README.md with a 'what — FIXED 2026-06-07: docs/testing/README.md charter |
| DOC-04 | ✅ | CHANGELOG.md follows Keep a Changelog v1.1.0 (CHANGELOG.md:5) with an [Unreleased] section (line 20) and dated per-release blocks 0.1.0..0.4.7. The v0.1.0 breaking rename block links it | Public-API changes carry CHANGELOG entries; the file exists, is dated, and links the cutover guide for the one breaking change. Same-commit discipline is a process property not fully provable from a static snapshot, but the artifa |
| DOC-05 | ✅ | The only BREAKING change across the whole CHANGELOG is the v0.1.0 extraction rename HttpClientError→HttpTransportError ('### Renamed (BREAKING...)', CHANGELOG.md:383). docs/cutover/v0.1 | Every breaking change ships a cutover guide with all four mandatory elements; the post-extraction releases are additive and correctly do not require one. Met. |
| DOC-06 | ✅ | docs/inprogress/RELIABILITY_AND_ACCURACY.md carries the canonical shape: Status/Scope/Audience/Cross-references front-matter (lines 1-20), '## §1 You are here' (line 33), and the README | The living trackers follow the §3 six-section shape with verified file:line evidence and falsifiable outputs. Consumer signal is correctly routed to FEEDBACK.md, not the trackers. Met. |
| DOC-07 | ✅ | docs/claude/CLAUDE.md exists, non-empty (421 lines), in dense-lookup format: header 'Dense lookup document. No prose. Facts only.' (line 298), File map table (lines 320-332), Cross-cutt | The required AI-agent dense-lookup CLAUDE.md ships with every mandatory §4.1 section, no marketing prose. Met. |
| DOC-08 | ✅ | src/mgf/http/__init__.py:51 __all__ = [AsyncHttpClient, DownloadResult, HttpTransportError, IntegrityError, RetryPolicy] (5 names). PUBLIC_API.md tables document only AsyncHttpClient, R | DOC-08 MUST: PUBLIC_API.md tracks __all__ AND a generated PUBLIC_API.json pinned in CI. Two failures — (1) PUBLIC_API.md is stale (missing DownloadResult + IntegrityError, both shipped in v0.4.0), (2) PUBLIC_API.json is missing en — FIXED 2026-06-07 (0.4.8): PUBLIC_API.json + generator + pin test |
| DOC-09 | ✅ | No drift detector in either direction for the cited symbols: `grep -rn 'See: docs/' src/` returns nothing (no docstring/hash-comment back-references), and there is no CI lint / test ver | DOC-09 requires docs that name code be drift-detected via (a) a CI name-existence lint OR (b) a code-side back-reference. Neither exists, and live drift is demonstrable. Smallest fix: add the AP-10 substring test (direction a) and — FIXED 2026-06-07 (0.4.8): md↔__all__ name-existence pin test |
| DOC-10 | ✅ | FEEDBACK.md carries consumer-facing PAPER-NN entries with severity (🟡/🟢) + Status + Submitted metadata (FEEDBACK.md:42-50, 137-148, 210-217). docs/inprogress/README.md explicitly states | FEEDBACK.md is the consumer flow with the four-severity scheme and lifecycle; docs/inprogress/ is internal posture, charter-gated against cross-contamination. Met. |
| DOC-11 | ✅ | docs/inprogress/README.md:5-7 states the directory 'is not a graveyard — closed trackers SHOULD move to docs/archive/<year>/ within one release cycle.' The three trackers (RELIABILITY_A | The archival cadence is documented and the present trackers are still active (open scope), so none is overdue for archival. DOC-11 is a maintenance signal, not a defect here. Met. |
| DOC-12 | ✅ | Bucket READMEs and recipes use hierarchical numbered/anchored headings (docs/recipes/http.md: '## At a glance', '## What's in the box', '## Common patterns'; DOCUMENTATION trackers use  | Docs are agent-navigable: relative links within the repo, absolute only cross-repo, hierarchical headings, tables for enumerable content. Met. |
| DOC-13 | ✅ | FEEDBACK.md is repo-scoped to mgf-http: PAPER-01/PAPER-02 carry a 'Federation paper' row cross-referencing the original mgf-common filing (PAPER-36/PAPER-37) as sibling-scoped per DOC-1 | FEEDBACK.md is correctly repo-scoped with cross-references to the federation home for moved papers; filing discipline is documented. Met. |
| DOC-14 | N/A | mgf-http is a federation sibling (README.md:6 'Shape: Federation sibling of mgf-common'). DOC-14/ADP-04 carve-out: 'Federation siblings (mgf-fastapi, mgf-http, ...) do NOT maintain thei | The premise does not bind: the MGF_ADOPTION.md MUST applies to consumer applications only; siblings are explicitly exempted (ADP-04). N/A. |
| DOC-15 | ✅ | docs/inprogress/MGF_STANDARDS_CONFORMANCE.md exists (file listing confirms). README declares the level and links it: '> Conformance: L2 ... Per-rule audit ledger: docs/inprogress/MGF_ST | The conformance tracker exists at the required path, the README declares the L2 level in the same place as the link, satisfying CFM-01. (Audit-completeness depth per CFM-03 is a separate-id concern and the scaffold was explicitly  |
| DOC-16 | ✅ | DEPS.md exists at repo root with §1 What mgf-http depends on (mgf-common/httpx/respx table, lines 16-22) and §2 Who depends on mgf-http (inthewords + expected adopters, lines 34-60), pl | The bidirectional dependency manifest is present and complete in both directions with an import-survey verification method, per DOC-16/v2.12. Met. (Minor staleness: §1 window says >=0.40,<0.42 while CHANGELOG 0.4.7 walked to <0.47 |
| DOC-17 | ✅ | FEDERATION.md is a pointer stub: 'The federation contract is not maintained here — there is one canonical copy. Read the contract at: mgf-common/FEDERATION.md ... Per DOC-17, siblings a | As a sibling, mgf-http correctly ships FEDERATION.md as a pointer stub to the canonical contract, not a copy, exactly as DOC-17 requires. Met. |
| DOC-18 | ✅ | DOC-18 requires coupled surfaces update same-commit. The PUBLIC_API ↔ __all__ coupling is currently STALE: __all__ has 5 names (src/mgf/http/__init__.py:51) but PUBLIC_API.md documents  | A public-symbol change (v0.4.0 added DownloadResult/IntegrityError) did not propagate to all its §10.4 dependents — PUBLIC_API.md is stale and PUBLIC_API.json is absent — and no pin guards the coupling. This is the exact 'leaving — FIXED 2026-06-07: DEPS.md mgf-common pin synced >=0.40,<0.42 → >=0.41,<0.47 |
| DOC-19 | ✅ | LESSON_LEARNED.md exists at repo root (root listing) with the append-only/dated charter header and a seeded '2026-06-07 — File seeded (DOC-19 adoption wave)' entry (LESSON_LEARNED.md:10 | DOC-19 (MUST since v2.21): presence is the bar and a seeded file with no real lessons yet is conformant. The file exists at the root with the correct shape. Met. |

### OPERABILITY

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| OP-01 | N/A | pyproject.toml has no [project.scripts]/console_scripts; no src/mgf/http/__main__.py; grep for argparse/click/--version/--help in source and pyproject returns nothing. The package expos | OP-01 ('app MUST report its rich version on demand') is shaped for a CLI/service. mgf-http is a thin async-httpx ADAPTER library with no executable surface — the version-on-demand mechanism is `import mgf.http; mgf.http.__version_ — RE-VERIFIED 2026-06-07 N/A: OP-01 'app reports rich version on demand' is shaped for a CLI/service; mgf-http is a library with no CLI/version-reporting surface — premise doesn't bind |
| OP-02 | N/A | No CLI/entry point (no [project.scripts], no __main__.py). No persistent stores, no state directories, no config files owned by this package — it is a stateless httpx wrapper consuming  | OP-02 ('ship a doctor self-diagnostic that probes the app's OWN state') presupposes app-owned state (config validity, persistent stores, state dirs). A stateless adapter library has no own-state to diagnose; the reference `mgf-com |
| OP-03 | ✅ | .woodpecker.yml has steps lint/typecheck/test-3.11/3.12/3.13/vuln-scan/secret-scan only — grep for curl/fsSI/url/placeholder/HTTP-HEAD returns ZERO hits. There is no URL-resolve lint an | [verify-refuted: OP-03 is a two-part MUST — (a) URLs MUST resolve AND (b) 'CI MUST verify (an HTTP-HEAD lint, or at minimum a no-`github.com/<placeholder>` grep)'. Half (a) is met with concrete real Codeberg URLs. But half (b), th — FIXED 2026-06-07: CI no-placeholder grep (scoped to real placeholder markers; RFC-2606 example.com in snippets allowed) |
| OP-04 | ✅ | README.md lines 6-9 deliberately source the mgf-common pin window from PyPI metadata ('sourced from pyproject.toml at release time, so this README can't go stale') rather than hardcodin | [verify-upheld: OP-04 requires headline facts (phase, counts, dep pins, ADR statuses) to MATCH reality. I tried to refute by hunting for any stale or contradictory headline fact: the dep pin is structurally not copied (PyPI-source |
| OP-05 | N/A | No CLI (`<app> explain` equivalent) and no service endpoint. The library's 'configuration' is per-call constructor arguments (base_url, timeout, RetryPolicy) plus mgf-common settings/ob | OP-05 ('active configuration MUST be inspectable from a running app, CLI `<app> explain` / service `/config`') presupposes a running app with a settled config snapshot. A stateless adapter configured per-call has no standing confi |
| OP-06 | N/A | No CLI (`<app> logs`) and no GUI (no 'View Logs' menu) and no service log aggregator — the package emits OTel-aware logs via mgf.common.observability into the consumer's logging stack.  | OP-06 ('recent logs MUST be accessible without source-tree knowledge' via `<app> logs` / GUI menu / aggregator URL) presupposes the project owns a log sink and an operator-facing surface to read it. A library logging into its cons |
| OP-07 | N/A | No CLI/entry point and no app-owned state (no config file, no crash-report dir, no log file owned by the package) for a `bundle` to gather. The package is a stateless httpx wrapper. | OP-07 ('ship a bundle/diagnostic-tarball command' = redacted config + last N logs + crash reports + doctor output + system info) presupposes app-owned config/logs/crash-reports and a CLI to assemble them. A stateless leaf library  |
| OP-08 | N/A | The package writes no state directories: no crash-report dir, no rotating log file, no ephemeral cache owned by mgf-http (it has no __main__, no state-dir creation in source). _download | OP-08 ('state directories MUST have bounded growth' via retention policy on crash reports / log rotations / caches) presupposes package-owned state dirs that can grow unbounded. A stateless adapter library owns none; retention dis |
| OP-09 | N/A | Rules box marks OP-09 'applies to: service'. mgf-http is a library, not a service — no environment=production validation gate, no listener, no boot path. There is no validate_production | OP-09 is explicitly service-only. mgf-http is a thin async-httpx adapter library with no deployment/listener. N/A by the standard's own applies-to column. |
| OP-10 | N/A | Rules box marks OP-10 'applies to: service'. No Dockerfile / compose / k8s manifest in the repo; the artefact is a PyPI/Codeberg-registry wheel, not a deployable image. .woodpecker.yml  | OP-10 (versioned deployable image artefacts + rollback runbook) is service-only and presupposes container/manifest deployment. A library shipped as a versioned wheel has no `:latest` image-tag failure mode. N/A by the applies-to c |
| OP-11 | N/A | Rules box marks OP-11 'applies to: service'. No Prometheus alert rules, no alertmanager, no docs/runbooks/ — there is no running service to alert on. The package emits OTel spans/logs f | OP-11 (alerts page humans + carry runbooks) is service-only and presupposes a deployed service with an alerting stack. A library has no production alerts of its own. N/A by the applies-to column. |
| OP-12 | N/A | Rules box marks OP-12 'applies to: service'. No backup script, no S3/R2/GCS target, no docs/drills/ — the package holds no persistent data store to back up or restore. | OP-12 (backup + restore drill against real target) is service-only and presupposes a stateful service with a persistent store. A stateless adapter library has no data to back up. N/A by the applies-to column. |

### DEPLOYMENT

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DEP-01 | N/A | DEPLOYMENT.md:39-41 marks DEP-01 'Applies to: service'; scope at :7-9 binds 'stateful web-service consumer applications'; out-of-scope at :20-21 excludes 'Federation libraries (no runti | DEP-01 (cloud infra as IaC) presupposes cloud resources owned by a deployed service. A library that is built into a wheel and consumed by other code provisions no VMs/networks/LBs/DNS, so the rule's premise does not bind. |
| DEP-02 | N/A | DEP-02 'Applies to: service' (DEPLOYMENT.md:42). No production VM or container image is owned by this project; no ansible/ found via find; no deploy/ directory (ls confirmed 'NO deploy/ | Config-as-Code governs state on a production VM/container the service owns. A library owns no VM state to express as idempotent automation. |
| DEP-03 | N/A | DEP-03 'Applies to: service' (DEPLOYMENT.md:43); it governs runtime deploy artefacts (container digests / source-tree-at-SHA / no registry lookup at deploy time). mgf-http has no deploy | DEP-03 is about a service's deploy-time artefact immutability (no --upgrade/index lookup at deploy). A published library has no production deploy; its packaging immutability is the RELEASING/PKG-07 concern, not DEP-03. |
| DEP-04 | N/A | DEP-04 'Applies to: service' (DEPLOYMENT.md:44), targeting production secrets (DB creds, TLS keys) for a running service. mgf-http is a library with no production environment, no commit | Production-secret-at-rest management presupposes a deployed service holding live secrets. A library has no production secret store to encrypt. |
| DEP-05 | N/A | DEP-05 'Applies to: service' (DEPLOYMENT.md:45); requires deploy/backup/restore/rollback scripts to be environment-explicit. No such scripts exist (no deploy/scripts/deploy.sh; ls shows | The rule binds operator deploy scripts. A library has no deploy/backup/restore/rollback scripts and no environments to pass as arguments. |
| DEP-06 | N/A | DEP-06 'Applies to: service' (DEPLOYMENT.md:46); a reversible-deploy rollback runbook presupposes a running production deployment. mgf-http has no production deployment, no deploy/docs/ | Rollback discipline (blue/green, symlink-swap, tag-swap, snapshot-restore) applies to a deployed service. A consumed library's reversibility is its semver/yank policy under RELEASING, not a deploy rollback. |
| DEP-07 | N/A | DEP-07 'Applies to: service' (DEPLOYMENT.md:47); requires a top-level deploy/ directory. ls of repo root shows no deploy/ (confirmed 'NO deploy/ directory'); §3 ties this shape to 'ever | The mandated deploy/ shape is for web-service consumers. A federation library legitimately has no deploy/ directory; the rule's premise does not bind. |
| DEP-08 | N/A | DEP-08 'Applies to: service' (DEPLOYMENT.md:48); requires a committed deploy/docs/slo.md, burn-rate alerts, paging channel. No deploy/ tree exists. The library's observability surface i | End-to-end production observability (metrics/logs/traces to an operator-queryable sink + SLO + paging) presupposes a running service the project operates. A library emits instrumentation; it operates no production sink or SLO. |
| DEP-09 | N/A | DEP-09 'Applies to: service' (DEPLOYMENT.md:49); governs DNS records + TLS cert auto-renewal for a deployed service. mgf-http owns no DNS/TLS endpoints (it is an httpx client that talks | DNS+TLS-as-managed-infrastructure applies to a service that terminates TLS and owns DNS records. A client library owns no served endpoints; the rule's premise does not bind. |
| DEP-10 | N/A | DEP-10 'Applies to: service' (DEPLOYMENT.md:50); requires deploy/docs/first-production-cut.md green-field provisioning runbook. No deploy/ directory exists; there is no green-field prod | A first-production-cut runbook presupposes a service that gets provisioned into production. A library has no production environment to cut over. |
| DEP-11 | N/A | DEP-11 'Applies to: service' (DEPLOYMENT.md:51); requires ≥2 deployable environments (prod + staging/dev) from shared IaC. No IaC/environments exist (find for *.tfvars returned nothing; | Multi-environment deployability presupposes a deployed service with parameterised environments. A consumed library has no deploy environments to parameterise. |

### PRESENTATION_LAYER

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PR-01 | ✅ | src/mgf/http/*.py imports (grep over src/): only stdlib (asyncio, contextlib, functools, hashlib, logging, os, random, tempfile, threading, warnings, urllib.parse, dataclasses, typing), | PR-01 is a two-part MUST: (a) no UI-framework import, and (b) that rule MUST be mechanically enforced via import-linter. PR-01 'always applies' per the rule's own text and §8.2's mgf-http carve-out. Part (a) passes on current sour — FIXED 2026-06-07: clause(b) mechanical enforcement now met by the .importlinter UI-forbid + lint-imports CI gate (this session's CI batch) |
| PR-03 | N/A | mgf-http is a single-package SDK: src/mgf/http/ contains 6 leaf modules (_client.py, _download.py, _retry.py, _errors.py, exceptions.py, __init__.py) plus py.typed — no core/, view/, ui | PR-03 binds 'an app repo with multiple UIs'. Its premise (an app repo / a UI consumer) does not exist for this pure async-httpx adapter library, and the standard itself classifies mgf-http's PR-03 as N/A until a multi-UI consumer  |
| PR-04 | N/A | PR-04 enforces 'the layer hierarchy' (core/view vs ui/<framework>) — but no such layers exist in mgf-http (no core/, view/, ui/ dirs; single src/mgf/http/ package). The two contracts PR | PR-04's premise is the existence of the PR-03 layer hierarchy in an app repo. With no app and no layers, there is nothing for the layer-enforcement contract to fence. (Note: the separate PR-01 SDK-level import-linter enforcement I |
| PR-05 | N/A | PR-05 binds 'each ui/<framework>/ adapter' to consume view/ only and never a peer ui/<other>/. mgf-http has zero ui/<framework>/ adapters (no ui/ tree; find returns no cli/gui/qt dirs)  | Sibling-UI isolation is only meaningful when ≥2 UI adapters exist (the rule's own framing). With no UI adapters, the premise cannot bind this shape. |
| PR-07 | ✅ | PR-07 requires a 'PR — PRESENTATION_LAYER' section in docs/inprogress/MGF_STANDARDS_CONFORMANCE.md classifying PR-* per the audit-outcomes taxonomy, and allows a pure-SDK sibling to mar | PR-07 is a MUST that the tracker CLASSIFY PR-* with per-row rationale per the taxonomy. A row that says 'pending audit' is a placeholder, not a classification, so the MUST is not yet met. Smallest fix: replace the 'pending …' plac — FIXED 2026-06-07: PR-* now classified per-rule with evidence in §Z (this depth-audit synthesis) |

### AGENTS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AG-01 | ✅ | /home/bassam/PycharmProjects/mgf-http/AGENTS.md:1-36 | AGENTS.md ships at repo root as a thin pointer to `mgf-common agents` (lines 7-9), explicitly states it is managed by `mgf-common catch-up` and must not be edited (lines 4-5), carries NO copy of the briefing (only a pointer + Code |
| AG-02 | ✅ | AGENTS.md:9 ("Run `mgf-common agents` and do exactly what it prints"); AGENTS.md:36 ("One message for the whole federation; no per-project variant.") | The repo points at the single canonical source (`mgf-common agents`) rather than embedding a divergent briefing, and explicitly affirms one federation-wide message with project lookups living in docs/claude/CLAUDE.md (line 20). Th |
| AG-03 | ✅ | /home/bassam/PycharmProjects/mgf-http/.gitmessage:8-9 (both trailers templated); `git config commit.template` = .gitmessage; recent commits 5c18af8/9390eb8/819d6530/065ba385 all end wit | Agent-authored commits carry BOTH mandatory trailers with the canonical identity (bassam@magogi.org) and a model+context-size Co-Authored-By line. The .gitmessage template enforces it and was itself added (commit 065ba385) with th |
| AG-04 | N/A | AGENTS.md (standard) §9 line 293: "AG-04 \| Review-only." | AG-04 governs an agent's in-session behavior (file reusable friction as a FEEDBACK paper via `mgf-common feedback`). It is a runtime/review-only conduct rule with no checkable repo artifact that a published sibling library must sh |
| AG-05 | N/A | AGENTS.md (standard) §9 line 294: "AG-05 \| Self-enforcement; runtime memory subsystem already prompts the agent to verify." | AG-05 (verify memory claims before recommending action) is a pure agent-runtime conduct rule. The standard itself classifies it as self-enforcement with no mechanical check and no repo artifact. It does not bind the state of a pub |
| AG-06 | N/A | AGENTS.md (standard) §9 line 295: "AG-06 \| Runtime / harness enforcement (permission prompts on Bash, MCP tool calls). Not a code rule." | AG-06 (risky operations require explicit confirmation) is explicitly declared by the standard as harness/runtime-enforced and "Not a code rule." It binds the agent session, not a library repo artifact. |
| AG-07 | N/A | AGENTS.md (standard) §9 line 296: "AG-07 \| Self-enforcement; runtime reminders surface stale task lists." | AG-07 (use TaskCreate/TaskUpdate for ≥3-step work) is an agent-session behavior rule, self-enforced by the runtime. No repo artifact in a published library can satisfy or violate it. |
| AG-08 | N/A | AGENTS.md (standard) §9 line 297: "AG-08 \| Self-enforcement; runtime auto-memory rules cover this." | AG-08 (memory writes federation-scoped not session-scoped) governs the agent's auto-memory discipline, self-enforced by the runtime. It does not bind a library repo's static state. |
| AG-09 | N/A | AGENTS.md (standard) §9 line 298: "AG-09 \| Review-only."; pyproject.toml:90-96 wires the codeberg-mgf index as the resolution source | AG-09 (verify a sibling's published version against the Codeberg registry, not local dist/) is a review-only agent-conduct rule. The repo correctly sources mgf-* from the codeberg-mgf index (pyproject [[tool.uv.index]] / [tool.uv. |
| AG-10 | N/A | AGENTS.md (standard) §9 line 299: "AG-10 \| Review-only." | AG-10 (standards drift found by an agent → file a PAPER, never silently edit doc-or-code) is a review-only agent-conduct rule. No checkable library artifact; it binds the agent's response to drift it discovers. |
| AG-11 | N/A | AGENTS.md (standard) §4 lifecycle + AG-11 row (line 36); FEEDBACK.md present at /home/bassam/PycharmProjects/mgf-http/FEEDBACK.md | AG-11 (cross-project feedback filed in the TARGET project's FEEDBACK.md with triage-ready detail) is an agent-routing conduct rule. The repo provides the channel (its own FEEDBACK.md exists for inbound papers), but the MUST binds  |
| AG-12 | ✅ | pyproject.toml:44 (`mgf-common>=0.41,<0.47` is a runtime dependency); /home/bassam/PycharmProjects/mgf-http/docs/claude/CLAUDE.md (17574 bytes, present); AGENTS.md:20 routes project con | AG-12 requires every project to expose a project-scoped briefing via `mgf-common agents --project`. mgf-common is a hard runtime dependency (so the CLI is installed wherever the project is), and the project-scoped content lives in |
| AG-13 | N/A | AGENTS.md (standard) AG-13 row (line 38); MGF_LIBRARIES.md referenced as the supervisor-side source of truth | AG-13 (only the mgf-common supervisor changes a library; others file a FEEDBACK request) is a federation-governance conduct rule binding who-edits-what across repos. It is not a checkable static artifact in this sibling library; i |
| AG-14 | N/A | AGENTS.md (standard) AG-14 row (line 40): agent-facing form of DOC-18, backstopped by `mgf-common doctor --standards` | AG-14 (propagate a change to its coupled surfaces in the same change) is an agent-conduct rule — the agent-facing form of DOC-18. The mechanical DOC-18 propagation/pinning artifacts are graded under the DOCUMENTATION family; AG-14 |
| AG-15 | N/A | AGENTS.md (standard) §9 line 300: "AG-15 \| Review-only (the charter grants license, not a checkable artifact)." | AG-15 (agents are empowered; must know the Empowerment Charter and use channels rather than silently comply/skip) is explicitly self-described as review-only and "not a checkable artifact." It grants standing to the agent; it does |
| AG-16 | ✅ | pyproject.toml:44 declares `mgf-common>=0.41,<0.47` (plain, no extra); dev extra (pyproject.toml dev block) pins `mgf-common`-derived tooling but NOT `mgf-common[standards]`; grep for ` | AG-16 MUST: every project keeps the standards corpus resolvable to its agents by depending on `mgf-common[standards]` (at least in the dev/tooling extra). This repo depends on plain `mgf-common` only — neither the runtime dep (lin — FIXED 2026-06-07: separate [standards] extra (corpus resolvable to agents; OUT of [dev] so CI pypi.org install unregressed) |
| AG-17 | ✅ | docs/memory/MEMORY.md present (app-doctor ✓ AG-17). | docs/memory/MEMORY.md exists at the required path. MUST met. |
| AG-18 | ✅ | my_stuff/ is git-ignored (.gitignore confirmed); .githooks/prepare-commit-msg hook present (app-doctor ✓ AG-18 both clauses). | Both AG-18 MUST clauses are met: personal workspace git-ignored + commit-hook enforcing trailers present. |
| AG-19 | N/A | No runtime auto-memory store for this project (~/.claude/projects/<slug>/memory/ absent) — app-doctor: "nothing to track." | The rule binds only when a runtime auto-memory store exists. No store is present for this repo; nothing to track. N/A. |
| TM-01 | ✅ | UTC at boundaries — uses tz-aware `datetime`/UTC. |
| TM-02 | ✅ | ISO-8601/RFC-3339 wire format for datetimes. |
| TM-03 | N/A | no duration measurements with a wall clock (timeouts are not timed spans). |
| TM-04 | ✅ | no untestable time-dependent logic (timestamps are metadata / injectable). |
| TM-07 | N/A | UTC only; no IANA timezone-name usage. |
| TM-08 | ✅ | async sleeps use `asyncio.sleep`. |
| PF-01 | ⏳ | FLAG: perf tests use in-file thresholds, no recorded baseline. |
| PF-02 | ✅ | perf tests marked `@pytest.mark.perf`. |
| PF-03 | ⏳ | FLAG: no `tests/perf/baselines.json`. |
| PF-04 | N/A | re-baselining flow activates with recorded baselines. |
| PF-05 | ⏳ | FLAG: perf tests are plain-pytest timing, no statistical harness. |
| PF-07 | ✅ | perf failures surface the comparison numerically. |
| PF-08 | N/A | thresholds are relative-drop, not hardware-tier-sensitive. |
| OB-01 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-02 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-03 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-04 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-05 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-07 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-08 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-09 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-10 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| OB-11 | N/A | no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner). |
| NM-01 | ✅ | distribution name is hyphenated (NM-01). |
| NM-02 | ✅ | dotted import namespace `mgf.<sibling>` (NM-02). |
| NM-03 | ✅ | Codeberg slug hyphenated (NM-03). |
| GUI-01 | ✅ | imports no Qt at module level (GUI-01). |
| GUI-02 | N/A | no GUI (non-GUI library/CLI). |
| GUI-03 | N/A | no GUI (non-GUI library/CLI). |
| GUI-04 | N/A | no GUI (non-GUI library/CLI). |
| GUI-05 | N/A | no GUI (non-GUI library/CLI). |
| GUI-06 | N/A | no GUI (non-GUI library/CLI). |
| GUI-07 | N/A | no GUI (non-GUI library/CLI). |
| GUI-08 | N/A | no GUI (non-GUI library/CLI). |
| GUI-09 | N/A | no GUI (non-GUI library/CLI). |
| GUI-10 | N/A | no GUI (non-GUI library/CLI). |
| REL-10 | ✅ | federation member; participates in cohort-walk releases on a mgf-common pin break. |
| REL-11 | ✅ | released via the Phase A/B cohort-release flow. |
| REL-12 | N/A | binds the cohort Phase-A *agent's* pytest-denial handling, not a released sibling's code. |
| REL-13 | ✅ | releases pass the federation consistency gate (`mgf-fed registry --check`). |
| FB-05 | ✅ | cross-repo feedback flows through the `pending-feedback/<target>.md` outbox + the supervisor relay (`mgf-fed feedback`), never a direct edit (FB-05). |
