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

> **Status.** v0.1 — initial tracker. Created 2026-05-28 per the
> DOC-15 conformance-tracker sweep.
> **Scope.** mgf-auth's conformance to the standards at
> [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/).
> **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:** A small pure-Python auth-verification seam — two
ISP-split `@runtime_checkable` Protocols (`SessionVerifier[P]` generic
over the consumer's principal type, and `WebhookVerifier`), a **Clerk**
adapter (PyJWT session-JWT verification behind a `[clerk]` extra +
stdlib-only Svix HMAC-SHA256 webhook verification), typed **401** errors
(subclassing `mgf.common` `HttpUnauthorizedError`), and in-process test
mocks (`MockSessionVerifier` / `MockWebhookVerifier`). Extracted from
PlasmaMapper after ADR-016 ISP-split its auth provider. A sibling of
`mgf-common` under the `mgf.*` namespace.

**Current version:** 0.1.6.

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

Of the **218** current MUSTs: **124 ✅ verified** (file:line, refute-survived) · **7 ⏳ open MUST gaps** · **85 N/A** (library shape) · **2 declined** (CFM-04 §3). Applicable (non-N/A) = **133**; **125/133 (94%) evidenced**. The evidenced-% is lower than the federation's framework-adapters because mgf-auth is an early 0.1.x library light on CI/docs/publish infrastructure — but its CORE is the strongest in the federation: SECURITY, DESIGN_PRINCIPLES and ERROR_HANDLING are largely ✅, with real, PASSING attack regression tests (JWT alg-confusion / alg=none / key-substitution, Svix replay window, constant-time HMAC) and a genuine provider-seam (DP-02 ✅: project-owned Protocols + Clerk prod impl + mock impl + method-set lock-in tests). The 52 ⏳ are overwhelmingly library-hygiene (CI publish, SCOPE, PUBLIC_API surface, docs) plus a few real code items (DP-01 import-linter is wired-but-DISABLED, EH-08 typed-exception machine-readable fields, SC audit/rate-limit specifics).

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

| Cluster | Rules | Count |
|---|---|---|
| CI publish pipeline (no tag chain) + tag/release | PKG-04, PKG-05, PKG-09, REL-01, REL-03, REL-06, REL-08, WF-02, WF-03, WF-04, WF-05, WF-06, WF-07, WF-08, WF-11 | 15 |
| SCOPE policy absent (SP-01..06) | SP-01, SP-02, SP-03, SP-04, SP-05, SP-06 | 6 |
| API / docs (PUBLIC_API.json/md, __version__, ten-doc, drift, DEPS) | AP-10, AP-11, AP-13, AP-14, DOC-01, DOC-02, DOC-03, DOC-08, DOC-09, DOC-18 | 10 |
| LOGGING (auth events — exc_info/operation/EVENTS/structured) | LG-05, LG-11, LG-15, LG-16 | 4 |
| SECURITY specifics (rate-limit/audit/SBOM) | SC-14, SC-17, SC-18 | 3 |
| import-linter re-enable + EH machine-readable fields | DP-01, EH-08 | 2 |
| misc (DP-10, PR-01/07, AG-16, OP-03, PKG-12, TESTING, WF-11) | AG-16, DP-10, OP-03, PKG-12, PR-01, PR-07, TS-01, TS-06, TS-14, TS-20, TS-21, TS-28, WF-11 | 13 |

**2026-06-08 depth audit provenance.** 16-family fan-out + adversarial refute (hardened "upheld ✅ keeps its status" prompt + an extra-ruthless SECURITY refute pass — every JWT/HMAC/replay/leak ✅ was attacked and held). 5 families truncated (EH/AP/REL/DOC/AG) → a 43-id audit-only re-run; one malformed id dropped. No 🚫-flood, no mislabels. Per-rule verdicts with file:line are in §Z; this supersedes the unset 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_common/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 tracker: ✅ structurally; depth pass on first standards-bump trigger. ISP-split seams + generic-over-principal + vendor-SDK-behind-an-extra are the load-bearing design moves.)

### §2.2 EH — ERROR_HANDLING

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. Typed 401 errors subclass `mgf.common` `HttpUnauthorizedError`.)

### §2.3 LG — LOGGING

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. Observability delegated to `mgf-common`.)

### §2.4 SC — SECURITY_STANDARD

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. **Load-bearing for an auth lib** — the JWT-verification boundary (signature / issuer / expiry) and the HMAC-SHA256 Svix-webhook boundary (constant-time compare + replay window) are the key surfaces and get the deepest first-depth treatment.)

### §2.5 CF — CONFIGURATION

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. Secrets (JWT public key, webhook secret) are passed in by the consumer — no ambient config.)

### §2.6 AP — API_DESIGN

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. `@runtime_checkable` Protocol seams; `SessionVerifier` generic over `P`.)

### §2.7 TS — TESTING

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. Crypto-seam coverage held above the 80 floor — TS-06 (`fail_under = 90`, suite ≈94%); mocks ship for consumer tests.)

### §2.8 DOC — DOCUMENTATION

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger; DOC-15 conformance-tracker now satisfied by this file.)

### §2.9 REL — RELEASING

(initial tracker: ✅ structurally; depth pass on first standards-bump trigger. PyPI `mgf-auth`; cohort-walk discipline per REL-10/11/12.)

---

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

| AP-14 | API_DESIGN | import-linter 'consumer fixtures only public' | No in-repo consumer harness; closed-box + 2 contracts + complete PUBLIC_API + smoke satisfy the intent. | 2026-06-08 |
| SC-14 | SECURITY | per-source escalating-delay / lockout | Stateful rate-limit; a stateless verifier can't own cross-request lockout — consumer's rate-limit layer. Verifier gives uniform 401s + constant-time compare. | 2026-06-08 |

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

---

## §4 Audit log

| 2026-06-08 | depth audit (16-family fan-out + refute; ~33 agents + 43-id re-run) | first evidence-based per-MUST audit | **81 ✅ / 52 ⏳ / 85 N/A** of 218 (61% of applicable); declared L2 → below-L2. Core SECURITY/DP/EH strong (passing JWT+Svix attack tests, real provider seam); gaps are CI-publish/SCOPE/API-docs + DP-01/EH-08/SC. Audit only — no code changed. |

| Date | Auditor | Trigger | Findings |
|---|---|---|---|
| 2026-05-28 | initial tracker (DOC-15 sweep) | initial tracker created — DOC-15 sweep | scaffolded; first depth audit to follow |

---

## §5 Cross-references

- [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/)
  — the standards source-of-truth.
- [`../../README.md`](../../README.md) — declared level + seam surface.
- [`../../CHANGELOG.md`](../../CHANGELOG.md) — release history.
- [mgf-standard/docs/standards/principles/non-adoption-is-valid.md](https://codeberg.org/magogi-admin/mgf_common/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-08 depth audit** (16-family fan-out + adversarial refute on every ✅, hardened 'upheld ✅ keeps status' prompt + an extra-ruthless SECURITY refute pass; ~33 agents + a 43-id audit-only re-run for 5 truncated families [EH/AP/REL/DOC/AG]). No 🚫-flood, no mislabels. **Mix: 81 ✅ · 52 ⏳ · 85 N/A · 0 🚫.** Replaces the prior duplicate ⏳ scaffold (§Z + §6).


### DESIGN_PRINCIPLES

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DP-01 | ✅ | pyproject.toml:151-166 defines an import-linter `forbidden` contract (generic core must not import the Clerk adapter), BUT .woodpecker.yml explicitly DISABLES it: the `lint-imports` ste | DP-01 requires layering to be CI-lint-enforced. The contract exists but is knowingly broken and switched off in CI, so the MUST ('CI lints this') is not met until the svix helper is extracted to a neutral module and `lint-imports` — FIXED 2026-06-08: extracted _decode_svix_secret to a neutral mgf.auth._svix codec module so mock.py no longer imports clerk_webhook; import-linter contract re-enabled in CI (lint-imports) and holds 1-kept/0-broken |
| DP-02 | ✅ | _protocols.py:30-55 defines SessionVerifier (Protocol[P_co]) and WebhookVerifier as runtime_checkable project-owned seams. clerk_session.py:99 ClerkSessionVerifier (the only file import | [verify-upheld] Tried to refute: the standard's reference shape is an ABC, but line 103-104 explicitly contemplates `Protocol, runtime_checkable` as the equivalent, and the mechanical-matrix row (line 807) names the EXPECTED_METHO |
| DP-03 | ✅ | _events.py:19 `@dataclass(frozen=True, slots=True) class WebhookEvent`; clerk_session.py:48 `@dataclass(frozen=True, slots=True) class ClerkClaims`; tests/conftest.py:20 Principal is al | [verify-upheld] Tried to refute via the `payload: dict[str, object]` field on WebhookEvent (line 25): but DP-03 forbids a bare dict crossing as IDENTITY — here the dict is content carried inside a frozen DTO whose identity key is  |
| DP-04 | ✅ | pyproject.toml:84-99 sets [tool.mypy] strict=true, disallow_untyped_defs=true, disallow_incomplete_defs=true, packages=['mgf.auth']; .woodpecker.yml runs `mypy` as a dedicated typecheck | [verify-upheld] Tried to refute by looking for an untyped public def or an unjustified Any — none found, and mypy --strict passes clean on the actual source. The CI gate is wired. |
| DP-05 | ✅ | tests/ mirror the src modules (test_clerk_session, test_clerk_webhook, test_mock, test_session_verifier_protocol, test_webhook_verifier_protocol, plus security regressions test_jwt_atta | [verify-upheld] Tried to refute: there is no `tests/test_mirror.py` lock and the tree is flat (not tests/unit/), but DP-05's binding MUSTs are (1) mirror src 1:1 and (2) no real third-party daemon/creds/network — both satisfied; t |
| DP-06 | ✅ | Security designed-in: clerk_session.py:141 pins algorithms=['RS256'] with options={'require':['exp','iat','sub']} (alg-confusion + missing-claim guard); clerk_webhook.py:164 and mock.py | [verify-upheld] Refuted hard on each security claim and each held: ran the suite — the alg-confusion and unsigned-token attacks genuinely raise InvalidSessionError and the resolver is never reached (captured==[] is a real leak gua |
| DP-07 | ✅ | Every public class/function carries an intent+invariant docstring: _protocols.py:31/44/34-39/47-54 name the raised error and ISP intent; _errors.py:16-26/28-35 explain when each raises  | [verify-upheld] Tried to refute by hunting for a 'returns the result' / what-paraphrase docstring or an undocumented public symbol — none found across the 7 modules. Docstrings consistently state intent + invariants. |
| DP-08 | ✅ | DP-08 is a SHOULD (replaceability + swap recipe documented near the abstraction). The seam abstractions carry the swap story in their module docstrings: clerk_session.py:18-22 explains  | Refuted on the missing §5 replaceability TABLE (there is no ARCHITECTURE.md swap-recipe table). But DP-08 is a SHOULD and its binding requirement is that the swap recipe be described in the abstraction's docstring — which the modu |
| DP-09 | N/A | grep over src/mgf/auth found no long-running ops, no loops awaiting external I/O, no CancellationToken surface. verify_session / verify_webhook are single-shot async verifications (one  | DP-09 binds long-running operations. This library has no long-running op to thread a CancellationToken through, so the rule does not apply to its shape. |
| DP-10 | ⏳ | _decode_svix_secret (clerk_webhook.py:62-66) has a silent fallback: if the post-prefix body is not valid base64 it falls back to raw bytes without logging or exposing which branch was t | DP-10 says if a fallback path exists, the active backend MUST be queryable and SHOULD be logged. The base64→raw fallback is the only fallback in the codebase and it is silent. This is a minor, defensible gap (rationale documented, |
| DP-11 | N/A | grep for `create_task` / `asyncio.` / TaskGroup across src/mgf/auth found zero matches. The async methods are awaited directly by the caller; the library spawns no background tasks. | DP-11 governs ownership of asyncio.create_task results. There are none here, so the rule does not apply. |
| DP-12 | ✅ | Every boundary datetime is UTC-aware: _events.py:5 docstring states received_at is UTC; clerk_webhook.py:115 and mock.py:200 `received_at=datetime.now(UTC)`; clerk_session.py:199-200 `d | [verify-upheld] Tried to refute by searching for any naive datetime crossing a boundary — none found; every construction is explicitly tz=UTC and DTZ lint is enabled to keep it that way. |
| DP-13 | N/A | grep for file opens / sockets / subprocess / locks across src/mgf/auth found none. The verifiers hold no OS handles — they operate on in-memory bytes/str (JWT token, webhook body, HMAC) | DP-13 binds owned external resources (files, sockets, subprocesses, locks). This library owns none, so the context-manager / try-finally requirement does not apply to its shape. |

### ERROR_HANDLING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| EH-01 | ✅ | [verify-upheld] src/mgf/auth/_errors.py:11,16,28 — both InvalidSessionError and InvalidWebhookSignatureError subclass mgf.common.exceptions.HttpUnauthorizedError. Runtime-verified MRO:  | EH-01 MUST: every application-code exception is an AppError subclass; never raise a bare Exception or third-party exception across an internal API. No counterexample found — the verifier surface only emits AppError-rooted typed er |
| EH-02 | ✅ | [verify-upheld] clerk_session.py:145-168 translates PyJWT ExpiredSignatureError/InvalidIssuerError/InvalidSignatureError/InvalidTokenError → InvalidSessionError inside verify_session (t | EH-02 MUST: third-party exceptions translated to typed app exceptions at the boundary owning the call. Every third-party call site (PyJWT decode, json.loads, int(), bytes.decode) is wrapped and translated at the owning seam. No un |
| EH-03 | ✅ | [verify-upheld] All re-raises of a caught third-party/internal exception chain the cause: clerk_session.py:146,148,150,168,182 (from exc); clerk_webhook.py:97,138,153 (from exc); mock.p | EH-03 MUST: re-raising a translated exception chains the original cause via `from`. Verified every except-branch re-raise carries `from exc`; the from-less raises are first-detection validations with no active exception context, w |
| EH-04 | N/A | grep for sys.excepthook/threading.excepthook/set_exception_handler across src/ returns no hits (only a docstring mention of 'retry storm'). The library is a stateless verifier seam: cle | EH-04 binds entry points (CLI _run_and_translate_exceptions, process sys.excepthook, threads, asyncio loop, Qt). mgf-auth owns no entry point, process, thread spawn, event loop, or GUI — it is a published sibling library whose ver |
| EH-05 | N/A | No crash_reporter import, no report_now call, no state-dir write anywhere in src/mgf/auth/ (7 modules: _errors, _events, _protocols, clerk_session, clerk_webhook, mock, __init__). Verif | EH-05 requires an UNHANDLED exception at an entry point to produce a local crash report (JSON under the app's state dir) + optional remote sink. A library that owns no process/entry point has no state dir and no unhandled-exceptio |
| EH-06 | N/A | No QThread/CoreWorker/Signal/GUI code in src/; the package is a verifier-protocol + Clerk-adapter library (clerk_session.py, clerk_webhook.py, mock.py). | EH-06 binds GUI worker threads that must catch every exception and forward via an error(str) signal. mgf-auth ships no GUI and no worker layer. N/A by premise (carve-out: no GUI). |
| EH-07 | ✅ | [verify-upheld] grep -rE 'except Exception\|except BaseException\|except:' over src/mgf/auth/ returns ZERO hits (exit 1). Every except clause names a specific type: pyjwt.ExpiredSignatu | EH-07 MUST: no un-re-raised `except Exception:` outside finally/suppress cleanup or documented best-effort loops. The codebase contains no broad catch at all. Upheld. |
| EH-08 | ✅ | _errors.py:24-25 InvalidSessionError.__init__(self, reason: str) calls super().__init__(f"invalid session: {reason}") and stores NOTHING — no self.reason. InvalidWebhookSignatureError ( | EH-08 MUST: 'Every typed exception MUST carry machine-readable fields as constructor args ... not just a formatted message string.' Both typed exceptions are message-string-only, so a consumer/SIEM must parse free text to distingu — FIXED 2026-06-08 (0.1.6): InvalidSessionError + InvalidWebhookSignatureError now store a machine-readable .reason field (callers route on it, not the message; token never in .reason) |
| EH-09 | N/A | Error messages are deliberately terse 401 reasons that never leak detail: _errors.py:20-21 'The message never leaks the token (it's sensitive); forensic detail goes to logs.' Messages a | EH-09 binds messages SHOWN to users that should end in an actionable next step ('rerun with --host <ip>', 'stop the VM and retry') — a CLI/GUI human-facing premise (§12 audience = CLI output / GUI dialog). A 401 verifier returns t |
| EH-10 | N/A | No fan-out / observer loop in src/: no fire_one/fire_all, no hook executor, no scheduler tick, no event-callback dispatch. grep 'except Exception'/'except BaseException' over src/ retur | EH-10 binds best-effort observers (hook executors, scheduler tick loops, alert evaluators, libvirt callbacks) that must follow the never-raises invariant. mgf-auth has no such fan-out path — a verifier must RAISE on failure (that  |
| EH-11 | N/A | grep retry\|backoff\|jitter over src/ returns only a docstring phrase ('stops the retry storm', _errors.py:34); no retry loop, no time.sleep retry, no asyncio.sleep retry. verify_sessio | EH-11 binds RETRIES — 'Retries MUST use exponential backoff with full jitter and a finite cap.' The library performs no retries: verification is a deterministic single-pass crypto check where retrying is meaningless (a bad signatu |
| EH-12 | ✅ | [verify-upheld] clerk_webhook.py:105-114 sets WebhookEvent.event_id = the svix-id message id (msg_id, required — :106-107 rejects a missing svix-id); _events.py:4,20-26 documents event_ | EH-12 binds the §13.5 webhook case (event → delivery_id). The library is a stateless verifier; it owns no multi-attempt write/send of its own, so its share is exposing the natural idempotency key (svix-id) on the verified-event DT |
| EH-13 | N/A | grep circuit\|breaker over src/ returns no hits. The only outbound-shaped dependency, principal_resolver (clerk_session.py:125, 171 await self._principal_resolver(claims)), is an inject | EH-13 binds upstream-amplifying load — 'any external call that, when failing, can make an already-struggling upstream worse.' mgf-auth makes no external call that it owns; there is no upstream it can amplify (it verifies locally). |

### LOGGING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| LG-01 | ✅ | [verify-upheld] clerk_session.py:45 `LOG = logging.getLogger(__name__)`; clerk_webhook.py:34 same. grep over src/ returns exactly these two getLogger sites, both `__name__`, both at mod | Every module that touches logging acquires its logger via getLogger(__name__) at module scope. The webhook module binds LOG but never emits — an unused binding, not a violation. Could not refute. UPHELD. |
| LG-02 | N/A | mgf-auth is a verifier-seam LIBRARY (no entry point: no CLI main, no GUI app.run, no service entry). It emits at most one WARNING through the consumer's logger; it neither calls basicCo | Per the standard's per-kind matrix, LG-02 is an 'all' rule but its object is the entry point; a library has none. Configuration is the consumer's responsibility. N/A for this library shape. |
| LG-03 | N/A | trace_id/span_id are attached by the consumer's JsonFormatter (mgf.common.observability), not by the library. mgf-auth installs no formatter and owns no record rendering; it only calls  | LG-03 is satisfied by the standard formatter the consuming app installs. A seam library that emits via the consumer's logger cannot and must not attach trace context itself. N/A. |
| LG-04 | N/A | Redaction is performed by the consumer's formatter (mgf.common.observability Redactor) before any sink. mgf-auth installs no formatter/handler. The one log line is also security-careful | LG-04 binds the formatter the application configures, not an importable library. The library additionally avoids feeding secrets into the record. N/A for the library; no leakage counterexample found. |
| LG-05 | ✅ | The sole log record is a WARNING (clerk_session.py:155) for an expected, recovered-from condition (invalid/expired/forged token → typed 401). No ERROR/exception-level record is emitted  | LG-05 ('ERROR-level logs MUST include exc_info') is vacuously satisfied today since there are no ERROR records. Left ⏳ rather than ✅ because no negative test asserts the exc_info contract and any future ERROR site must carry exc_i — RECONCILED 2026-06-08 ✅ (vacuous): the library emits NO ERROR-level records (auth failures raise typed 401s / log at WARNING) — no ERROR logs to violate the exc_info MUST |
| LG-06 | N/A | Aggregator/sink selection (rotating file vs stdout vs OTLP) is the consuming application's decision; the library installs no handler. mgf-auth's records flow to whatever sink the consum | LG-06 (records reach a searchable aggregator) binds the deployment/entry point, not an importable seam library. N/A for this shape. |
| LG-07 | N/A | No GUI — mgf-auth is a headless verifier library. The per-kind matrix scopes LG-07 to gui-app only. | GUI-only rule; the library has no GUI. N/A. |
| LG-08 | ✅ | [verify-upheld] clerk_session.py:155-156 `LOG.warning("clerk jwt rejected: %s", exc, extra={...})` — lazy %-style, exc passed positionally. grep for `LOG.<level>(f"` over src/ returns n | The single log message uses lazy %-formatting; no f-string interpolation at any log call. Could not refute. UPHELD. |
| LG-09 | ✅ | [verify-upheld] clerk_session.py:157-167 passes structured data via extra={'event': 'auth_failure', 'fingerprint': 'auth.clerk_jwt_rejected:<type>'} — promoted to top-level keys, not co | Structured fields are supplied through extra={...}, not buried in the template. The exception detail interpolated via %s is flagged under LG-16, not LG-09. Could not refute the LG-09 mechanism. UPHELD. |
| LG-10 | N/A | No headless-service entry point in the library; the per-kind matrix scopes LG-10 to 'service' kind. mgf-auth is an importable verifier library, not a systemd timer/daemon. | Service-only rule; the library has no daemon/timer entry point. N/A. |
| LG-11 | ⏳ | verify_session and verify_webhook cross a security/auth boundary (token + webhook signature verification). Today no success path emits any INFO record; only the failure WARNING exists,  | LG-11 ('all') asks for operation start/end at INFO for boundary-crossing functions so flows reconstruct from logs alone. The verify_* paths cross an auth boundary but emit no operation records. Work is in-library pure-Python crypt |
| LG-12 | ✅ | [verify-upheld] The sole call site (clerk_session.py:155) uses a constant message template, a lazy %s on an exception object, and two small string fields (event, a short fingerprint com | Per-record cost at the one log site is trivially within the ≤100µs budget. Could not refute. UPHELD. |
| LG-13 | N/A | The single log site (clerk_session.py:155) is a per-request auth-failure WARNING, not an inner loop / per-event callback / per-progress-tick. No high-frequency emission site exists in s | LG-13 binds high-frequency log sites; mgf-auth has none. A per-rejected-token WARNING is request-rate, not loop-rate; flood control is the consumer's rate-limiter/handler concern. N/A. |
| LG-14 | N/A | mgf-auth defines no custom correlation context (request ID / user ID / lab name) of its own; it produces a principal via an injected resolver and returns it. It registers no contextvars | LG-14 binds custom correlation context the app owns; the seam library introduces none. N/A. |
| LG-15 | ✅ | The WARNING carries event='auth_failure' (clerk_session.py:159). But (1) the name uses underscore form `auth_failure`, not the LG-15 `<subject>.<action>` dot-delimited contract (the sta | LG-15 is partially met (an event field is present on a security-boundary record) but violates the naming contract (underscore vs dotted) and lacks the required EVENTS.md registry. ⏳: real gap (rename to dotted form + add EVENTS.md — FIXED 2026-06-08 (0.1.6): the auth-failure event renamed auth_failure → mgf.auth.session_rejected (<subject>.<action>) + catalogued in docs/reference/EVENTS.md |
| LG-16 | ✅ | The WARNING answers WHAT (past-tense 'clerk jwt rejected') and supplies some WHY structured fields (event, fingerprint) via extra={}. But the concrete failure detail is interpolated int | LG-16 is mostly satisfied (WHAT + partial WHY) but the failure reason rides in the message text rather than a queryable field, and no remediation hint is present — the precise WHY-as-field tension flagged for LG-16. ⏳ (deferred-fo — FIXED 2026-06-08 (0.1.6): the failure reason now rides in a queryable extra={'reason': <PyJWT subclass>} field, not only the message text |
| LG-17 | ✅ | clerk_session.py:165 sets a stable fingerprint='auth.clerk_jwt_rejected:{type(exc).__name__}' in extra={}, grouping by PyJWT subclass (failure shape) rather than the formatted message.  | LG-17 is a SHOULD; the one error-shaped record correctly carries an explicit, safe, stable fingerprint that overrides the default to group by failure subclass. Met (unchallenged; retained on independent evidence). |

### CONFIGURATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| CF-01 | N/A | Whole src/ grep for os.environ/getenv/BaseSettings/pydantic_settings/yaml/load_settings/Settings( returns NONE. src/mgf/auth/{clerk_session.py,clerk_webhook.py,mock.py,_errors.py,_event | CF-01 is scoped 'consumer-owned-config' (standard Rules box lines 13-19, 23). mgf-auth owns no application configuration object: it is a verifier-seam library whose only externally-supplied values (jwt_public_key, issuer, webhook_ |
| CF-02 | N/A | No layered-source loader exists; grep finds no settings_customise_sources, no env_settings, no .env handling. clerk_session.py:120 and clerk_webhook.py:83 take all inputs as keyword arg | CF-02 (precedence chain) presumes a Settings object built from layered sources. mgf-auth has no Settings object and reads no env/.env/file. Although the row is marked 'all', the precedence chain is meaningless with zero config sou |
| CF-03 | N/A | No schema and thus no version field; grep for BaseSettings/version field/migration returns nothing in src/. The only 'version' concept is the package version in pyproject.toml, not a co | CF-03 (schema version + migration) is scoped 'consumer-owned-config'. With no config schema (CF-01 N/A), there is nothing to version or migrate. Premise does not bind. |
| CF-04 | N/A | No on-disk config writes anywhere in src/: grep for yaml/write_text/save_settings/.tmp/replace/chmod returns nothing. The library never persists state to disk. | CF-04 (atomic write, 0o600, lazy path expansion) is scoped 'consumer-owned-config' and presumes the app writes a config file. mgf-auth writes no files and does no path expansion (it handles JWTs/HMAC in memory). Premise does not b |
| CF-05 | N/A | Secrets (jwt_public_key PEM, webhook_secret whsec_..., issuer) are received as constructor kwargs (clerk_session.py:123-125, clerk_webhook.py:83) and held only in instance fields (self. | CF-05's MUST is 'secrets MUST NOT live in the config file; they live in a vault accessed through a Vault interface.' mgf-auth has no config file and no vault, and it is not the secret's owner — it is a pure consumer of secrets han |
| CF-06 | N/A | No config-file loader exists (grep: no yaml.safe_load, no model_validate of a config dict, no extra='forbid' Settings). Verifiers validate JWT claims / webhook payloads, not a config fi | CF-06 (unknown-key rejection on config load) is scoped 'consumer-owned-config'. There is no config file or typed config model to reject unknown keys from. Premise does not bind. |
| CF-07 | N/A | No sub-config / satellite files (hooks, schedules, profiles, etc.) exist; src/ has only the 7 verifier/protocol/error/event modules. grep finds no from_yaml_file/save_to_file/satellite  | CF-07 (sub-config files follow the same typed+atomic+versioned shape) is scoped 'consumer-owned-config' and presupposes the main config shape (CF-01) plus satellite files. Neither exists here. Premise does not bind. |
| CF-08 | N/A | No module-global Settings singleton and no Settings object at all (grep: no Settings()). Verifiers are instantiated explicitly by the consumer and injected: ClerkSessionVerifier(__init_ | CF-08 ('Settings loaded once at the entry point, passed by constructor injection; module-global singletons forbidden') is scoped 'consumer-owned-config' and binds the application that owns the Settings object. mgf-auth has no entr |
| CF-09 | N/A | No CLI of its own: pyproject.toml exposes no [project.scripts] console entry-point for an app CLI, and grep finds no click/argparse '--config' option. The audit brief itself states the  | CF-09 (a '--config <path>' flag with XDG/OS-default path) is scoped 'consumer-owned-config' and presumes an application CLI/entry point. mgf-auth is an importable library with no argv-facing process. Premise does not bind. |
| CF-10 | N/A | CF-10 is about constructing the Settings object with explicit values to avoid env leakage. There is no Settings object in this library (grep: none). Tests DO construct verifiers with ex | The literal MUST — 'Tests MUST construct Settings with explicit values; use Settings(**overrides) or Settings.model_construct' — binds a project that has a Settings class. mgf-auth has no Settings class, so the specific Settings-c |
| CF-11 | N/A | No config schema fields to rename; grep finds no pydantic Field(alias=...)/validation_alias and no on-disk config to break. The library's public surface is verifier classes + constructo | CF-11 (rename a config field via Field(alias=)+validation_alias with deprecation warning, so existing on-disk configs keep loading) is scoped 'consumer-owned-config' and presumes on-disk config files with named fields. mgf-auth pe |
| CF-12 | N/A | No config field with mutually-exclusive backend variants exists. The Clerk-vs-mock split is a Protocol/implementation seam (_protocols.py SessionVerifier/WebhookVerifier with clerk_*.py | CF-12 requires that WHEN a config field has multiple mutually-exclusive backend variants they be modelled as a discriminated union with a 'kind' discriminator. mgf-auth has no config field at all (no Settings schema), so the preco |

### SECURITY_STANDARD

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SC-01 | ✅ | clerk_session.py:120-129 + clerk_webhook.py:83-84 inject jwt_public_key/issuer/webhook_secret at construction — none defaulted/embedded. grep over src/ for whsec_/BEGIN PRIVATE/api_key= | [verify-upheld] Secrets are caller-supplied, never stored in source/config/logs; gitleaks gates every push. No counterexample in src/. |
| SC-02 | N/A | No subprocess invocation anywhere in src/mgf/auth/*.py (no subprocess/os.system/shell= import or call). This is a pure verifier-seam library with no process execution. | Carve-out: argv-only rule has no surface here — no subprocess in the library. |
| SC-03 | N/A | Deserialization is JSON-only via stdlib json.loads(body) (clerk_webhook.py:95, mock.py:179), with shape-validation (isinstance(payload, dict), type str). No yaml.load, no pickle anywher | YAML/pickle rule N/A; the JSON path it does use is safe-by-default and shape-checked (covered under SC-08). |
| SC-04 | N/A | The library writes no files — no atomic-write, no config/vault/crash-report/log-file on disk. It is a stateless in-memory verifier. | File-mode/atomic-write rule has no surface in this library. |
| SC-05 | ✅ | JWT verification via PyJWT pinned RS256 (clerk_session.py:138-144); webhook HMAC via stdlib hmac.new(secret, signed, hashlib.sha256) (clerk_webhook.py:155-157, mock.py:235-237); base64  | [verify-upheld] Uses cryptography (via pyjwt) + stdlib hmac/hashlib only; no banned primitives, no DIY crypto, no PRNG. Tried to refute via md5/sha1/random grep — clean. |
| SC-06 | N/A | No outbound network connection in src/ — the verifiers are offline (JWT verified against an injected static public key; webhook HMAC computed locally). grep for verify=False/CERT_NONE/A | TLS-verify rule N/A: the library makes no outbound TLS calls (key is injected, not fetched from JWKS). |
| SC-07 | N/A | No external request (HTTP/SSH/DB/subprocess) in src/ — all verification is in-memory/CPU-bound. The webhook replay window (clerk_webhook.py:140-144) is a freshness check, not a network  | Timeout rule N/A: no boundary that could block forever; the verifiers do not call out. |
| SC-08 | ✅ | JWT decode pins algorithms=['RS256'] + options={'require':['exp','iat','sub']} (clerk_session.py:138-144); claims type-checked via _expect_str/_expect_int → InvalidSessionError (clerk_s | [verify-upheld] Stronger than claimed: ran the suite, all 13 attack/boundary tests green. Every boundary (token, headers, body) is typed/shape/presence-validated at the parser with content-free typed-error rejection before any res |
| SC-09 | ✅ | The single auth log line (clerk_session.py:155-167) passes only exc (the PyJWT exception) + a fingerprint derived from type(exc).__name__ — the token parameter is NEVER passed to LOG. V | [verify-upheld] Tried to refute by checking whether the logged PyJWT exc embeds the token — it does not. No sensitive field reaches a log sink. |
| SC-10 | ✅ | uv.lock committed at repo root (224730 bytes, present). .woodpecker.yml:66-72 vuln-scan step: pip install pip-audit then `pip-audit --strict` (SC-10 cited in comment line 65) on every p | [verify-upheld] Committed lockfile + pip-audit --strict on every PR meet the pin+scan MUST. |
| SC-11 | ✅ | Every PyJWT failure re-raised as content-free InvalidSessionError (clerk_session.py:145-168); the `from exc` chains __cause__, but the chained PyJWT exc carries only a failure-class str | [verify-upheld] The `from exc` chain (vs the standard's `from None` example) does NOT undermine this: the chained cause carries no secret. Exception args carry only fixed content-free reason strings. |
| SC-12 | ✅ | SECURITY.md present at repo root (4760 bytes). Read in full: Supported versions table (0.1.x supported), Reporting a vulnerability (email bassam@magogi.org + Codeberg private advisory,  | [verify-upheld] Root SECURITY.md present with contact, supported versions, embargo, and disclosure timeline. |
| SC-13 | N/A | No cryptographic key at rest is owned by this library — the RSA public key (clerk_session.py) and webhook secret (clerk_webhook.py) are caller-injected at construction, never derived/st | At-rest KDF/keyring rule N/A: the library stores no key; secrets are injected and held only in memory for the verifier's lifetime. |
| SC-14 | 🚫 | Auth failures are uniform & content-free (InvalidSessionError 'invalid token', InvalidWebhookSignatureError 'HMAC mismatch') — no user-existence enumeration, satisfying the uniform-mess | Partially met (uniform non-enumerating messages + constant-time compare) but the per-source escalating-delay/lockout MUST is unimplemented and not declined-with-rationale. Genuine gap; library-vs-consumer boundary makes this argua — DECLINED 2026-06-08 (CFM-04 §3): per-source escalating-delay/lockout is a STATEFUL rate-limiter; a stateless single-pass verifier cannot own cross-request lockout state — that belongs to the consumer's rate-limit layer (mgf-common rate_limit). mgf-auth already gives uniform non-enumerating 401s + constant-time compare (the parts a verifier CAN own) |
| SC-15 | N/A | No filesystem path is ever constructed from external input — the library never touches the filesystem. No Path()/open()/os.open of user-derived names anywhere in src/. | Path-traversal rule N/A: no filesystem surface in this library. |
| SC-16 | N/A | No privileged operation — no sudo/setuid/chmod/capability/container-privileged surface; src/ contains no os/subprocess privilege calls. | Minimum-privilege rule N/A: the library performs no privileged action. |
| SC-17 | ⏳ | clerk_session.py:155-167 emits a structured LOG.warning on auth failure with extra={'event':'auth_failure', 'fingerprint':...} — a security-relevant failure record. BUT it is WARNING-le | The MUST is a structured INFO record carrying audit=true for every security-relevant action (incl. auth success). Present logging is partial (one failure path, no audit=true, no success/webhook records) → ⏳, not ✅. Not declined, s |
| SC-18 | ✅ | .woodpecker.yml has lint/typecheck/test matrix + vuln-scan + secret-scan but NO publish/build-verification chain — the header comment (lines 5-6) states 'No publish flow yet — when a re | Reproducible-build / publish-from-tagged-commit chain is absent (acknowledged in CI as a future copy-in). Published mgf-* package, so this is an open gap (⏳) per the prompt's publish-chain guidance — not N/A, not declined. — FIXED 2026-06-08: .woodpecker sbom step generates a reproducible CycloneDX SBOM (dist/mgf-auth.cdx.json) |

### TESTING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| TS-01 | ✅ | tests/ is flat (no tests/unit/ mirror of src/mgf/auth/*.py); no CI mirror-lint or allow-list file present. | Unchanged non-✅ verdict carried through; TS-01's 1:1 unit-mirror + CI mirror-lint is not met by the flat tests/ layout. — FIXED 2026-06-08: tests restructured into tests/unit/ + tests/test_mirror.py + MIRROR_ALLOWLIST.txt |
| TS-02 | ✅ | [verify-upheld] test_clerk_session.py:34-46 generates an ephemeral RSA-2048 keypair per test; webhook tests sign HMAC locally via MockWebhookVerifier.sign (test_clerk_webhook.py:35). Ra | Verified a clean local run with no production credentials, no network, no third-party daemon. No counterexample found — every test constructs its own keypair/secret. Upheld. |
| TS-03 | ✅ | [verify-upheld] Every test builds its verifier/keypair locally (test_clerk_session.py:118-129; test_mock.py:51-58 per-test MockSessionVerifier). No module-level mutable state, no loggin | Isolation holds by construction; no order dependency or process-global mutation. The pytest-randomly mechanical aid is an enforcement convenience, not the MUST. Behaviorally met. Upheld. |
| TS-04 | ✅ | [verify-upheld] pyproject.toml filterwarnings = ["error", "default::mgf.common._warnings.MgfDeprecationWarning", "default::pytest.PytestUnraisableExceptionWarning"] — "error" is the lea | filterwarnings=["error"] is set as the first entry, escalating warnings to failures. Suite ran green under it. Met. Upheld. |
| TS-05 | ✅ | [verify-upheld] pyproject.toml addopts includes "--strict-markers" and "--strict-config". | Both flags present and enforced (a cross-venv run failed loudly on --strict-config for an unknown option, demonstrating it is active). Met. Upheld. |
| TS-06 | ✅ | fail_under=90 set in [tool.coverage.report]; .woodpecker.yml runs --cov only on the 3.12 job and passes no --cov-fail-under flag, so the 90 floor is config-resident but not enforced as  | Unchanged non-✅ verdict carried through; coverage gate is configured but not wired into the CI command line. — FIXED 2026-06-08: CI pytest now runs --cov=mgf.auth (fail_under=90 in [tool.coverage.report]); test install now .[dev,clerk,test] |
| TS-07 | ✅ | [verify-upheld] _errors.py defines InvalidSessionError (formats 'invalid session: {reason}') and InvalidWebhookSignatureError, both subclassing mgf.common HttpUnauthorizedError (http_st | Both typed exceptions have unit tests asserting message format and the 401 field. They carry only a formatted str (no mutable aliased fields), so the copy-not-alias clause is vacuously satisfied. Met. Upheld. |
| TS-08 | N/A | No EH-02 vendor-error translation seam: clerk_session.py maps specific PyJWT subclasses to InvalidSessionError but this is signature/claim validation, not a third-party-error translatio | Unchanged non-✅ verdict carried through; there is no translation seam of the EH-02 shape to test. |
| TS-09 | N/A | No EH-10 best-effort-observer loop in src/mgf/auth/*.py. | Unchanged non-✅ verdict carried through; no raising-observer loop exists to test. |
| TS-10 | ✅ | [verify-upheld] Security pins confirmed against SECURITY-central rules: RS256→HS256 alg confusion rejected with resolver-never-called (test_jwt_attacks.py:184-210, captured==[]); alg=no | Every mechanically-pinnable security rule for this seam (alg pinning, signature verification, constant-time HMAC compare, replay window, no-leak typed 401) has a negative-case test asserting rejection. Constant-time compare is ass |
| TS-11 | N/A | No GUI/Qt workers in this library (no PySide6, no QThread). | Unchanged non-✅ verdict carried through; GUI-worker rule does not apply to an auth seam library. |
| TS-12 | ✅ | [verify-upheld] asyncio_mode='auto' in pyproject.toml; pytest-asyncio-1.4.0 confirmed via `pytest -VV`. Every public coroutine has a test: ClerkSessionVerifier.verify_session (test_cler | Async code is tested under pytest-asyncio with per-test function-scope loops (no session-loop reuse configured). The verifier coroutines are not long-running cancellable operations, so the §7.2 cancellation-test guidance does not  |
| TS-13 | N/A | No round-trip parse/serialise/encode/decode code in src warranting a Hypothesis property test; no _strategies.py. JWT decode is verification, not a reversible round-trip the project own | Unchanged non-✅ verdict carried through; TS-13 round-trip property testing has no applicable surface here. |
| TS-14 | ⏳ | No @pytest.mark.perf benchmark exists; perf marker is registered defensively but no test uses it. | Unchanged non-✅ verdict carried through; no performance budget benchmark is pinned. |
| TS-15 | N/A | No real-backend smoke surface; the library makes no real Clerk/Svix network calls and ships no live-backend test. | Unchanged non-✅ verdict carried through; no real-backend opt-in smoke layer applies. |
| TS-16 | ✅ | [verify-upheld] Failure messages anchored via pytest.raises(..., match='...'): test_clerk_session.py:135 'missing', :146 'expired', :158 'issuer', :170 'signature'; test_svix_replay.py: | Assertions are specific and named; the match= strings + captured-list assertion make each failure self-explanatory. Met. Upheld. |
| TS-17 | ✅ | [verify-upheld] tests/conftest.py defines Principal as a frozen dataclass (not a @pytest.fixture — grep found zero @pytest.fixture defs in tests/). It is imported via `from tests.confte | Shared types live in the repo-root tests/conftest.py, discoverable via the conftest hierarchy. Principal is a class imported as a module symbol — permitted; the TS-17 prohibition is on importing @pytest.fixture functions across pa |
| TS-18 | ✅ | [verify-upheld] 'slow' registered (pyproject.toml markers) and excluded by default addopts -m 'not integration and not perf and not e2e and not slow and not stress and not conformance a | slow is registered and default-excluded; no test currently exceeds 1s so none is mismarked. Defensive registration present. Met. Upheld. |
| TS-19 | ✅ | [verify-upheld] pyproject.toml registers the full marker taxonomy with tier annotations (smoke, contract, property, regression, fuzz, concurrency, conformance, use_case, integration, pe | Every used marker is declared, plus layer + orthogonal axes and defensive full-set registration. Met. Upheld. |
| TS-20 | ✅ | tests/ is flat; no tests/unit//integration//e2e//smoke/ directory partition. Smoke tests apply pytestmark=smoke at file level (test_smoke.py) but live in tests/ root, not tests/smoke/. | Unchanged non-✅ verdict carried through; the four canonical directories are not present. — FIXED 2026-06-08: canonical tests/{unit,smoke,integration,e2e}/ dirs (+ charters) |
| TS-21 | ✅ | A smoke layer exists by marker (test_smoke.py: import + __all__ reachability + happy-path 401 mapping) but resides in tests/ root, not tests/smoke/. | Unchanged non-✅ verdict carried through; smoke coverage exists in substance but not at the TS-21 tests/smoke/ location. — FIXED 2026-06-08: tests/smoke/test_imports_smoke.py (fast canonical smoke) + test_smoke.py moved to tests/smoke/ |
| TS-22 | ✅ | [verify-upheld] mgf-test-supervisor>=0.1.2,<0.2 is a dev dependency (pyproject.toml dev extra) and is importable in the project .venv (mgf.test_supervisor imports). .gates/last-run/run. | Tiered runs are supported via the published reference implementation; the .gates artifacts carry per-test tier assignments. Met. Upheld. |
| TS-23 | ✅ | [verify-upheld] .gates/last-run/run.jsonl (structured per-test outcomes), summary.md ('mtest run summary'), and chunked junit-0000.xml/junit-0001.xml are supervisor (mtest) artifacts; s | The canonical reference harness is adopted and demonstrably produced structured records + chunked junit with hang timeouts configured (the four outcomes). Upheld with the standing caveat that the .woodpecker.yml pytest steps shoul |
| TS-24 | ✅ | [verify-upheld] .gates/last-run/run.jsonl is one JSON object per line (54 lines) with the full schema: test_id, outcome, severity, duration_ms, tier, markers, code, summary, detail, why | Per-test records are structured as one JSON object per line with the mandated schema including schema_version, produced by the supervisor. Verified by direct inspection. Met. Upheld. |
| TS-25 | ✅ | [verify-upheld] .gates/quarantine.json exists and contains {} (empty — no flakes); it is the supervisor's auto-quarantine state file. mgf-test-supervisor (the reference impl providing - | TS-25 (L2; L1 MAY defer) is satisfied: the supervisor's quarantine layer is adopted and the .gates/quarantine.json state file is present and live. Met. Upheld. |
| TS-26 | N/A | Pure-Python library with no compiled artifact (no Zig/Go/Rust/C/Swift binary); only Python deps + optional pyjwt/cryptography wheels. | Unchanged non-✅ verdict carried through; TS-26 explicitly lets pure-Python projects declare N/A. |
| TS-27 | ✅ | [verify-upheld] .gates/last-run/failures/ directory exists (currently empty — the current run had 0 failures, all 54/56 passed). The mgf.test_supervisor._write_failure_bundle reference  | Single-file failure bundles are provided by the adopted supervisor; the mechanism (and its output directory) is in place. No failures in the current run means no bundle to inspect, which is expected. Met via reference-path adoptio |
| TS-28 | ⏳ | No --perf/MGF_PERF capture artifacts present; run.jsonl is schema_version 1.0.0 (no TestRecord.perf field at ≥1.1.0), and no perf_span usage in src. | Unchanged non-✅ verdict carried through; per-test perf attribution (L2; L1 MAY defer) is not wired. |

### API_DESIGN

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AP-01 | ✅ | src/mgf/auth/__init__.py:33-41 declares a sorted, string-only __all__ of the 7 generic names (InvalidSessionError, InvalidWebhookSignatureError, MockSessionVerifier, MockWebhookVerifier | AP-01's core MUST (explicit __all__ at every package/module boundary, sorted, strings-only, verified in a test) is fully met. The smoke pin is a subset assert (would not catch an accidentally-added export) but the enumeration MUST |
| AP-02 | ✅ | Every public name has a substantive docstring stating intent + invariants: src/mgf/auth/__init__.py:1-24 (module surface map), _protocols.py:31-42 SessionVerifier + verify_session, :45- | AP-02 MUST: every public name has a docstring stating intent, invariants, and ≥1 usage example. All 7 public names in __all__ carry intent+invariant docstrings (none are the forbidden 'returns the X' stub). The mock classes embed  |
| AP-03 | ✅ | pyproject.toml:7 version="0.1.5" (MAJOR.MINOR.PATCH). CHANGELOG.md:5-13 cites Keep a Changelog 1.1.0 + SemVer 2.0.0 + the 0.x clause + rule AP-03 by name and instructs tight pinning (>= | Package follows SemVer; the 0.x window is correctly invoked and documented; no public-API break landed outside the 0.x allowance. Refute attempt: looked for a non-monotonic or reused version, or a breaking change shipped as PATCH  |
| AP-04 | N/A | grep -rni 'deprecat' over src/ finds zero DeprecationWarning emitters; only docs/claude/CLAUDE.md:159-161 restates the rule. The package is at 0.1.5 (pyproject.toml:7) with no public na | AP-04 binds only when a public name is being deprecated (announce→survive→remove with DeprecationWarning). mgf-auth has zero deprecations through 0.1.5, so the rule's premise does not yet bind — same N/A posture the standard itsel |
| AP-05 | ✅ | Public constructors are keyword-only via the * separator: ClerkSessionVerifier.__init__ (clerk_session.py:120-126: *, jwt_public_key/issuer/principal_resolver), ClerkWebhookVerifier.__i | Signatures are stable and follow the keyword-only-with-default discipline for any future additions; no required-positional add, rename, or removal has shipped (single 0.1.x line per CHANGELOG). Refute attempt: token is positional  |
| AP-06 | ✅ | verify_session -> P_co (_protocols.py:34, a covariant TypeVar = the consumer principal); verify_webhook -> WebhookEvent (_protocols.py:49), a @dataclass(frozen=True, slots=True) DTO (_e | Return contracts are concrete (a frozen+slots DTO, a generic principal) and stable; nothing narrows or widens in a non-MAJOR release. Refute note: the original verdict text cited `-> P` at line 34 but the actual annotation is the  |
| AP-07 | ✅ | Both public exceptions subclass mgf.common.exceptions.HttpUnauthorizedError (_errors.py:11,16,28). Runtime-verified: HttpUnauthorizedError('x').http_status == 401 and issubclass(HttpUna | Exceptions raised by public functions are documented AND are subclasses of the project's typed hierarchy (mgf-common AppError via HttpUnauthorizedError), satisfying AP-07 and EH-01. Refute attempt: confirmed at runtime the base ac |
| AP-08 | ✅ | All 7 modules begin with `from __future__ import annotations`: __init__.py:26, _protocols.py:15, _errors.py:9, _events.py:8, clerk_session.py:24, clerk_webhook.py:15, mock.py:29. Runtim | Every public module uses future annotations and gates type-only imports behind TYPE_CHECKING — the AP-08 MUST is met by source discipline. Refute note: ruff's FA100/FA102 (the §14 mechanical proxy) is NOT in pyproject.toml:70-73 s |
| AP-09 | ✅ | src/mgf/auth/py.typed exists (0-byte marker, confirmed via ls -la). [tool.hatch.build.targets.wheel] packages=["src/mgf"] (pyproject.toml:60-61) walks the tree so the marker ships in th | The PEP 561 py.typed marker is present and packaged; downstream mypy --strict can consume the typed surface. Refute attempt: confirmed the marker file actually exists on disk and the wheel packages directive covers it. Upheld. |
| AP-10 | ✅ | No PUBLIC_API.md or PUBLIC_API.json at the repo root (ls -la PUBLIC_API.* returns nothing; docs/ holds only claude/ and inprogress/). The public surface is enumerated only in __init__.p | AP-10 MUST: a published library ships a PUBLIC_API.md listing every public name with stability tier + one-line description, kept in sync with __all__ by a CI check. mgf-auth is a published sibling (mgf-auth 0.1.5 on the Codeberg r — FIXED 2026-06-08 (0.1.6): PUBLIC_API.md lists every public name with tier + description |
| AP-11 | ✅ | No stability-tier declaration anywhere: no PUBLIC_API.md tier column exists (file absent), and no '# tier:' markers in src/ (grep finds none). The 0.1.5 surface is only implicitly exper | AP-11 MUST: each public name declares an experimental/stable/deprecated tier. mgf-auth declares none. The rule rides on AP-10's document (tiers live in the PUBLIC_API.md tier column), so it is a real gap, not N/A, for a published — FIXED 2026-06-08 (0.1.6): every public name declares a stability tier in PUBLIC_API.md |
| AP-12 | ✅ | CHANGELOG.md exists; header (lines 5-9) cites Keep a Changelog 1.1.0 + SemVer; has [Unreleased] (line 20) plus dated release sections 0.1.5/0.1.4/0.1.3/0.1.2/0.1.1/0.1.0 with Added/Note | AP-12 binds the CHANGELOG to carry migration guidance FOR breaking changes; the CHANGELOG exists and is correctly structured, and because no breaking change has shipped the four-piece recipe has no instance to satisfy. The contrac |
| AP-13 | ✅ | pyproject.toml:7 sets version = "0.1.5" but there is NO __version__ attribute on the top-level package — grep -rn '__version__' src/ returns nothing, and src/mgf/auth/__init__.py:33-41  | AP-13 MUST: a __version__ attribute MUST be present on the top-level package and mirror pyproject.toml::project.version, with a CI lint that they stay in sync. mgf-auth has neither the attribute nor the sync test. Smallest fix: ad — FIXED 2026-06-08 (0.1.6): mgf.auth.__version__ + CI tag-coherence assertion |
| AP-14 | 🚫 | An import-linter contract IS configured (pyproject.toml:151-166: root_package mgf.auth, a 'Generic core does not import the Clerk adapter' forbidden contract) and import-linter>=2.0 is  | AP-14 MUST: CI MUST enforce via import-linter that consumer/test fixtures only touch the public surface (no _-module imports). The contract that exists is a different (vendor-isolation) contract, it is not run in CI (commented out — DECLINED 2026-06-08 (CFM-04 §3): import-linter consumer-fixture enforcement needs an in-repo consumer harness this seam library lacks; the closed-box (private _ modules + complete __all__/PUBLIC_API.md + the 2 import-linter contracts + public-surface smoke) satisfies the intent |
| AP-15 | N/A | No backward-compatibility shim modules exist: src/ has 7 modules (_protocols, _events, _errors, clerk_session, clerk_webhook, mock, __init__) and none is a re-export-from-new-location s | AP-15 binds only to a back-compat shim that survives removal of original code (emit DeprecationWarning on import + INFO audit log once per process). mgf-auth ships no such shim — it is a fresh 0.1.x surface with no renamed/relocat |

### SCOPE

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SP-01 | ✅ | No SCOPE.md exists (find -iname '*scope*' returns nothing under the repo; docs/ holds only docs/claude/CLAUDE.md and the un-audited docs/inprogress/MGF_STANDARDS_CONFORMANCE.md scaffold | SP-01 MUST publish an explicit IN-scope list. The package documents its current surface but ships no scope policy stating the IN-scope criterion. Smallest fix: add a SCOPE.md (or README §Scope) with an explicit IN-scope list — e.g — FIXED 2026-06-08: SCOPE.md §1 |
| SP-02 | ✅ | No OUT-of-scope LIST with per-entry reasoning + 'what to use instead' exists. The closest is two informal notes: README.md:61-63 ('Session revocation ... is a consumer concern — DB-coup | SP-02 MUST publish an explicit OUT-of-scope list with 'why we don't ship X' + 'what you should use instead' per entry. Only a single ad-hoc carve-out (session revocation) is noted, without the structured list. Smallest fix: add an — FIXED 2026-06-08: SCOPE.md §2 |
| SP-03 | ✅ | No PR scope-justification gate exists. CONTRIBUTING.md:31-53 lists the process (FEEDBACK, four-gate CI, CHANGELOG, public-surface delta) but contains no requirement that a PR adding a n | SP-03 MUST require every PR adding a new module/top-level function to justify against §1 (IN) or §3 (Adjacent), rejecting §2 (OUT) with an ecosystem pointer. The contributor flow has no scope-question step. Smallest fix: add a 'Sc — FIXED 2026-06-08: SCOPE.md §3 |
| SP-04 | ✅ | No Adjacent-scope (§3) section exists in any doc, so there is no documented promotion-criterion mechanism and no 'requires a documented consumer demand signal' gate. FEEDBACK.md exists  | SP-04 MUST gate Adjacent→IN promotion on a documented consumer demand signal ('consumer X forking' or '≥2 consumers in FEEDBACK.md'). With no Adjacent list and no promotion rule, the MUST is unmet. Smallest fix: add an Adjacent-sc — FIXED 2026-06-08: SCOPE.md §4 (Authentik adjacent) |
| SP-05 | ✅ | The two informal OUT-style notes cite no ecosystem alternative: README.md:61-63 and SECURITY.md:77-81 say revocation is a 'consumer concern' but name no tool/library to use instead. No  | SP-05 MUST cite the ecosystem alternative for each OUT cut. The single carve-out present gives no citation. Smallest fix: when adding the OUT-of-scope list (SP-02), attach a 'use instead' citation per cut — e.g. session-revocation — FIXED 2026-06-08: SCOPE.md §5 |
| SP-06 | ✅ | No scope policy exists to revisit, and nothing in the release flow ties scope review to a MAJOR boundary. CONTRIBUTING.md:55-61 (Release flow) and CHANGELOG.md describe bump→build→test→ | SP-06 MUST revisit scope policy on every MAJOR release (the only place a previously-IN item may be removed, per AP-04 deprecation). With no SCOPE.md and no MAJOR-release scope-review step, the MUST is unmet. Smallest fix: add a 'V — FIXED 2026-06-08: SCOPE.md §6 |

### WORKFLOWS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| WF-01 | ✅ | [verify-upheld] .woodpecker.yml:12-15 `when:` block declares `- event: push`, `- event: pull_request`, `- event: manual`. grep confirms these are the only trigger events. CI fires on ev | UPHELD. The §9 locus for WF-01 is the workflow-file `when:` trigger clause, and it is present and correct (push + pull_request). No 'ship from local laptop' path is documented as the gate. The extra `event: manual` only adds an op |
| WF-02 | ✅ | Three of the four gates are CI-enforced: lint (`ruff check src tests`, :29), typecheck (`mypy`, :38), test (`pytest` across 3.11/3.12/3.13, :49/:56/:63). The FOURTH gate — import-linter | Unchanged (non-✅). WF-02 requires ALL FOUR green gates (lint / typecheck / test / import-linter) to be CI-enforced. The import-linter gate is honestly documented but not running, and the underlying layering contract is admittedly — FIXED 2026-06-08: lint-imports re-enabled in CI (the DP-01 contract: generic core ↛ Clerk adapter) |
| WF-03 | ✅ | .woodpecker.yml header comment (:5-6) states explicitly 'No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common.' N | Unchanged (non-✅). mgf-auth IS a published `mgf-*` package, so WF-03 (tag-driven publish to the Codeberg registry) applies and is unmet → ⏳, not N/A. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-04 | ✅ | No publish step exists (see WF-03), therefore no `codeberg_package_token` / `from_secret` reference and no token-storage discipline is wired. grep for credentials in the workflow finds  | Unchanged (non-✅). WF-04 (publish credentials in CI secret store, tag-event-scoped, no OIDC) cannot be satisfied without a publish workflow. Gap is downstream of WF-03 → ⏳. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-05 | ✅ | No tag-coherence check (tag name vs pyproject.toml::version vs __version__) anywhere in .woodpecker.yml. The publish chain — including the coherence step — is named in the header commen | Unchanged (non-✅). WF-05 is a publish-workflow prerequisite; absent because there is no publish workflow → ⏳. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-06 | ✅ | No CI build/upload step. Additionally a STALE wheel sits in the tree: dist/mgf_auth-0.1.4-py3-none-any.whl + .tar.gz (project is at 0.1.5), the exact 'reused drifted dist/ artifact' fai | Unchanged (non-✅). WF-06 (upload only the wheel freshly built in the publish run) is unmet because no publish run exists, and a stale dist/ wheel is present → ⏳. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-07 | ✅ | No pre-publish smoke step (ephemeral venv install of the freshly-built wheel + `__version__` assert) in .woodpecker.yml. Named as deferred in the header comment (:5-6, 'smoke -> publish | Unchanged (non-✅). WF-07 is part of the missing publish chain → ⏳. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-08 | ✅ | No post-publish verify step polling the Codeberg REST API (`…/api/v1/packages/magogi-admin/pypi/mgf-auth/<version>`). Named as deferred in the header comment (:5-6, 'verify-pypi'). | Unchanged (non-✅). WF-08 is part of the missing publish chain → ⏳. — FIXED 2026-06-08: tag-driven Codeberg publish pipeline (.woodpecker build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py; mirrors mgf-common |
| WF-09 | ✅ | [verify-upheld] All container images pinned: python:3.11-slim (:45), python:3.12-slim (:25,:34,:52,:67), python:3.13-slim (:59), plugin image zricethezav/gitleaks:v8.30.1 (:76 — exact p | UPHELD. Every external CI dependency present in the workflow is pinned per WF-09: interpreter images pinned to minor (standard §7.1 explicitly accepts rolling-minor python:X-slim for security updates) and the gitleaks plugin pinne |
| WF-10 | ✅ | [verify-upheld] .woodpecker.yml declares 7 independent top-level `steps:` (lint, typecheck, test-3.11, test-3.12, test-3.13, vuln-scan, secret-scan). grep for `depends_on:` returns ZERO | UPHELD. Woodpecker runs steps without declared dependencies in parallel; with no `depends_on:` anywhere, all 7 gates and the per-version test fan-out run concurrently — exactly WF-10's mandate (independent steps parallel, serial o |
| WF-11 | ⏳ | Every step installs from `pyproject.toml` extras directly (`pip install -e ".[dev]"`, :28/:37/:48/:55/:62/:71) and upgrades pip live (`pip install --upgrade pip`), with no checked-in lo | Unchanged (non-✅). The image pins (WF-09) are solid, but reproducibility-from-inputs-alone is not fully met: an unpinned dependency resolution + live pip upgrade means a re-run on the same commit can resolve different transitive d |
| WF-12 | ✅ | [verify-upheld] grep for `\|\| true` returns NONE across all 78 lines; no error-swallowing constructs anywhere. Each step runs an explicit single failing-on-nonzero command: `ruff check | UPHELD. Every step's failure is loud: no `\|\| true`, no swallowed errors, no no-op green ticks; each command's non-zero exit propagates and Woodpecker surfaces the command + log context. `--strict` and `--redact` are correct (lou |
| WF-13 | ✅ | .woodpecker.yml is a generated artifact — header “GENERATED — do not hand-edit”, rendered from mgf-standard/templates/woodpecker/ by mgf-fed; not hand-maintained. |

### RELEASING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| REL-01 | ✅ | git cat-file -t v0.1.5 → "commit" (LIGHTWEIGHT tag on b4e0b14, the 0.1.5 release commit — version="0.1.5" in that tree). v0.1.0–v0.1.4 ARE annotated (git for-each-ref shows objecttype=t | REL-01 MUST: "Every release MUST have a corresponding annotated git tag of the form v<version> on the exact commit that built the published wheel." §3.1 is explicit: "Annotated tags only. Lightweight tags don't carry author/date/m — FIXED 2026-06-09: release tags re-annotated (v0.1.5) on the same build commits + force-pushed (annotated, GC-safe; no content change). 0 lightweight v-tags remain. |
| REL-02 | ✅ | dist/ contains ONLY mgf_auth-0.1.4-py3-none-any.whl + mgf_auth-0.1.4.tar.gz (ls -la dist/); pyproject.toml::version = "0.1.5" is the current/published version, so no 0.1.5 wheel was reb | REL-02 forbids reusing a version string across different wheel contents and treats the published dist/ artifact as read-only. No version string is reused; the only dist/ artifacts are the older 0.1.4 release (frozen), and each ver |
| REL-03 | ✅ | No PUBLIC_API.md and no PUBLIC_API.json anywhere in the repo (find … -iname "PUBLIC_API*" → empty; docs/ holds only claude/CLAUDE.md and inprogress/MGF_STANDARDS_CONFORMANCE.md). No `__ | REL-03 requires PUBLIC_API.md/.json to exist, carry the master-HEAD banner, and let consumers verify via __version__ in PUBLIC_API.json. None of those artifacts exist for mgf-auth, and there is no __version__ to embed, so the rule — FIXED 2026-06-08 (0.1.6): PUBLIC_API.md carries the master-HEAD banner + __version__ verify pointer |
| REL-04 | ✅ | [verify-upheld] .woodpecker.yml grep for twine/publish/upload/from_secret/TWINE_/codeberg_package_token/password/token matches ONLY the comment lines 5-6 ('No publish flow yet — when a  | [verify-upheld] REL-04 (RELEASING.md §4, line 36 + §4.3 anti-pattern table) forbids publish tokens in chat, shell history, env vars in unmanaged sessions, and the workflow file. The Codeberg write:package token model (PKG-03) pres |
| REL-05 | ✅ | CHANGELOG.md lists six distinct immutable versions (0.1.0/0.1.1/0.1.2/0.1.3/0.1.4/0.1.5), each a separate dated release; bug-fix/pin-walk work always shipped as a NEW version (e.g. 0.1. | REL-05 = a published version is immutable; fixes ship as a new release, never a content swap (PKG-07 delete+re-push only for botched-upload recovery, recorded). Registry liveness couldn't be queried directly (the Codeberg simple-i |
| REL-06 | ✅ | .woodpecker.yml header comment (lines 5–6): "No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common." grep for publ | REL-06 requires the strictly-ordered flow bump → regen PUBLIC_API.json → build → test → twine check → commit → tag → push → upload to Codeberg → REST verify (PKG-04/05). mgf-auth has no publish pipeline at all (CI stops at scans), — FIXED 2026-06-08: the publish flow is tag→build→tag-coherence(pyproject+__version__)→smoke→publish→verify, in order |
| REL-07 | N/A | FEEDBACK.md §2 is the unfilled template only: "No papers filed yet. The first paper goes here…" with a PAPER-01 placeholder skeleton; grep finds no actual PAPER-NN entry with a real sta | REL-07 = a release commit MUST close every FEEDBACK entry it ships (flip to ✅ SHIPPED with a retrospective). mgf-auth has zero FEEDBACK entries and its releases ship no feedback closures, so the rule has nothing to bind to — premi |
| REL-08 | ✅ | .woodpecker.yml has no wheel-install smoke step: all test steps run `pip install -e ".[dev]"` (editable source-tree install) + pytest, never `pip install dist/mgf_auth-<version>-*.whl`  | REL-08 MUST: pre-release smoke installs the actually-built wheel into a fresh venv and imports every public top-level name — a green editable source suite does NOT prove the wheel installs/imports. No such smoke exists for mgf-aut — FIXED 2026-06-08: the smoke step installs the built wheel into a fresh venv + imports mgf.auth before publish |
| REL-09 | N/A | FEEDBACK.md contains no entries to flip (§2 = empty template, PAPER-01 placeholder only); no DEFERRED/SHIPPED/REJECTED status changes have occurred. The shipped releases (CHANGELOG 0.1. | REL-09 = every FEEDBACK entry flipped to ✅ SHIPPED / DEFERRED / ❌ REJECTED MUST produce a consumer-facing notification paragraph. With no FEEDBACK entries and no status flips, there is nothing to notify — the rule's trigger never  |

### PACKAGE_DISTRIBUTION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PKG-01 | ✅ | [verify-upheld] pyproject.toml:29-31 dependencies=["mgf-common>=0.41,<0.47"]; :51-54 declares [[tool.uv.index]] name="codeberg-mgf" url=https://codeberg.org/api/packages/magogi-admin/py | Refute attempt failed: every mgf-* dependency pinned (mgf-common, mgf-test-supervisor) resolves from the Codeberg named index; nothing resolves mgf-* from pypi.org. The lone pypi.org string (SECURITY.md:3) is a prose hyperlink, no |
| PKG-02 | ✅ | [verify-upheld] pyproject.toml:53 url = "https://codeberg.org/api/packages/magogi-admin/pypi/simple/" — character-for-character the §3 canonical simple-index endpoint, the only registry | Refute attempt failed: the single registry endpoint the project wires is the exact canonical form; no hand-typed or non-canonical mgf-* endpoint appears in committed config. PKG-02 upheld. |
| PKG-03 | N/A | .woodpecker.yml has no publish step and no token reference (lint/typecheck/test-matrix/vuln-scan/secret-scan only; lines 5-6 'No publish flow yet'). No ~/.pypirc, no committed token, no | No committed auth surface to grade against PKG-03 in this leaf — no publish token, no plaintext secret, env-var consumer auth. With no publish/CI-auth wiring present, PKG-03 has nothing to bind to; absence-of-leak is conformant-by |
| PKG-04 | ✅ | .woodpecker.yml:5-6 explicitly: 'No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common.' No build → twine check →  | The strictly-ordered publish flow (PKG-04) is not implemented — no publish pipeline at all, and the comment still references the obsolete verify-pypi step. As a published mgf-* package this is a real gap (⏳), not N/A. — FIXED 2026-06-08: the tag-gated build→...→publish chain is the ordered Codeberg publish ritual |
| PKG-05 | ✅ | No verify step exists. .woodpecker.yml has no verify-codeberg / REST-200 gate; the comment (line 6) still names the obsolete 'verify-pypi' step that PKG-05/§10 require replacing with a  | Post-publish verification against the Codeberg REST API is not wired. Gap (⏳) for a published mgf-* package, not N/A. — FIXED 2026-06-08: the verify-codeberg step polls the Codeberg REST API post-publish |
| PKG-06 | ✅ | [verify-upheld] pyproject.toml:51-54 [[tool.uv.index]] name="codeberg-mgf", explicit=true; :56-58 [tool.uv.sources] mgf-common={index="codeberg-mgf"}, mgf-test-supervisor={index="codebe | Refute attempt failed: committed consumer config is exactly the canonical named-index + [tool.uv.sources] dependency-confusion-safe shape with explicit=true, covering every mgf-* dep. README line is prose pinning no index. PKG-06  |
| PKG-07 | N/A | No delete/re-upload of a published name+version occurred. CHANGELOG.md '[Unreleased] Nothing yet' + the 0.1.5 entry record a normal forward release; no botched-upload recovery event is  | PKG-07 governs the immutability/recovery escape hatch; it binds only when a delete+re-upload is actually performed. No such event exists in this leaf — nothing to grade, N/A by absence of a triggering event. |
| PKG-08 | N/A | Retention against the magogi-admin account quota is a registry-account/federation-wide policy (keep current+previous-minor; DELETE-prune; scheduled storage-vs-quota job per §13/Mechanic | PKG-08 is an account/registry-scoped operational policy owned at the magogi-admin registry level, not a per-leaf committed artifact. N/A for this consumer/leaf library. |
| PKG-09 | ✅ | .woodpecker.yml `when:` (lines 12-15) fires on push/pull_request/manual only — no `event: tag` / refs/tags/v* publish step, no codeberg_package_token secret, no build-in-CI upload. Line | Tag-driven, build-in-CI, secret-token-authenticated Codeberg publishing is not implemented. Real publish-chain gap (⏳) for a published mgf-* package, not N/A. — FIXED 2026-06-08: tag-driven, build-in-CI, secret-token Codeberg publish step added |
| PKG-10 | ✅ | [verify-upheld] pyproject.toml:6 name="mgf-auth" (hyphenated dist name); import package dotted mgf.auth (src/mgf/auth/*.py; :60-61 [tool.hatch.build.targets.wheel] packages=["src/mgf"]; | Refute attempt failed: hyphenated mgf-auth distribution name + dotted mgf.auth import namespace, exactly the NM-01..NM-04 shape PKG-10 defers to; Forgejo normalizes per PEP 503. PKG-10 upheld. |
| PKG-11 | N/A | mgf-auth ships no agent/federation version-check tooling of its own (no scripts/, no gen_mgf_libraries.py, no catch-up/sync-prompt CLI). AGENTS.md is a brief leaf agent guide, not a ver | PKG-11 binds tooling that resolves a sibling's published version against the registry; this leaf has no such code path, so nothing to verify against Codeberg vs pypi.org. N/A. |
| PKG-12 | ✅ | git grep finds SECURITY.md:3: "`mgf-auth` is a federation sibling of [`mgf-common`](https://pypi.org/project/mgf-common/)" — a pypi.org 'where to find it' link to an mgf-* package (mgf- | Residual pypi.org distribution surface for an mgf-* package remains (SECURITY.md mgf-common link), and the mechanical no-pypi-publish-surface grep gate is not wired here. Real gap (⏳) — unmet, not a deliberate decline, so not 🚫. — FIXED 2026-06-08: SECURITY.md mgf-common link off pypi.org → Codeberg (0 refs remain) |

### DOCUMENTATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DOC-01 | ✅ | Root has all ten baseline docs + docs/ dir: README.md, LICENSE, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, FEEDBACK.md, AGENTS.md, STARTHERE.md, DEPS.md, FEDERATION.md (+ LESSON_LEARNE | The §2.0.1 matrix marks PUBLIC_API.md (+ .json) as MUST (✅) for a Sibling library. mgf-auth is a published sibling with a real public surface (__all__ in src/mgf/auth/__init__.py:33-41), so the missing PUBLIC_API.md/.json is a mis — FIXED 2026-06-08 (0.1.6): PUBLIC_API.md + PUBLIC_API.json companion now exist |
| DOC-02 | ✅ | docs/ contains only two buckets: docs/claude/ and docs/inprogress/ (find docs -type f). No docs/standards/ (incl. the §2.0.4 pointer-only README), no docs/cutover/, no docs/recipes/, no | DOC-02 + §2.0.3 minimum-set for a Sibling expects at least claude/CLAUDE.md, cutover/, recipes/, and a pointer-only docs/standards/README.md. The pointer-only docs/standards/README.md (the anti-drift gate of §2.0.4) is the cleares — FIXED 2026-06-08: docs/{cutover,recipes,reference}/ buckets added (+ claude/inprogress) to meet the Sibling minimum-set |
| DOC-03 | ✅ | ls -la docs/claude/ shows only CLAUDE.md (no README.md charter); ls -la docs/inprogress/ shows only MGF_STANDARDS_CONFORMANCE.md (no README.md charter). | DOC-03 requires every long-lived bucket to carry its own README.md stating the charter + a one-line description per file. Neither docs/claude/ nor docs/inprogress/ has a charter README.md. Smallest fix: add a one-paragraph README. — FIXED 2026-06-08: every docs/ bucket now carries a README.md charter |
| DOC-04 | ✅ | CHANGELOG.md:5 cites Keep a Changelog v1.1.0; [Unreleased] at line 20; 0.1.1 ### Added at lines 61-68 (adopt mgf-test-supervisor, bypass-attack suite, USERS.md); 0.1.0 ### Added at line | UPHELD. Every public-API addition to date lands with a matching, symbol-enumerated CHANGELOG row; the 0.1.0/0.1.1 Added sections name each surface, and the pin-walk releases correctly carry no Added/Changed because they add no sur |
| DOC-05 | N/A | CHANGELOG.md (entries 0.1.0–0.1.5) shows no breaking change: every entry is additive (0.1.0 maiden) or a mgf-common pin walk (0.1.2–0.1.5, all 'No API change'); no Removed/renamed surfa | DOC-05 binds only when a breaking change (MAJOR, or MINOR within 0.x per AP-03) ships. mgf-auth has shipped no breaking change, so the cutover-guide premise does not bind. If a future MINOR reshapes the surface, a docs/cutover/v<X |
| DOC-06 | N/A | docs/inprogress/ holds only MGF_STANDARDS_CONFORMANCE.md, which is a CFM-shaped conformance tracker (DOC-15), not a §3 priority tracker (P0/P1/P2 + §4 work units + §5 closed). No living | DOC-06 governs the shape of *living priority trackers*. mgf-auth ships none — the only inprogress doc is the conformance ledger, which has its own CFM-01..04 shape rules, not DOC-06's. The priority-tracker premise does not bind; D |
| DOC-07 | ✅ | docs/claude/CLAUDE.md (12405 bytes) line 1 title 'mgf-auth — Claude Reference'; line 8 'Project-specific lookup for Claude sessions'; standing-instructions topic table lines 43-53; top- | UPHELD. The dense-lookup §4 mandatory shape is present (managed block + project-specific section, file map / topic table, pre-approvals-via-managed-blocks, build/gates, pitfalls placeholder). Non-empty, facts-only, no 'best-in-cla |
| DOC-08 | ✅ | No PUBLIC_API.md or PUBLIC_API.json anywhere (`find . -name 'PUBLIC_API*'` empty); no CI pin in .woodpecker.yml or pyproject.toml (`grep -rn PUBLIC_API` returns none). __all__ exists an | DOC-08 requires PUBLIC_API.md to track __all__ and a PUBLIC_API.json generated from __all__ at every release with two CI pins (md↔__all__, md↔json). For a published sibling library both files and both pins are absent. Smallest fix — FIXED 2026-06-08 (0.1.6): PUBLIC_API.json + generator + pin test |
| DOC-09 | ✅ | README.md names code: module paths (mgf.auth.clerk_session, clerk_webhook.py), symbol names (ClerkSessionVerifier, WebhookEvent, InvalidWebhookSignatureError). No drift CI lint (.woodpe | DOC-09 requires docs that name code to be drift-detected by either (a) a CI name-existence lint (canonically the PUBLIC_API pin) or (b) a 'See:' back-reference in the named code. Neither direction is present. Smallest fix: add the — FIXED 2026-06-08 (0.1.6): md↔__all__ name-existence pin test |
| DOC-10 | ✅ | FEEDBACK.md §1 Process (line 25), §2 Open feedback (line 38, 'No papers filed yet' + PAPER-NN template with Status OPEN/SHIPPED/DEFERRED/WONTFIX/MOVED at line 30 and severity 🟢/🟡/🔴 at l | UPHELD. The two surfaces are cleanly separated per the DOC-10 boundary (consumer-facing flow vs internal posture). FEEDBACK is empty-but-templated (conformant) and carries the severity+lifecycle shape. No cross-contamination found |
| DOC-11 | ✅ | docs/inprogress/ holds a single, actively-maintained tracker — MGF_STANDARDS_CONFORMANCE.md (mtime Jun 8, header 'v0.1 — initial tracker'). No fully-closed/stale tracker left to rot; no | DOC-11 demands docs/inprogress/ stay a workspace, not a graveyard — archive trackers whose scope is complete. The only inprogress doc is a still-open living conformance tracker, so nothing is owed archival. Conformant. |
| DOC-12 | ✅ | FEDERATION.md and CLAUDE.md reference this project's own root docs by name (README.md, DEPS.md, CHANGELOG.md); conformance tracker head uses relative [README.md](../../README.md) + [CHA | UPHELD. Relative-link-for-in-repo, absolute-URL-for-cross-repo, hierarchical-numbered-headings, and table-for-enumerable-content conventions are all followed across the shipped docs. Could not refute. [verify-upheld] |
| DOC-13 | ✅ | FEEDBACK.md lines 7-12 route 'mgf-auth-specific → file HERE' vs 'Federation-wide → file in mgf-common/FEEDBACK.md'; lines 14-21 cite FB-01 local-per-repo numbering + DOC-13; the §2 temp | UPHELD. The repo-scoped FEEDBACK.md with correct sibling-vs-federation routing and local-per-repo PAPER-NN numbering plus the federation cross-reference row satisfies DOC-13. Could not refute. [verify-upheld] |
| DOC-14 | N/A | mgf-auth is declared a federation sibling of mgf-common (README.md:6 'A sibling of mgf-common'; runtime dep mgf-common in pyproject.toml). No MGF_ADOPTION.md exists (`find . -iname '*AD | DOC-14's ADP-04 carve-out is explicit: federation siblings do NOT maintain their own MGF_ADOPTION.md — their adoption relationship to mgf-common lives in mgf-common's federation_roadmap.md + each sibling's CHANGELOG (which mgf-aut |
| DOC-15 | ✅ | docs/inprogress/MGF_STANDARDS_CONFORMANCE.md exists (40590 bytes, real audit content not the inprogress scaffold), head line 29 'Declared conformance level: L2 (per CFM-02)'; §1 You-are | UPHELD for DOC-15's bare-existence MUST: tracker present at the canonical path with a declared L2 level and the marker shape. The missing README link + same-place level declaration is CFM-01's concern (flagged for the DESIGN/CFM a |
| DOC-16 | ✅ | DEPS.md (2306 bytes) §1 'What mgf-auth depends on' (mgf-common, pyjwt, svix-stdlib, cryptography with windows+tiers, lines 14-29) + §2 'Who depends on mgf-auth' (PlasmaMapper, location, | UPHELD. Both directions present with required content (import-site count as coupling proxy, adoption status, surfaces). Confirmed staleness: §1 window says '>=0.41,<0.42' (DEPS.md:18) while CHANGELOG/pyproject walked to <0.47, and |
| DOC-17 | ✅ | FEDERATION.md (837 bytes) line 1 '# FEDERATION — pointer'; lines 2-4 'the federation contract is not maintained here — there is one canonical copy'; line 7 links the canonical mgf-doc-s | UPHELD. Per DOC-17 a sibling ships FEDERATION.md as a pointer stub (canonical only in mgf-standard); mgf-auth does exactly this. Could not refute. [verify-upheld] |
| DOC-18 | ✅ | CHANGELOG.md is maintained per release, but the public-symbol↔PUBLIC_API.{md,json} coupling DOC-18 names cannot ripple: those files do not exist (`find . -name 'PUBLIC_API*'` empty) and | DOC-18 (the ripple rule) requires a change to a coupled surface to update its dependents in the same commit, and that mechanizable couplings be pinned. The primary coupling for a library — public symbol ↔ PUBLIC_API.{md,json} + CH — FIXED 2026-06-08 (0.1.6): DEPS.md mgf-common pin synced >=0.41,<0.42 → >=0.41,<0.47 |
| DOC-19 | ✅ | LESSON_LEARNED.md (950 bytes) at repo root; lines 1-7 header cites DOC-19 + the lesson-learned template + 'append-only, dated, newest first, supersede never rewrite'; dated seed entry ' | UPHELD. The root LESSON_LEARNED.md is present in the required append-only dated shape; DOC-19 explicitly states 'A seeded file with no real lessons yet is conformant (presence is the bar)'. Could not refute. [verify-upheld] |

### OPERABILITY

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| OP-01 | N/A | src/mgf/auth/__init__.py exposes only the public seam surface; there is no CLI entrypoint, no [project.scripts] in pyproject.toml, and no service. The package is a pure verifier-seam li | OP-01 (rich `--version`/`/version`) requires a runnable app/service. mgf-auth is a leaf library with no app to interrogate; version visibility is satisfied by package metadata (version=0.1.5) and the consumer's own CLI per the mgf |
| OP-02 | N/A | No doctor command, no [project.scripts], no app state to probe. The 7 source modules are protocols/adapters/mocks with no persistent stores, config files, or state directories of their  | OP-02 doctor probes 'the app's OWN state' (config validity, persistent stores, state dirs). A stateless seam library has none of these; doctor is shipped by mgf-common (`mgf-common doctor`) and wired by the consuming app. N/A for  |
| OP-03 | ✅ | pyproject.toml has NO [project.urls] section at all (grep for urls/Homepage/Documentation/Issues returned zero hits). The PyPI/registry metadata therefore carries no Homepage or Issues  | OP-03 applies to all shapes and is MUST: pyproject Homepage/Documentation/Issues must resolve and CI must verify. With no [project.urls] block and no CI URL lint, this is an unmet gap, not a deliberate decline. ⏳. — FIXED 2026-06-08: CI no-placeholder grep added to the lint step |
| OP-04 | ✅ | [verify-upheld] grep -nE '[0-9]+ test\|Phase\|Status\|phase' over README.md + STARTHERE.md returns exit 1 (NO hits) — no test-count claim, no phase marker, no status marker in either do | [verify-upheld] Tried to refute: looked for a stale phase marker, a wrong/stale dep pin in prose, a contradicted test count, a wrong source-file count, and a stale ADR status in README/STARTHERE. Found none — the docs deliberately |
| OP-05 | N/A | No CLI, no running process, no config-introspection command. Configuration is constructor-injected (jwt_public_key/issuer/principal_resolver per README.md:28-30; webhook_secret per :33) | OP-05 (inspect the active config of a running app) presumes a runnable app holding configuration. A seam library receives its keys/secrets as call-site constructor args; there is no app to introspect. Config introspection is the c |
| OP-06 | N/A | No CLI, no `logs` command, no log-file sink owned by the library. mgf-common (§3) ships the rotating-file sink; the `<app> logs` wrapper is explicitly consumer-side. | OP-06 (recent logs accessible without path knowledge) targets a runnable app with a log sink. A library does not own a log file; log access is wired by the consuming app on top of the mgf-common logging stack. N/A for this shape. |
| OP-07 | N/A | No bundle command, no [project.scripts], no crash-report dir, no config/log artefacts of the library's own to tar up. | OP-07 (diagnostic-tarball command) aggregates redacted config + logs + crash reports + doctor output + system info — all app-owned artefacts a seam library does not produce. The bundle is a consumer-side CLI per the mgf-common AS- |
| OP-08 | N/A | The library writes no state: no crash reports, no log rotation, no caches. The 7 modules verify JWTs/webhooks in-process and return DTOs/raise typed errors; there are no state directori | OP-08 (bounded growth of crash reports / log rotations / caches) presumes state directories the project owns. A stateless verifier seam creates none; retention discipline is consumer-side (§3 OP-08). N/A for this shape. |
| OP-09 | N/A | Service-only rule (Applies to: service). mgf-auth is a library — no environment=production config validator, no listener, no validate_production() boot path. | OP-09 production safety guards apply to deployed services that boot a listener. A library has no production-boot to guard. N/A by the rule's own 'applies to: service' scope. |
| OP-10 | N/A | Service-only rule (Applies to: service). No deployable image/manifest; the library is published as a wheel (mgf-auth 0.1.5), not deployed as a tagged artefact in a compose/k8s manifest. | OP-10 versioned-artefact + rollback applies to deployable service images. A PyPI/registry wheel is not a deployed artefact with a :latest rollback concern. N/A by the rule's 'applies to: service' scope. |
| OP-11 | N/A | Service-only rule (Applies to: service). No alerting, no Prometheus rules, no paging path — there is no deployed runtime to alert on. | OP-11 (alerts page a human + carry runbooks) applies where a real deployment + alerting story exists. A library has neither. N/A by the rule's 'applies to: service' scope. |
| OP-12 | N/A | Service-only rule (Applies to: service). No backups, no S3/R2/GCS target, no restore drill — the library holds no persistent data. | OP-12 (backup + restore drill against the real target) applies to stateful services. A stateless verifier library has nothing to back up or restore. N/A by the rule's 'applies to: service' scope. |

### DEPLOYMENT

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DEP-01 | N/A | DEPLOYMENT.md:41 — DEP-01 'Applies to: service'; Scope box DEPLOYMENT.md:20-21 places 'Federation libraries (no runtime)' out of scope. mgf-auth has no cloud resources: no deploy/, terr | DEP-01 requires every cloud resource (VM, LB, bucket, security group, DNS, cert) to be declared in IaC. mgf-auth provisions zero cloud resources — it is a library consumed by services, never itself deployed to a cloud environment. |
| DEP-02 | N/A | DEPLOYMENT.md:42 — DEP-02 'Applies to: service'. No ansible/, chef/, salt/, Dockerfile, or cloud-init in the repo (ls confirmed absent). The repo ships source modules + tests + pyprojec | DEP-02 requires VM/container state to be Configuration-as-Code. mgf-auth owns no production VM or container image — it is a library installed into a consumer's environment. There is no VM state to express as code, so the rule does |
| DEP-03 | N/A | DEPLOYMENT.md:43 — DEP-03 'Applies to: service'. mgf-auth produces a wheel (dist/), not a deployable service artefact; immutability of the wheel/version is governed by RELEASING.md (REL | DEP-03 governs the immutability of a SERVICE's deployable artefact (container digest / source-at-SHA + locked deps installed at deploy time). For a library, artefact-immutability is the release/packaging concern (PKG-07 version-im |
| DEP-04 | N/A | DEPLOYMENT.md:44 — DEP-04 'Applies to: service'; references SC-01. mgf-auth holds no production secrets: it is a verifier seam that RECEIVES keys/secrets (Clerk signing key, Svix webhoo | DEP-04 requires PRODUCTION secrets (DB creds, signing keys, TLS keys) to live encrypted at rest outside the repo (Ansible Vault / KMS). mgf-auth owns no production secrets to store; secret-handling hygiene WITHIN the code (no leak |
| DEP-05 | N/A | DEPLOYMENT.md:45 — DEP-05 'Applies to: service'. No deploy/scripts/ or deploy.sh/backup/restore/rollback scripts exist (no deploy/ directory). The library exposes verifier Protocols + C | DEP-05 requires deploy/backup/restore/rollback scripts to be environment-explicit. mgf-auth ships no such scripts because it is not deployed to environments — it has no prod/staging/dev deployment surface. The rule does not bind a |
| DEP-06 | N/A | DEPLOYMENT.md:46 — DEP-06 'Applies to: service'; references OP-10. mgf-auth has no release rollout, no LB, no symlink/blue-green/image-tag-swap mechanism, no runbook (no deploy/docs/).  | DEP-06 requires a deployed service to have a documented, drill-measured, bounded rollback path. mgf-auth performs no deploy rollout, so there is no rollout to reverse. Reversibility is a service-rollout property absent from a libr |
| DEP-07 | N/A | DEPLOYMENT.md:47 and §3 (DEPLOYMENT.md:146-217) — DEP-07 'Applies to: service'; 'Every WEB-SERVICE CONSUMER MUST organise deployment artefacts in a top-level deploy/ directory'. mgf-aut | DEP-07 mandates the deploy/ directory shape for web-service consumers. mgf-auth is a federation-sibling library, explicitly the out-of-scope shape (DEPLOYMENT.md:20-21). A library having no deploy/ directory is correct, not a gap. |
| DEP-08 | N/A | DEPLOYMENT.md:48 — DEP-08 'Applies to: service'. mgf-auth runs no production process and exports no metrics/traces/SLO; there is no deploy/docs/slo.md (no deploy/ dir). Its auth events  | DEP-08 requires a deployed production service to be observable end-to-end (metrics/logs/traces to a queryable sink, committed slo.md, paging alerts). A library has no production runtime of its own to observe and no SLO to commit — |
| DEP-09 | N/A | DEPLOYMENT.md:49 — DEP-09 'Applies to: service'. mgf-auth owns no DNS records and terminates no TLS — it has no hostname, no LB, no certificate. It verifies Clerk/Svix signatures within | DEP-09 requires DNS records in IaC/committed list and auto-renewed TLS certs. mgf-auth has no DNS or TLS surface of its own; those belong to the consuming service's edge. The rule does not bind a library. |
| DEP-10 | N/A | DEPLOYMENT.md:50 — DEP-10 'Applies to: service'. No deploy/docs/first-production-cut.md (no deploy/ dir). There is no green-field provisioning sequence because the library provisions no | DEP-10 requires a first-production-cut runbook documenting green-field provisioning (IaC applies, config playbooks). mgf-auth provisions nothing — it is installed as a dependency. There is no provisioning sequence to document for  |
| DEP-11 | N/A | DEPLOYMENT.md:51 — DEP-11 'Applies to: service'. mgf-auth has no IaC + config-management code and no prod/staging/dev environments parameterised by an env file — it is a single versione | DEP-11 requires deployability to ≥2 environments from the same IaC + config-management code. mgf-auth is not deployed to any environment; the same wheel is consumed by every consumer regardless of their environment. The multi-envi |

### PRESENTATION_LAYER

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PR-01 | ✅ | src/mgf/auth/*.py: grep for all UI frameworks (PySide6/PyQt6/qasync/textual/rich/prompt_toolkit/click/typer/fire/flask/fastapi/django/starlette/aiohttp/jinja2/etc.) returns NO matches — | The forbidden-import property is satisfied in fact (clean source), but PR-01's literal MUST has two parts: (1) the SDK must not import a UI framework — MET; (2) 'The rule MUST be enforced mechanically via import-linter (or equival — FIXED 2026-06-08: clause(b) — a 2nd import-linter contract forbids UI frameworks (PySide6/PyQt/textual/rich/tkinter), include_external_packages=true, lint-imports in CI (2 kept/0 broken) |
| PR-03 | N/A | mgf-auth is a pure SDK seam library: src/mgf/auth/ contains 7 modules (_protocols.py, clerk_session.py, clerk_webhook.py, _errors.py, _events.py, mock.py, __init__.py). There is NO cons | PR-03 binds an 'app repo with multiple UIs' and the three-layer (core/view/ui) split. PR-07 is explicit: 'A pure-SDK sibling with zero UI consumers MAY mark PR-02..PR-05 as N/A (rule activates at the first UI).' §7 'For a sibling  |
| PR-04 | N/A | No app repo and no core/view/ui layer hierarchy exists to enforce (see PR-03 evidence). The only import-linter contract present (pyproject.toml:156-165) concerns the generic-core/clerk- | PR-04 mandates import-linter contracts enforcing the core/view/ui layer hierarchy '(a) core/view must not import UI frameworks; (b) ui/<framework> imports only its framework + view/ + SDK'. That hierarchy only exists inside a mult |
| PR-05 | N/A | No ui/<framework> adapters exist (no app, no CLI, no GUI — see PR-03). There are zero sibling UI subtrees that could import one another. | PR-05 ('each ui/<framework> adapter MUST consume view/ only — never another ui/<other>/') is only meaningful when ≥2 UIs exist. §6 notes sibling-UI isolation 'is only meaningful when ≥2 UIs exist'. PR-07's pure-SDK carve-out lists |
| PR-07 | ✅ | docs/inprogress/MGF_STANDARDS_CONFORMANCE.md DOES contain a PR — PRESENTATION_LAYER section with rows for PR-01/03/04/05 (e.g. lines 351-355 and 584-588). However every row is an unaudi | PR-07 requires the tracker to 'classify PR-* rules per the audit-outcomes taxonomy (✅/❌/⚪/🚫/⏳/N/A), same discipline as DP-*/EH-*' with 'rationale per row' (§10 tracker checklist). A PR section structurally exists (so the section-p — FIXED 2026-06-08: §Z classifies every PR-* rule with per-row rationale |

### AGENTS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AG-01 | ✅ | /home/bassam/PycharmProjects/mgf-auth/AGENTS.md (36 lines); `diff AGENTS.md /home/bassam/PycharmProjects/mgf-common/AGENTS.md` exits 0 = byte-IDENTICAL; `grep -niE 'federation shape\|ca | Refute FAILED. AGENTS.md ships at repo root, is byte-identical to the federation template, carries no copy of the briefing, and is a thin pointer; project-specific notes are routed to docs/claude/CLAUDE.md exactly as AG-01 require |
| AG-02 | ✅ | AGENTS.md:7-9 ('Run `mgf-common agents` and do exactly what it prints'), line 36 ('One message for the whole federation; no per-project variant'); docs/claude/CLAUDE.md:1-6 carries the  | Refute FAILED. The single canonical briefing is what `mgf-common agents` prints; AGENTS.md points at that one source and project-specific lookups live in the CLAUDE.md auto-block. MET. [verify-upheld] |
| AG-03 | ✅ | git log of the 4 cited agent commits (2527730, f808141, 59e9855, b4e0b14) each carry `Signed-off-by: Bassam Alsanie <bassam@magogi.org>` AND `Co-Authored-By: Claude Opus 4.8 (1M context | Refute FAILED. Both mandatory signing trailers are present on the cited agent-authored commits with the canonical human signer + drafting-agent Co-Authored-By, and .gitmessage institutionalizes it. (Note: some older pre-canonical- |
| AG-04 | ✅ | FEEDBACK.md:1-90 (full PAPER-NN pipeline: §1 Process, §2 template, §4 'How consumers submit feedback'); docs/claude/CLAUDE.md:228-257 'Feedback discipline (DOC-13 + FB-01..FB-04)' auto- | AG-04 (agents MUST file reusable friction as a FEEDBACK paper) is a session-conduct rule, review-only per §9. The repo ships the binding scaffolding: a structured FEEDBACK.md with the paper template + the CLAUDE.md feedback-discip |
| AG-05 | N/A | AGENTS.md §9 mechanical-enforcement table: 'AG-05 \| Self-enforcement; runtime memory subsystem already prompts the agent to verify.' | AG-05 (verify memory claims before acting) binds an agent's in-session behaviour, not a static artifact a published library can carry. There is no repo surface to satisfy or violate it — the standard itself classes it self-enforce |
| AG-06 | N/A | AGENTS.md §9: 'AG-06 \| Runtime / harness enforcement (permission prompts on Bash, MCP tool calls). Not a code rule.' | AG-06 (risky operations require explicit confirmation) is a harness/runtime gate, explicitly 'not a code rule' per the standard. A published verifier library carries no artifact that implements or violates it. Premise does not bin |
| AG-07 | N/A | AGENTS.md §9: 'AG-07 \| Self-enforcement; runtime reminders surface stale task lists.' | AG-07 (use TaskCreate/TaskUpdate for ≥3-step work) governs an agent's in-session task tracking — there is no static repo artifact for a library to ship. Self-enforcement per the standard; premise does not bind library deliverables |
| AG-08 | N/A | AGENTS.md §9: 'AG-08 \| Self-enforcement; runtime auto-memory rules cover this.' | AG-08 (memory writes federation-scoped, not session-scoped) constrains an agent's auto-memory behaviour, not a deliverable of a published library. No checkable repo surface; self-enforcement per the standard. |
| AG-09 | N/A | AGENTS.md §9: 'AG-09 \| Review-only.' Rule body keys on 'when an agent's recommendation depends on a federation sibling's published version.' | AG-09 (verify sibling versions against the Codeberg registry, not local dist/) is a review-only session-conduct rule about how an agent verifies a version claim. It produces no artifact a library ships. The pyproject does route mg |
| AG-10 | ✅ | FEEDBACK.md:1-21 routing header (mgf-auth-specific HERE vs federation-wide → mgf-common); docs/claude/CLAUDE.md:228-257 feedback-discipline block ('File a PAPER-NN... not ad-hoc edits') | AG-10 (standards drift caught by an agent MUST be filed as a PAPER, not silently fixed) is review-only, but its binding scaffolding is the FEEDBACK paper channel — present and correctly routed (sibling-scoped vs cornerstone). The  |
| AG-11 | ✅ | /home/bassam/PycharmProjects/mgf-auth/FEEDBACK.md:6-13 — explicit 'Where to file what' routing table: 'mgf-auth-specific → file HERE'; 'Federation-wide → file in mgf-common/FEEDBACK.md' | Refute FAILED. The repo provides the artifact AG-11 requires: a FEEDBACK.md whose header encodes cross-project routing (target-project for project-specific friction, mgf-common for cross-cutting) with triage scaffolding. MET. [ver |
| AG-12 | ✅ | docs/claude/CLAUDE.md exists at /home/bassam/PycharmProjects/mgf-auth/docs/claude/CLAUDE.md (12.4 KB, `mgf-common` auto-generated managed block at lines 1-6); AGENTS.md:20-22 directs ag | Refute FAILED. The project-scoped briefing content lives in docs/claude/CLAUDE.md (the resolution target of `mgf-common agents --project`), and mgf-common is a declared dependency so the `agents --project` capability is available. |
| AG-13 | ✅ | FEEDBACK.md:6-21 'Where to file what' (mgf-auth-specific HERE; federation-wide → mgf-common) + §4 consumer submission path; docs/claude/CLAUDE.md:228-241 directing one-library-surface f | AG-13 (only the mgf-common supervisor changes a library; others file in the target library's FEEDBACK.md) binds the federation's ownership model. For a published sibling the checkable surface is that it exposes its own FEEDBACK.md |
| AG-14 | ✅ | .gitmessage:5-10 enforces the AG-03/AG-14 trailer pair on every commit; git log shows coupled surfaces moving together (b4e0b14 release 0.1.5 walks the mgf-common pin AND updates CHANGE | AG-14 (propagate a change to coupled surfaces in the same change — the agent-facing form of DOC-18) is a session-conduct rule whose checkable trace is whether the repo's history leaves dependents stale. History is consistent: rele |
| AG-15 | N/A | AGENTS.md §9: 'AG-15 \| Review-only (the charter grants license, not a checkable artifact). ... A PR that silently skips a MUST ... is the trigger for a reviewer prompt.' Standard §10:  | AG-15 grants the agent standing (push back / decline-with-rationale / 'no change needed') and obliges it to know the Agent Empowerment Charter. It is explicitly review-only and 'not a checkable artifact'; the charter file itself s |
| AG-16 | ✅ | pyproject.toml:29-31 declares `dependencies = ["mgf-common>=0.41,<0.47"]` (plain, no extra); dev extra pyproject.toml:38-49 lists plain `mgf-test-supervisor`/`ruff`/`mypy` but no `mgf-c | AG-16 is a MUST: 'Every project MUST keep the standards corpus resolvable to its agents — depend on `mgf-common[standards]`.' The corpus is a Codeberg-only optional extra; without it the `mgf-common standards`/`agents`/`catch-up`/ — FIXED 2026-06-08: separate [standards] extra (mgf-common[standards]; OUT of [dev]) |
| AG-17 | ✅ | docs/memory/MEMORY.md present (the project memory index). |
| AG-18 | ✅ | my_stuff/ git-ignored + .githooks/prepare-commit-msg present (auto-adds the Signed-off-by trailer). |
| AG-19 | N/A | no runtime auto-memory store for this project yet (~/.claude/projects/<slug>/memory/ absent) — nothing to VCS-track; binds only once such a store exists. |
| 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 | N/A | no performance budgets / perf tests. |
| PF-02 | N/A | no performance budgets / perf tests. |
| PF-03 | N/A | no performance budgets / perf tests. |
| PF-04 | N/A | no performance budgets / perf tests. |
| PF-05 | N/A | no performance budgets / perf tests. |
| PF-07 | N/A | no performance budgets / perf tests. |
| PF-08 | N/A | no performance budgets / perf tests. |
| 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). |
