# Magogi Foundation Standards — Conformance Tracker (mgf-tele-teltonika)

> **Status.** v0.1 — initial tracker. Created 2026-05-28 during a
> DOC-15 sweep across the federation siblings (mgf-tele-teltonika
> previously lacked a conformance tracker).
> **Scope.** mgf-tele-teltonika's conformance to the standards at
> [mgf-standard/docs/standards/](https://codeberg.org/magogi-admin/mgf-standard/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:** Teltonika Codec 8 / 8E / 12 decoder + a
`TelematicsProvider` implementation. The decoder is a **pure function**
(`bytes → mgf.tele.core.DecodedAvlRecord[]`); the provider owns the TCP
session lifecycle (IMEI handshake → receive → ack → close) behind the
`mgf-tele-core` two-layer seam. The first vendor package in the
`mgf-tele-*` family — it builds on `mgf-tele-core` and never the reverse.

**Current version:** 0.1.1.

**Declared conformance level: L2** (per CFM-02).
mgf-tele-teltonika pins `mgf-common>=0.41,<0.42` and
`mgf-tele-core>=0.1,<0.2`.

**Premise note.** This is a pure byte-parsing vendor codec plus a thin
session provider. It holds no application state, owns no schema, and
exposes no end-user surface. Its standards footprint is dominated by the
**SC** (hostile-byte parsing) and **TS** (binary-parser coverage)
surfaces; everything else is the standard library-sibling shape.

**Applicable standards groups (rules audited):** DP, EH, LG, SC, CF, AP, TS, DOC, REL.

**N/A standards groups (rule premise doesn't apply to this
project's shape):**
- **GUI / UI standards** — no user interface; the package is a decoder +
  socket provider with no rendered surface.
- **DB / persistence standards** — no database; it owns no schema,
  migrations, or storage. It decodes bytes into in-memory records and
  hands them to the consumer.
- **HTTP-server standards** — no HTTP server. It speaks the Teltonika
  TCP wire protocol behind the `mgf-tele-core` `TelematicsProvider` ABC;
  there is no request/response web surface.
- **OP** — no standalone runtime (library + provider consumed by a host
  ingest loop). **DEP** — library, not a deployed service.
- Logging is delegated to `mgf-common` rather than self-hosted (LG is
  still audited as *applicable*, just satisfied via the seam).

**Counts (2026-05-28 — initial tracker):**

| Priority | Count | Status |
|---|---|---|
| ✅ structurally conformant | (set on first depth audit) | (set on first depth audit) |
| 🚫 declined-with-rationale | 0 (initial tracker; populate as deferrals get filed) | — |
| ⏳ in-progress | (track from sibling's own RA/SH/PC trackers, when filed) | — |
| N/A | GUI / DB / HTTP-server / OP / DEP — see §1 above | — |

**Headline.** This is an honest first scaffold: the per-standard rows
below are marked *structurally conformant* pending the first falsifiable
depth audit. The decoder's safety posture (frame caps, malformed-packet
handling) is already partly evidenced in source — see §2.4 — but no
per-rule SH/RA/PC trackers exist yet. Drift over time is the signal.

---

## Per-standard verdicts → see §Z

> **2026-06-09 depth audit (evidence-based)** supersedes any prior per-family prose. Of 218: **75 ✅**
> (file:line, refute-survived) · **60 ⏳** · **83 N/A** · **0 declined**; applicable = 135; **56% evidenced**.
> **The best-hardened untrusted-binary parser in the federation** — Codec-8/8E decode bounds-checks every
> wire-supplied count/length before its loop, caps the frame size before allocation (DoS), validates CRC,
> surfaces malformed input as a typed parse_status (no raw struct.error/IndexError leak), and a Hypothesis
> fuzz corpus proves it. DP-02 prod side (TeltonikaProvider implements mgf-tele-core's ABC). The 56%
> reflects the missing publish chain (CI exists), a missing __version__ (AP-13), no PUBLIC_API surface,
> and the logging/typed-error build-out — NOT a weak or unsafe core.

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

| Cluster | Rules | Count |
|---|---|---|
| **CLOSEABLE (release 0.1.6)** (publish chain + SCOPE + AP-13 __version__ + PUBLIC_API + EH-10 never-raises test + sbom) | AG-16, AP-10, AP-11, AP-13, DOC-01, DOC-02, DOC-03, DOC-08, DOC-09, DOC-18, EH-10, OP-03, OP-04, PKG-04, PKG-05, PKG-09, PKG-12, PR-01, PR-07, REL-03, REL-06, REL-08, SC-18, SP-01, SP-02, SP-03, SP-04, SP-05, SP-06, TS-01, TS-06, TS-20, TS-21, WF-03, WF-04, WF-05, WF-06, WF-07, WF-08 | 39 |
| **DEFERRED** (EH-01/08 typed-error build-out, logging LG-*, OP-01, SC-17, AP-14, property/supervisor/perf TS, WF-11, REL-01) | AP-14, EH-01, EH-08, LG-11, LG-13, LG-14, LG-15, LG-16, LG-17, OP-01, REL-01, SC-17, TS-09, TS-14, TS-22, TS-23, TS-24, TS-25, TS-27, TS-28, WF-11 | 21 |


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

> **2026-06-09 depth audit** (16-family fan-out + adversarial refute on every ✅, hardened prompt + RUTHLESS untrusted-binary-Codec-8 SECURITY pass; ~31 agents + a 21-id re-run for 4 truncated families). Has CI + import-linter already. **The best-hardened binary parser in the federation** (DP-06 ✅ — bounded counts/lengths before every loop, frame-cap before alloc, CRC, typed errors, a Hypothesis fuzz corpus asserting no raw parser fault leaks). No 🚫-flood, no mislabels. **Mix: 75 ✅ · 60 ⏳ · 83 N/A · 0 🚫.** Replaces the prior scaffold.


### DESIGN_PRINCIPLES

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DP-01 | ✅ | pyproject.toml:148-158 import-linter root_package=mgf.tele.teltonika + forbidden contract (source mgf.tele.teltonika MUST NOT import 'plasmamapper'); include_external_packages=true. .wo | [verify-upheld] Refute attempt: the only contract is a single forbidden 'plasmamapper' rule — is layering really enforced? But there is no UI/core split here (it is a leaf vendor codec+provider with no GUI/CLI), and no vendored SD |
| DP-02 | ✅ | In-sandbox (.venv) introspection: `mgf.tele.core.TelematicsProvider.__abstractmethods__` = {close_session, open_session, receive_packet, send_command}; `issubclass(TeltonikaProvider, Te | [verify-upheld] Refute attempt: there is NO explicit EXPECTED_METHODS frozenset / issubclass lock-in test in tests/. But the prompt's DP-02 bar = 'provider subclasses the core ABC + a test proves it satisfies the method set'. Subc |
| DP-03 | ✅ | provider.py:115 TeltonikaSessionHandle is @dataclass(frozen=True, slots=True) extending the upstream frozen SessionHandle; its docstring (115-123) documents the immutable-reference-to-m | [verify-upheld] Refute attempt: codec.py:252/364 build a mutable `inputs: dict[int,int]` and pass it into DecodedAvlRecord — is a mutable dict crossing the boundary? It is assigned into a frozen dataclass field typed Mapping (read |
| DP-04 | ✅ | Ran `mypy` (config [tool.mypy] strict=true, packages=['mgf.tele.teltonika']) in-sandbox => 'Success: no issues found in 5 source files'. pyproject.toml:71-86 strict=true + disallow_unty | [verify-upheld] mypy --strict passes on src/ with no Any; required CI gate present. MET. |
| DP-05 | ✅ | tests/ mirrors src/: test_codec.py↔codec.py, test_crc.py↔_crc.py, test_packets.py↔_packets.py, test_provider.py↔provider.py (+ test_codec_hostile_input.py fuzz suite, test_smoke.py). No | Tests mirror src 1:1 by module name and run with no network, no real daemon, no credentials — the provider's StreamReader/StreamWriter seam is exercised purely via in-memory reader + mock writer. Mirror is flat (not tests/unit/) m |
| DP-06 | ✅ | Security designed-in: codec.py:128-181 envelope checks (length-disagreement, preamble, CRC high-bits); :201/263/405 count ceilings (_MAX_RECORDS_PER_PACKET/_MAX_IO_ENTRIES_PER_SECTION/_ | [verify-upheld] RUTHLESS refute: fed truncated/oversized/malformed packets in my head. (1) Wire AVL count: bounded at :201/:263/:405 before the for-loops — no O(2^32) iteration. (2) Payload length: :143 expected_total==len(data);  |
| DP-07 | ✅ | codec.py module docstring:1-34, decode_avl_packet:118-127 (documents the three raise conditions), decode_codec8:192, decode_codec8e:306, _build_decoded:465-469; _decode_codec8_record/_d | [verify-upheld] Rich intent+invariant docstrings across the public surface; the SH-* comments explain non-obvious WHY (allocation-amplification defence, NX-skip rationale, first-sight forensic value). MET. |
| DP-08 | ✅ | codec.py:9-33 docstring documents implemented-vs-deferred + the codec-version swap path (Codec 12/13/14/15/16 raise UnsupportedCodecError until built). provider.py:16-18 documents the U | [verify-upheld] DP-08 is SHOULD/review-only. The replaceability stories (alternate codec versions, UDP envelope, CRC table, provider swap via the ABC) are documented in the module docstrings near their abstractions. MET. |
| DP-09 | ✅ | Upstream ABC methods (open_session/receive_packet/send_command/close_session) take only session/reader/writer/cmd — no CancellationToken in the signature. Each provider method is a boun | [verify-upheld] Refute attempt: no explicit CancellationToken parameter — is this a DP-09 gap? DP-09 forbids thread-global cancellation flags and mandates cooperative cancellation for LONG-RUNNING ops. There is no thread-global fl |
| DP-10 | N/A | No fallback/backend-selection path exists in the codebase. src/mgf/tele/teltonika/ is a single TCP Codec-8/8E decoder + one concrete TeltonikaProvider (provider.py). No keyring→file cha | DP-10 binds only where a fallback path exists (e.g. keyring→encrypted file). This vendor codec library has no such alternate-backend path, so the 'active backend must be queryable/logged' premise does not bind. |
| DP-11 | ✅ | grep for 'asyncio.create_task' / 'create_task' across src/ and tests/ returns ZERO matches. provider.py has async methods (open_session L167, receive_packet L196, send_command L299, clo | DP-11 forbids unretained asyncio.create_task; there are none. All async work is awaited directly within the call frame, so no task can be GC-collected mid-flight or swallow exceptions. The rule is satisfied by absence of the defec |
| DP-12 | ✅ | codec.py:471 ts=datetime.fromtimestamp(timestamp_ms/1000, tz=UTC) (tz-aware UTC at the decode boundary, in _build_decoded whose docstring cites DP-12). provider.py:191 opened_at=datetim | [verify-upheld] Every datetime crossing a module boundary is tz-aware UTC constructed at the boundary; DTZ lint enabled to mechanically forbid naive datetimes. MET. |
| DP-13 | ✅ | provider.py:312-323 close_session is idempotent (early-return on writer.is_closing()), writer.close() then await writer.wait_closed() with ConnectionResetError/BrokenPipeError swallowed | [verify-upheld] Refute attempt: does the provider leak the writer? The one external resource it touches (the StreamWriter) is released deterministically via an idempotent close_session that tolerates double-close and vanished peer |

### ERROR_HANDLING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| EH-01 | ⏳ | All DECODE/DOMAIN errors are typed TelematicsError->AppError subclasses (codec.py raises MalformedPacketError/ChecksumError/UnsupportedCodecError; provider.py raises SessionClosedError/ | NOT in the refute set, but adjudicated fresh and it is a real gap. The three builtins are programmer-misuse guards (bad constructor arg, not-yet-implemented method, wrong handle type), and test_provider.py:378/389/405 deliberately |
| EH-02 | ✅ | [verify-upheld] Two seams. (1) Provider asyncio/stdlib seam: provider.py:242-247 IncompleteReadError->SessionClosedError; :406-411/:423-428 IncompleteReadError->MalformedHandshakeError; | Both boundaries translate third-party/stdlib exceptions to typed TelematicsError. The codec achieves it by bounds-validation rather than catch-and-translate, which the standard treats as equivalent (and the fuzz suite proves it st |
| EH-03 | ✅ | [verify-upheld] grep over src/ for the 5 catch-and-translate sites confirms every one chains: provider.py:247 `raise SessionClosedError(msg) from exc`, :411 `... from exc`, :428 `... fr | Every re-raise of a translated exception chains the original cause via `from exc`; no dropped-cause branches (contrast the libvirt gap the standard §3 cites). Upheld. |
| EH-04 | N/A | pyproject.toml has NO [project.scripts]/console_scripts; no __main__.py; no install_excepthook/set_exception_handler/threading.excepthook anywhere in src/. This is a pure decoder+provid | EH-04 binds at entry points (CLI/process/thread/asyncio loop). A library that ships no entry point and creates no loop has no top-level handler obligation — that lands on the consumer that runs the asyncio server. Per the SHAPE ca |
| EH-05 | N/A | No crash reporter, no state-dir crash JSON, no remote sink — and none expected: the package ships no process entry point (see EH-04). No import of mgf.common.observability.report_now. | EH-05 (local crash report + remote forward on unhandled exception) is an entry-point/process obligation. A pure library does not own the process; the consuming service installs the crash reporter. N/A for this shape. |
| EH-06 | N/A | No GUI: no PySide6/Qt import, no QThread/CoreWorker, no gui/ package. Pure async library. | EH-06 governs GUI worker threads. No GUI surface exists. N/A. |
| EH-07 | ✅ | [verify-upheld] grep confirms exactly ONE broad `except Exception:` in src/ — provider.py:348 inside _record_first_sight, which immediately LOG.exception(:349) and returns (logs+continu | The lone broad catch is exactly the EH-07(b) carve-out: a best-effort observer that logs+continues with the invariant documented in the docstring. Refute attempt failed — there is no swallow-and-pass, no bare `except:`. Upheld. |
| EH-08 | ⏳ | Every typed error raised here (MalformedPacketError, ChecksumError, SessionClosedError, MalformedHandshakeError, UnknownTrackerError, UnsupportedCodecError) is constructed with a MESSAG | EH-08 requires typed exceptions to carry machine-readable fields as constructor args, not just a formatted message. These domain errors are message-only. The fix is partly upstream (mgf-tele-core would add fields like ChecksumErro |
| EH-09 | N/A | No user-facing surface: no CLI output, no click.echo, no GUI dialog, no print to a human. Error messages are operator/developer-facing log + exception strings (codec/provider messages l | EH-09 binds on messages SHOWN TO USERS (CLI/GUI). A library with no user-facing rendering surface has no end-user message to append a next-step to; the consuming CLI/service owns that. Per the no-CLI/GUI shape. N/A. |
| EH-10 | ⏳ | provider.py:325-401 `_record_first_sight` is the one best-effort observer: a never-raises helper called from the critical path `open_session` (line 188) AFTER the accept byte is written | The never-raises invariant is correctly implemented and documented in code for the sole best-effort observer (_record_first_sight), satisfying the broad-catch + log + continue shape. But §7's mandatory failure-injection test is ab |
| EH-11 | N/A | grep src/ for retry\|backoff\|jitter\|sleep finds zero retry logic. The decoder is pure-functional; the provider does single readexactly/write/drain calls with no retry loop. On socket  | EH-11 (exponential backoff + jitter + finite cap) binds on code that performs retries against an external system. This package performs no retries — a failed read closes the session. No retry surface => N/A. |
| EH-12 | N/A | No write/send operation against an external system with multi-attempt cost. send_command is NotImplementedError in v1 (provider.py:310). The only writes are the handshake accept/reject  | EH-12 (idempotent external writes) binds on retryable write/send operations. The write surface here (control plane) is deliberately unimplemented in v1; the ack/handshake bytes are not multi-attempt operations. N/A for v1's read-o |
| EH-13 | N/A | No outbound call that could amplify load on a struggling upstream — no circuit breaker, and none needed. The provider is the SERVER side of the ingest socket (it accepts inbound device  | EH-13 (circuit breaker / bounded amplification) binds on external calls that, when failing, worsen an already-struggling upstream. This is a server accepting inbound connections; there is no amplifying outbound call. The relevant  |

### LOGGING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| LG-01 | ✅ | provider.py:107 `LOG: Final = logging.getLogger(__name__)` at module scope — the only logger in the package. codec.py/_crc.py/_packets.py/__init__.py are pure functions with no log site | [verify-upheld] Refute attempt: searched for any function-scope getLogger, bare root logging.* call, or flat name — none exist. The single canonical __name__-based logger is correct. UPHELD. |
| LG-02 | N/A | Pure vendor codec + provider LIBRARY: no [project.scripts], no GUI app.run, no service/daemon entry point (pyproject has no scripts/console entry; no setup_logging/basicConfig/addHandle | Per the per-kind matrix, LG-02 (single setup point + JSON-on-non-TTY) binds at an entry point. A library that owns no entry point and (correctly) installs no handlers has no LG-02 surface. The carve-out is explicit: this is a libr |
| LG-03 | N/A | OTel trace_id/span_id are attached by the standard JsonFormatter, which lives in mgf.common.observability and is installed by the consumer's entry point — not by this library. This pack | LG-03 is a formatter-side obligation ('the standard formatter adds these automatically'). The library produces records via getLogger(__name__); whatever consumer formatter handles them adds trace context. No formatter is owned her |
| LG-04 | N/A | No formatter/Redactor is installed by the package. Redaction is the consumer formatter's job (mgf.common.observability.Redactor). The structured fields this lib emits are imei + source_ | LG-04 binds the formatter ('redacted by the formatter before any sink sees them'). The library owns no sink/formatter, so it cannot satisfy or violate LG-04 directly. Note: emitting source_ip is legitimate forensic value (SH-06);  |
| LG-05 | ✅ | The only ERROR-level record in the package is provider.py:349 `LOG.exception("ingest: get_extra_info(peername) raised", extra={"imei": imei})` — `LOG.exception` sets exc_info=True, capt | The single error site uses LOG.exception inside an `except Exception` handler, which is exactly the LG-05 shape (traceback preserved, not the stringified exception). The two WARNING sites are recovered conditions, not ERROR, so LG |
| LG-06 | N/A | Library installs no sink. The aggregator (rotating file / stdout / OTLP) is wired by the consumer entry point (plasma-ingest). No 'console only' config is shipped because no config is s | LG-06 binds the deployable that owns the sink configuration. A library that emits to whatever the consumer wires cannot independently satisfy/violate the 'reaches a searchable aggregator' contract; it correctly defers. |
| LG-07 | N/A | No GUI — this is a headless codec/provider library (no PySide6, no dialogs, no gui package). | LG-07 is gui-app scoped only. Not applicable. |
| LG-08 | ✅ | All four log sites use lazy %-style (or no args): provider.py:349 (no format args), :357-361 `"ingest: peername has unexpected shape: %r", peer`, :386-393 `"ingest: imei=%s hit per-IMEI | [verify-upheld] Refute attempt: looked for any f-string or pre-formatted message passed to a log call — none. Every site passes a %-template with the args as positional parameters (deferred formatting). UPHELD. |
| LG-09 | ✅ | Structured data is passed via extra={} at every site: :349 `extra={"imei": imei}`, :361 `extra={"imei": imei}`, :392 `extra={"imei": imei, "source_ip": peer_ip}`, :400 `extra={"imei": i | [verify-upheld] Refute attempt: the %s/%r in the messages duplicate fields for human eyes, but the machine-queryable copy is consistently in extra={} — exactly the LG-09 contract, and a test pins promotion. The duplication is perm |
| LG-10 | N/A | pyproject.toml:16 keywords=["telematics","teltonika","codec8","avl","gps","fmb"]; no [project.scripts]/console_scripts (grep of pyproject.toml empty); zero systemd/journald/daemon/Journ | LG-10's 'Applies to' is exactly 'service' (per-kind matrix line 45: service = all-applicable + LG-10). The rule (line 24) binds 'Headless services (systemd timers, daemons, mobile background tasks)' that 'MUST configure logging at |
| LG-11 | ⏳ | receive_packet (provider.py:196) crosses a NETWORK boundary (readexactly off a TCP socket) and open_session (:167) crosses the handshake network boundary — both are LG-11 candidates (ne | LG-11 MUSTs a start/end record with a stable `operation` name for functions crossing a process/network/storage boundary so flows reconstruct from logs alone. The per-session receive/handshake loop has none. This is a real, precise |
| LG-12 | ✅ | The four call sites do only trivial work: extras are plain str/int (imei, source_ip, an int cap), messages are lazy %-templates, and there is no synchronous network/file I/O at the call | [verify-upheld] Refute attempt: checked each call site for hidden expense (str() of a large object, repr of a packet buffer, network call) — the only repr is %r of a small peername tuple at :357, which is negligible. Per-record ca |
| LG-13 | ⏳ | The INFO first-sight site (:396) is de-duplicated by the bounded (IMEI, IP) set, so it cannot flood — that path is fine. BUT the per-IMEI-cap WARNING (:386) fires only at the cap edge ( | The two WARNING/INFO sites are structurally bounded (set/cap), so today nothing floods. But LG-13's canonical inner-loop/per-event-callback protection is not present for the per-packet receive path; marking ⏳ because the per-event |
| LG-14 | ⏳ | Correlation context (the per-session IMEI / source_ip) is threaded through function arguments and extra={} on each call, NOT via contextvars.ContextVar. There is no ContextVar registrat | LG-14 MUSTs that custom correlation context (here: IMEI / source_ip per session) propagate via contextvars.ContextVar so the standard formatter promotes it to every record automatically — rather than being passed by argument/extra |
| LG-15 | ⏳ | Only ONE record carries a stable `event` field: the first-sight INFO at :400 `event="first_sight"`. (1) That name is not `<subject>.<action>` form (LG-15 contract: dot-delimited noun.ac | LG-15 MUSTs a stable `event` in <subject>.<action> form on notable lifecycle/security/boundary records AND a docs/reference/EVENTS.md registry. The cap-WARNING (a capacity-boundary event) is missing its event; the existing `first_ |
| LG-16 | ⏳ | WARNING at :357 ('ingest: peername has unexpected shape: %r') answers WHAT (past-ish) + WHY (peer repr) but gives NO HOW (no hint/recovery path) — a record naming a failure with no reco | LG-16 MUSTs every WARNING/ERROR answer WHAT+WHY+HOW. Two of the three WARNING/ERROR records lack the HOW (hint/remediation or explicit 'unknown') and lack the LG-15 event linkage. Genuine gap at the WARNING/ERROR surface. |
| LG-17 | ⏳ | The single ERROR record (:349 `LOG.exception`) carries no `fingerprint` field. There is no consumer-installed formatter here to auto-compute fingerprint from exc_info, and the library d | LG-17 SHOULD: ERROR records carry a stable fingerprint (default = exception type, auto by the formatter when exc_info is set). Since this library installs no formatter, the auto path isn't guaranteed and no explicit fingerprint/ca |

### CONFIGURATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| CF-01 | N/A | grep across src/ and tests/ for os.environ/getenv/BaseSettings/pydantic_settings/yaml.safe_load returns NOTHING. The 5 source modules (codec.py, _crc.py, _packets.py, provider.py, __ini | CF-01 applies to 'consumer-owned-config'. A vendor codec/provider library has no application configuration object — there is nothing to put in a pydantic-settings BaseSettings. Premise does not bind. |
| CF-02 | N/A | No layered-source loader exists anywhere; the library never reads env vars, .env, or a config file (grep empty). The only tunables are TeltonikaProvider.__init__ kwargs (provider.py:132 | CF-02 (precedence CLI>env>.env>file>defaults) presumes a multi-source config loader. This library has none; its behavior is parameterized purely by constructor arguments owned by the consumer process. No precedence surface to orde |
| CF-03 | N/A | No config schema and no on-disk config file exist (grep for load_settings/BaseAppSettings/config.yaml empty). The only version concept is the package version (mgf-tele-teltonika 0.1.5)  | CF-03 (config schema MUST carry a version field + migration chain) binds only to consumer-owned-config. There is no config schema to version or migrate. |
| CF-04 | N/A | No code path writes a config or state file to disk. provider.py only reads/writes bytes over an asyncio StreamWriter (the TCP socket); there is no temp+rename, no chmod, no path expansi | CF-04 (atomic 0o600 config-file writes + lazy path expansion) binds to consumer-owned-config. This library persists nothing to disk and resolves no filesystem paths. |
| CF-05 | N/A | There is no config file and no Vault interface (grep for Vault/keyring empty). The provider takes an optional async imei_authoriser callback (provider.py:135) injected by the consumer;  | CF-05 (secrets in a Vault, not config) is marked 'all', but its premise is a consumer that handles secrets/config. This library has neither a config file to keep secrets out of nor any secret to store; the authoriser is an injecte |
| CF-06 | N/A | No config-file loading occurs (grep for yaml.safe_load/model_validate-of-config empty). There is no schema with extra='forbid' to reject unknown keys because there is no config schema. | CF-06 (unknown-key rejection on config load) binds to consumer-owned-config. No config is ever loaded from disk/env. |
| CF-07 | N/A | No sub-config / satellite files (hooks, schedules, profiles, etc.) exist or are loaded; the library has no on-disk data files of any kind. grep for config/yaml loaders empty. | CF-07 (satellite config files follow the same typed/atomic/versioned shape) binds to consumer-owned-config. There are no satellite config files. |
| CF-08 | N/A | There is no Settings object to load once at an entry point — the library has no entry point (no CLI/GUI/service). It is import-only. Positively, provider.py:132-158 uses constructor inj | CF-08 (load Settings once, inject by constructor; no module-global singleton) binds to consumer-owned-config. No Settings object exists; the wiring obligation falls on the consumer process that constructs the provider. |
| CF-09 | N/A | No CLI exists in this package (no click/argparse, no console_scripts; src has only codec/_crc/_packets/provider/__init__). grep for '--config' empty. | CF-09 (a --config CLI flag with XDG/OS default path) binds to consumer-owned-config with a command surface. This is an import-only library with no CLI; the standard explicitly scopes CF-09 away from non-CLI consumers. |
| CF-10 | N/A | The test suite (test_codec.py, test_codec_hostile_input.py, test_crc.py, test_packets.py, test_provider.py, test_smoke.py) constructs the decoder/provider directly and feeds synthetic b | CF-10 (tests construct Settings with explicit values, no env leakage) is marked 'all' but presumes a Settings class. None exists, so there is nothing to instantiate explicitly; tests already pass explicit byte inputs and construct |
| CF-11 | N/A | No config schema with named fields exists, so there is no config field to rename via Field(alias=...)/validation_alias (grep for alias/AliasChoices empty). | CF-11 (rename config field without breaking on-disk configs via alias) binds to consumer-owned-config. There are no config fields and no on-disk configs. |
| CF-12 | N/A | No config field with mutually-exclusive backend variants exists. The provider has no backend-selection config; codec.py dispatches on the wire codec id (CODEC_8 vs CODEC_8_EXTENDED) at  | CF-12 (model backend variants as a discriminated union with a kind discriminator) is marked 'all' but binds only where the consumer has a config field choosing among backends. This library has no such config field. |

### SECURITY_STANDARD

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SC-01 | N/A | grep over src/ finds no secrets, tokens, keys, or credentials; the library is a pure binary decoder + TCP provider with no vault, no auth secrets, no config-file secret handling. CI doe | No secret material exists in scope. The vault boundary obligation does not apply to a codec/provider library; the gitleaks CI step is a good hygiene bonus but the rule's substance (secrets-in-vault) has no surface here. |
| SC-02 | N/A | `grep -rnE 'subprocess\|shell=True' src/` returns nothing; ruff S603 (subprocess) is even ignore-listed because there are no subprocess call sites. The library makes no subprocess invoc | No subprocess invocation anywhere in the package, so the argv-only rule has no applicable surface. |
| SC-03 | N/A | No yaml/json/pickle deserialization in src/ (grep finds none). The only deserialization is hand-written struct/byte parsing of the Teltonika wire format, covered under SC-08. | No YAML/JSON/pickle loading surface. The wire-byte parsing is the SC-08 concern, not the SC-03 deserialization-format concern. |
| SC-04 | N/A | The library performs no file writes — it is a pure decoder + an in-memory TCP session provider. No config.yaml, vault, crash report, or state file is written. | No private-data file-write surface exists, so the 0o600/atomic-write obligation does not apply. |
| SC-05 | N/A | _crc.py implements CRC-16/IBM, an integrity checksum for protocol framing (not a cryptographic primitive). No encryption, signing, password derivation, or token generation exists; grep  | No cryptographic primitives are used. CRC-16 is a protocol integrity check (the standard's §6.3 carve-out for integrity-not-cryptographic uses), not hand-rolled crypto. |
| SC-06 | N/A | No outbound TLS/HTTP/SSH connections. The only network surface is the inbound asyncio TCP ingest socket (the device connects to us); the library never dials out. grep finds no verify=Fa | No outbound connection surface, so the TLS-verification-on-outbound rule does not apply. (Teltonika devices speak plaintext TCP inbound; transport security is a deployment/proxy concern noted out-of-scope in SECURITY.md.) |
| SC-07 | ✅ | [verify-upheld] The only external boundary is the inbound asyncio socket, read via reader.readexactly() gated by the SH-04 frame cap at provider.py:234-239 (data_field_length > self._ma | The rule's substance — no outbound request without a finite timeout — is vacuously met (no outbound calls), and the inbound DoS surface that SC-07 cares about (unbounded read allocation) is bounded by SH-04. Upheld. |
| SC-08 | ✅ | [verify-upheld] This is the central obligation and it is met thoroughly. REFUTE ATTEMPTS, all defended in code: (1) lying data_field_length — codec.py:143-149 requires expected_total == | Every attacker-controlled count/length from the wire is validated against both the actual buffer length and an explicit ceiling, at the parser boundary, before use; adversarial + property tests prove robustness. The strongest part |
| SC-09 | N/A | The library defines no logging Formatter/Redactor; SC-09 is the formatter-level redaction obligation. The provider's only log records (provider.py:349/357/386/396) carry IMEI + source-I | No formatter/sink is owned here, so the 'redactor before the sink' obligation has no surface. The non-secret-field logging it does perform aligns with the rule's intent (IMEI/IP are not redactor-targeted secrets). |
| SC-10 | ✅ | [verify-upheld] uv.lock is committed at repo root (189922 bytes, confirmed via ls). CI runs the CVE scanner: .woodpecker.yml:61-67 'vuln-scan' step runs `pip-audit --strict` on every pu | Lockfile committed + pip-audit --strict gate on every PR. Both halves of SC-10 met. |
| SC-11 | ✅ | [verify-upheld] No secret material is ever in scope (see SC-01 N/A), so no exception can carry one. Exception messages carry only protocol/structural data: byte counts/codec ids (codec. | Traceback-secret-leak obligation is vacuously satisfied (no secrets in scope) and messages are bounded to non-sensitive structural fields, translated at the EH-02 seam. Upheld. |
| SC-12 | ✅ | [verify-upheld] SECURITY.md exists at repository root with: supported-versions table (lines 18-21), a 'Reporting a vulnerability' section with email bassam@magogi.org + Codeberg private | A correct, project-specific root SECURITY.md matching the §11.1 template is present and tailored to this library's threat surface. Upheld. |
| SC-13 | N/A | No cryptographic keys at rest, no KDF, no keyring usage. The library holds no key material of any kind. | No at-rest key storage surface exists. |
| SC-14 | N/A | The IMEI handshake is an identity assertion, not an authentication credential check: a malformed IMEI is rejected uniformly (provider.py:174-177) and registry rejection (provider.py:178 | No credential-verification auth boundary exists here. IMEI authorization (and any rate-limit/lockout) is the consumer registry's responsibility; the library merely provides the hook. The anti-enumeration obligation has no applicab |
| SC-15 | N/A | No filesystem path is ever constructed from external input — the library reads bytes off a socket and decodes them; it writes/reads no files. grep finds no Path/open/os.path usage from  | No path-from-external-input surface, so the directory-traversal obligation does not apply. |
| SC-16 | N/A | No privileged operations: no sudo/setuid/chmod/capabilities/--privileged. grep finds none in src/. The library runs entirely as an unprivileged in-process decoder + asyncio provider. | No privileged-operation surface exists. |
| SC-17 | ⏳ | The library emits ONE structured INFO record — the SH-06 first-sight (IMEI, source-IP) log (provider.py:396-401) with a stable event='first_sight' field — but it does NOT carry the `aud | The first-sight log is genuine forensic value but is not marked audit=true, and the accept/reject handshake decisions — the security-relevant actions on the unauthenticated ingest socket — lack audit records entirely. This is a re |
| SC-18 | ✅ | The package is hatchling-built from a tagged source (pyproject.toml:1-3, build-backend = hatchling.build) and deps are declared + locked (uv.lock), so it is buildable-from-source. BUT t | Reproducible-from-source is structurally true, but the SC-18 mechanical surface (SBOM artefact + recorded build metadata via the publish chain) is absent. Sibling libraries treat this as a ⏳ until the SBOM step + publish chain lan — FIXED 2026-06-09: .woodpecker sbom step (reproducible CycloneDX) |

### TESTING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| TS-01 | ⏳ | tests/ holds 6 FLAT files (test_codec.py, test_codec_hostile_input.py, test_crc.py, test_packets.py, test_provider.py, test_smoke.py) — there is NO tests/unit/ tree mirroring src/mgf/te | TS-01 MUST: tests/ mirrors src/ 1:1 inside tests/unit/, with CI linting unmirrored source files against a documented allow-list. The flat layout satisfies neither the directory mirror nor the CI lint. Content coverage exists (ever |
| TS-02 | ✅ | [verify-upheld] test_provider.py::_stub_streams (44-62) uses a real asyncio.StreamReader + a MagicMock writer with AsyncMock drain/wait_closed — no real socket, no network, no daemon, n | Unit suite runs with no production credentials, no network, no real third-party daemon; the only external I/O (asyncio writer) is mocked. MET. |
| TS-03 | ✅ | [verify-upheld] Each test constructs its own provider = TeltonikaProvider() and per-test reader/writer; asyncio_mode=auto gives function-scoped loops; the only process-global touched is | No order-dependence, no shared mutable module state, no env/cwd leakage. MET. |
| TS-04 | ✅ | [verify-upheld] pyproject.toml lines 102-106: filterwarnings = ["error", "default::mgf.common._warnings.MgfDeprecationWarning", "default::pytest.PytestUnraisableExceptionWarning"]. Base | filterwarnings escalates to errors with two documented per-category relaxations. MET. |
| TS-05 | ✅ | [verify-upheld] pyproject.toml addopts (lines 93-98) include "--strict-markers" and "--strict-config". | Both strict flags set. MET. |
| TS-06 | ⏳ | pyproject sets [tool.coverage.report] fail_under=85, but .woodpecker.yml test-3.12 runs `pytest --cov=src/mgf --cov-report=term-missing` with NO --cov-fail-under flag and no subsequent  | TS-06 MUST: the ≥80% gate fails CI. Here the gate value is configured but never enforced in any CI job (term-missing prints, it doesn't gate), and the ≥90%-changed-lines check is absent. A configured-but-unenforced gate does not m |
| TS-07 | N/A | No typed exception subclasses are defined in src/mgf/tele/teltonika/ (grep for `class *Error` in src/ finds only docstring references). All typed errors (MalformedPacketError, ChecksumE | TS-07 binds every typed exception the project DEFINES (message format, field preservation, field copying). This package defines none — they belong to mgf-tele-core. Not applicable here. |
| TS-08 | ✅ | [verify-upheld] provider.py is the EH-02 seam. test_provider.py pins: success (test_receive_packet_decodes_one_packet_and_writes_ack:219); IncompleteReadError→SessionClosedError (test_r | Success + each known third-party-error translation + the recoverable-decode catch-all are all tested. The decoder converts all unknowns to typed errors before the seam, so the seam's 'unknown vendor error' surface is the codec's t |
| TS-09 | ⏳ | The EH-10 best-effort observer is _record_first_sight (provider.py 325-401), whose `except Exception: LOG.exception(...)` (348-349) swallows a raising get_extra_info so open_session con | TS-09 MUST: every best-effort observer has a test that injects a raising observer and asserts the loop/caller continues. The peername=None case is the skip-branch, not the raise-branch; the broad-except at line 348 (the actual bes |
| TS-10 | ✅ | [verify-upheld — refuted the decoder hard] codec.py bounds-checks at every cursor advance: envelope min-len + length-disagreement (128/143), CRC-16/IBM validated + high-bits guard (155- | Fed a truncated/oversized/malformed Codec-8/8E packet mentally: every wire-supplied count and length is validated against the buffer before indexing; oversized data_field_length is refused before allocation; CRC is validated; malf |
| TS-11 | N/A | No GUI/Qt code anywhere in src/ — this is a pure codec + asyncio provider library (5 modules, no PySide6, no QThread worker). | TS-11 binds GUI workers. None exist. Not applicable. |
| TS-12 | ✅ | [verify-upheld] pyproject asyncio_mode="auto" (92), pytest-asyncio>=0.23 in dev deps (38). Every async provider coroutine (open_session, receive_packet, send_command, close_session, wri | Every coroutine on the provider surface has a test. The receive loop reads one frame and returns (no long-running cancellable op), so §7.2's cancellation SHOULD doesn't surface a missing-MUST; the 'a coroutine with no test is a de |
| TS-13 | ✅ | [verify-upheld] Round-trip property tests: test_codec.py::test_codec8_round_trip_property (:296) and test_codec8e_round_trip_property (:602) use @given over @st.composite strategies (_c | The round-tripping decode surface is covered by Hypothesis property tests — the TS-13 MUST is met. Strategies live inline via @st.composite rather than a separate _strategies.py module; that placement is a SHOULD-ish detail, not a |
| TS-14 | ⏳ | No @pytest.mark.perf test exists anywhere in tests/ (grep finds the marker registered in pyproject:117 and excluded by addopts, but no test body carries it). The decoder is a hot binary | TS-14 MUST: performance budgets are pinned by a benchmark measuring the production path, marked @pytest.mark.perf. The marker is defensively registered but there is zero perf benchmark. Gap (the decoder's throughput is unbudgeted) |
| TS-15 | N/A | No real external backend exists — the codec is pure functions and the provider's only I/O is an asyncio reader/writer pair supplied by the consumer's TCP server. There is no live third- | TS-15 binds opt-in smoke tests against real backends. There is no real backend surface for this library. Not applicable. |
| TS-16 | ✅ | [verify-upheld] The highest-risk fuzz failures pytest.fail with f-strings carrying the leaked exception type, packet length, and full hex (test_codec_hostile_input.py:336-340, :360-364) | Failure messages are self-explanatory; the fuzz failures emit full reproduction context. MET. |
| TS-17 | ✅ | [verify-upheld] No conftest.py exists (find -name conftest.py returns nothing outside .venv). Each test file defines its own local plain-function builders (_wrap_envelope, _build_codec8 | Zero cross-test-package fixture importing — the anti-pattern TS-17 forbids is absent. MET. (Note: with the flat layout there are no test packages to import across, so the rule is trivially satisfied.) |
| TS-18 | ✅ | [verify-upheld] pyproject registers 'slow' (line 120) and addopts excludes it via -m '... and not slow' (97). No individual test exceeds 1s: fuzz suites cap at max_examples=200 with per | slow registered + excluded from default; no slow test is currently mismarked. MET. |
| TS-19 | ✅ | [verify-upheld] pyproject markers (107-121) register smoke, contract, property, regression, fuzz, concurrency, conformance, use_case, integration, perf, stress, e2e, slow — each with de | Full layer + orthogonal marker set declared; no used marker is unregistered. MET. |
| TS-20 | ⏳ | tests/ has NO four-canonical-directory partition: there is no tests/unit/, tests/integration/, tests/e2e/, or tests/smoke/ directory — all 6 test files sit flat at tests/ root. The smok | TS-20 MUST: tests partitioned across the four canonical directories with each non-unit dir applying its marker. The package uses markers but not the directory partition the MUST requires. Gap (same root cause as TS-01's missing te |
| TS-21 | ⏳ | A smoke layer exists as a marker — test_smoke.py (pytestmark=smoke) verifies (a) `import mgf.tele.teltonika` and (b) a 6-name SUBSET of __all__ via `expected <= set(m.__all__)`. But it  | TS-21 MUST: the smoke layer verifies the package imports, EACH name in __all__ is reachable, and the shortest happy path of every public entry works. The current smoke checks import + a name subset only — it omits the full-__all__ |
| TS-22 | ⏳ | Markers carry tier tags in their pyproject descriptions (e.g. 'tier-0', 'tier-1'), and mgf-test-supervisor>=0.1.2 is a dev dependency (pyproject:41), but no --tier/--tier N-M/--budget r | TS-22 MUST: tiered runs are supported with a runner accepting --tier/--budget. The tier metadata exists in marker descriptions but the runner mechanism is not wired. Gap (supervisor present-but-unused). |
| TS-23 | ⏳ | .woodpecker.yml test-3.11/3.12/3.13 steps run `pytest --tb=short -q` DIRECTLY — no mgf-test-supervisor / mtest / mgf-common test wrapper, no subprocess supervisor. A pytest crash/hang/S | TS-23 MUST: every CI run survives + structures a crash/hang/signal death (the four outcomes). Plain pytest-from-CI with no crash/hang/signal capture is the explicitly-named never-compliant case. Gap. |
| TS-24 | ⏳ | No .gates/last-run/run.jsonl is produced — CI runs bare pytest with no supervisor and no --json/record emitter. No code references mgf.common.testing.TestRecord or writes per-test JSON  | TS-24 MUST: per-test records written as one JSON object per line to .gates/last-run/run.jsonl mirroring the TestRecord schema. Not produced. Gap (follows from TS-23 — supervisor unused). |
| TS-25 | ⏳ | No flaky-quarantine mechanism: no .gates/quarantine.json, no --quarantine-attempt wiring, supervisor unused in CI. This is an L2 rule; L1 projects MAY defer, and the package is an early | TS-25 (L2): auto-quarantine of flaky tests via the supervisor. Mechanism absent. ⏳ as a deferrable-but-unmet L2 gap (not a hard MUST at L1). |
| TS-26 | N/A | The package ships no compiled artifact — src/mgf/tele/teltonika/ is pure Python (codec/_crc/_packets/provider/__init__); build-system is hatchling pure-wheel; runtime deps mgf-common +  | TS-26 explicitly lets pure-Python libraries declare N/A. No compiled artifact to freshness-check. Not applicable. |
| TS-27 | ⏳ | No .gates/last-run/failures/<test-id>.txt per-failure bundles are produced — CI uses bare pytest (--tb=short) with no supervisor _write_failure_bundle. A failure surfaces as inline pyte | TS-27 MUST: every failed/hung/crashed test produces a single explorable detail bundle. Not produced. Gap (follows from TS-23 — supervisor unused). |
| TS-28 | ⏳ | No --perf / MGF_PERF=1 per-test perf attribution: no cProfile capture, no perf_span usage, no {test_id,total_ms,functions,spans} artifacts, no perf-summary.md. Supervisor unused. This i | TS-28 (L2): opt-in per-test perf attribution surfaced through the supervisor. Mechanism absent. ⏳ as a deferrable-but-unmet L2 gap. |

### API_DESIGN

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AP-01 | ✅ | __init__.py:33-41 explicit sorted __all__ (7 names); codec.py:52-63 (10 names incl. CODEC_12..16); _crc.py:26; _packets.py:29-35; provider.py:59 — all string-literal alphabetically-sort | [verify-upheld] Every package/module boundary consumers import from declares an explicit, sorted, string-literal __all__. The rule (AP-01) is the explicit enumeration itself, which is fully met. Caveat for the audit record: the sm |
| AP-02 | ✅ | decode_avl_packet (codec.py:118-127) documents return shape + the three raised types; decode_codec8 (192), decode_codec8e (306), crc16_ibm (_crc.py:30 states 0..0xFFFF range), Teltonika | [verify-upheld] Every public name carries an intent/invariant docstring with usage context from the module docstrings. MET. |
| AP-03 | ✅ | pyproject.toml:7 version="0.1.5" (MAJOR.MINOR.PATCH). CHANGELOG.md:5-13 invokes the SemVer 0.x clause + rule AP-03 and prescribes tight consumer pins; entries 0.1.0..0.1.5 show MINOR/PA | [verify-upheld] Follows SemVer with the documented 0.x window; CHANGELOG explicitly cites the 0.x break-in-MINOR clause and pins. MET. |
| AP-04 | N/A | grep for warnings.warn / DeprecationWarning / @deprecated across src/ returns nothing; CHANGELOG has no [Deprecated]/[Removed] sections. The library is at 0.1.5 and has only ever made n | AP-04 governs the deprecation cycle (announce→survive→remove + DeprecationWarning). No public name has been deprecated, so there is no deprecation obligation to meet. N/A (the standard's own §15 marks AP-04 N/A in mgf-common for t |
| AP-05 | ✅ | TeltonikaProvider.__init__ (provider.py:132-137) makes both params keyword-only via `*` with defaults (imei_authoriser=None, max_frame_bytes=_DEFAULT_MAX_FRAME_BYTES). Public decode sig | [verify-upheld] New/optional parameters are keyword-only with defaults; the public decode signatures are single-positional and stable; the provider's method set mirrors the core ABC it subclasses. MET. (Note: could not runtime-con |
| AP-06 | ✅ | decode_avl_packet -> tuple[int, list[DecodedAvlRecord]] (codec.py:117); decode_codec8/8e -> list[DecodedAvlRecord] (191,305); crc16_ibm -> int (_crc.py:29); provider methods return conc | [verify-upheld] Return types are narrowly pinned (concrete list/tuple/int — no Iterable-where-list widening). MET. |
| AP-07 | ✅ | decode_avl_packet docstring (codec.py:120-127) enumerates MalformedPacketError/ChecksumError/UnsupportedCodecError; all imported from mgf.tele.core (codec.py:42-48, provider.py:40-52) — | [verify-upheld] Could not refute. Every wire-supplied count and length is bounds-checked against both an explicit ceiling and the actual buffer before any indexing/allocation/loop; CRC is validated; a malformed/oversized/truncated |
| AP-08 | ✅ | All 5 src modules carry `from __future__ import annotations` (__init__.py:19, codec.py:36, _crc.py:24, _packets.py:19, provider.py:31). provider.py:56-57 confines runtime-unused Awaitab | [verify-upheld] Every public module uses future annotations and isolates type-only imports under TYPE_CHECKING. MET. |
| AP-09 | ✅ | src/mgf/tele/teltonika/py.typed present (verified on disk); pyproject.toml:26 'Typing :: Typed' classifier; pyproject.toml:47-48 [tool.hatch.build.targets.wheel] packages=["src/mgf"] wa | [verify-upheld] PEP 561 py.typed marker present and packaged. MET. |
| AP-10 | ✅ | No PUBLIC_API.md, docs/PUBLIC_API.md, or PUBLIC_API.json exists (ls confirmed absent). No tests/unit/test_public_api_doc.py. The public surface is enumerated only in __all__ + the modul | AP-10 MUSTs a PUBLIC_API.md listing every public name with its stability tier + one-line description, kept in sync with __all__ by a CI check. None exists. It IS a published registry library (mgf-tele-teltonika 0.1.5), so the web- — FIXED 2026-06-09: PUBLIC_API.md lists all public names + tier |
| AP-11 | ✅ | No stability tier is declared for any public name: no PUBLIC_API.md tier column, no `# tier:` markers in source. Everything is implicitly experimental per the 0.x convention but it is n | AP-11 MUSTs an explicit per-name tier (experimental/stable/deprecated). With no PUBLIC_API.md and no tier markers, tiers are undeclared. GAP — lands with the AP-10 PUBLIC_API.md work. — FIXED 2026-06-09: tier declared (experimental, 0.1.x) |
| AP-12 | ✅ | CHANGELOG.md:5 follows Keep a Changelog + cites RELEASING.md; entries 0.1.0-0.1.5 use Added/Engineering/Notes headings; every release through 0.1.5 is explicitly a non-breaking dependen | [verify-upheld] AP-12 binds the Keep-a-Changelog format + a four-piece migration recipe to breaking [Changed] entries. The CHANGELOG is in the prescribed format and no breaking change has occurred, so there is nothing to migrate.  |
| AP-13 | ✅ | grep for __version__ across the entire repo (.py + .toml) returns NOTHING. src/mgf/tele/teltonika/__init__.py:33-41 has no __version__ attribute. pyproject.toml:7 declares version="0.1. | AP-13 MUSTs a __version__ attribute on the top-level package mirroring pyproject.toml::project.version, CI-linted for sync. The attribute is entirely absent. GAP (the audit prompt explicitly flagged this as likely). For a PEP 420 — FIXED 2026-06-09: added the missing __version__='0.1.6' to __init__.py + __all__ (mirrors pyproject) + version-mirror pin test + CI tag-coherence — released 0.1.6 |
| AP-14 | ⏳ | pyproject.toml:148-158 has exactly ONE import-linter contract — a `forbidden` contract that mgf.tele.teltonika MUST NOT import `plasmamapper` (the consumer-app firewall). There is NO co | AP-14's mechanical requirement (enforcement matrix row) is an import-linter contract that tests/consumers MUST NOT import `_<name>` modules. The single present contract is the consumer-app-forbid (a PR-01-shaped firewall), not the |
| AP-15 | N/A | No backward-compatibility shim modules exist: no re-export-from-new-location modules, no warnings.warn-on-import, grep for DeprecationWarning across src/ is empty. The package has never | AP-15 governs shims that survive removal of original code (they MUST emit DeprecationWarning + log INFO once). No such shim exists, so there is no obligation to meet. N/A. |
| AP-16 | ✅ | pyproject.toml pins the cornerstone with the federation window `mgf-common>=0.41,<1.0` (floor kept, ceiling <1.0 per API_DESIGN §4.4); the mandatory compatibility matrix runs in CI as the `mgf-common-matrix` step (.woodpecker.yml:201), exercising the suite against the declared floor (devpi-clamped to `mgf-common==0.44.*`, the oldest series the index serves). | Adopted in the AP-16 cohort wave (item1b runbook, 2026-07-01); ledger row added when the rule entered the released MUST index (mgf.standard 0.1.61 reconciled the count 264→265). |

### SCOPE

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| SP-01 | ✅ | No SCOPE.md anywhere (find . -iname '*scope*' returns only .venv site-packages; docs/ has only claude/ + inprogress/). Closest IN-scope statements are scattered and informal: README.md: | SP-01 MUSTs an EXPLICIT IN-scope list that 'is the bar new modules clear'. The README 'What's in it' table enumerates the current surface but is a feature inventory, not a scope-policy IN list framed as the admission bar (no §1 IN — FIXED 2026-06-09: SCOPE.md §1 |
| SP-02 | ✅ | No OUT-of-scope feature list with the mandated 'why we don't ship X' + 'what to use instead' per-entry structure. Partial cuts exist informally: CONTRIBUTING.md:99-100 'new vendor codec | SP-02 MUSTs an explicit OUT-of-scope list where each entry carries reasoning + alternative. The 'other-vendor codecs → their own package' cut exists in prose but is not assembled into an OUT list, and natural OUT items for this li — FIXED 2026-06-09: SCOPE.md §2 |
| SP-03 | ✅ | CONTRIBUTING.md:99-102 §5 'Vendor scope': 'new vendor codecs belong in their own mgf-tele-<vendor> package built on mgf-tele-core, not here. The decoder stays pure (bytes → records); th | SP-03 MUSTs that every PR adding a module/top-level function justify against §1 IN or §3 Adjacent, with an OUT-PR rejected against the ecosystem solution. CONTRIBUTING §5 states one concrete scope rule (vendor codecs go elsewhere; — FIXED 2026-06-09: SCOPE.md §3 |
| SP-04 | ✅ | No Adjacent-scope (§3) section exists in any doc. FEEDBACK.md exists (PAPER-NN flow, §2 'No papers filed yet') and is the demand-signal channel, but nothing ties an adjacent-scope item  | SP-04 MUSTs that Adjacent-scope items require a documented consumer-demand signal before promotion to IN. Without any §3 Adjacent list (the SP-01 gap cascades), there is no place where this rule is satisfied. Smallest fix: in SCOP — FIXED 2026-06-09: SCOPE.md §4 |
| SP-05 | ✅ | No OUT-of-scope list cites an ecosystem alternative per cut. The existing cuts that come closest do name a target: CONTRIBUTING.md:99-100 + DEPS.md 'belongs in their own mgf-tele-<vendo | SP-05 MUSTs that the OUT-of-scope list cite the ecosystem alternative for each cut. Since the SP-02 OUT list does not exist as a structured artifact, the per-entry citation requirement cannot be met. The federation does have natur — FIXED 2026-06-09: SCOPE.md §5 |
| SP-06 | ✅ | No documented commitment to revisit scope on each MAJOR release. No SCOPE.md, hence no '§6 Versioning of this document' table. AP-03 / SemVer-0.x discipline is documented (SECURITY.md:5 | SP-06 MUSTs that scope policy is revisited on every MAJOR release, the only boundary where a previously-IN item may be removed (with an AP-04 deprecation cycle). With no scope-policy document to revisit and no rule stating the MAJ — FIXED 2026-06-09: SCOPE.md §6 |

### WORKFLOWS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| WF-01 | ✅ | .woodpecker.yml:13-16 `when: - event: push - event: pull_request - event: manual`. | [verify-upheld] CI runs on every push to the long-lived branch and every PR. No local-only ship path — the four gates plus scans are CI steps. The `manual` trigger is additive, not a bypass. MET. |
| WF-02 | ✅ | .woodpecker.yml:20-26 lint runs `ruff check src tests` + `lint-imports`; :29-34 typecheck `mypy`; :39-58 test-3.11/3.12/3.13 `pytest`. import-linter contract in pyproject.toml:148-159 ( | [verify-upheld] All four green gates (lint / typecheck / test / import-linter) are present as CI steps running on every push/PR; any non-zero exit fails the step. lint-imports is wired to a real forbidden-import contract. Platform |
| WF-03 | ✅ | .woodpecker.yml:5-6 header explicitly states 'No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common.' No `when: ev | It IS a published mgf-* package WITH CI, so this is a real gap (not N/A): there is no tag-driven publish workflow targeting the Codeberg registry. The six tags were cut without a CI publish chain. ⏳. — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-04 | ✅ | No publish step exists (.woodpecker.yml:5-6, steps :18-74 are only lint/typecheck/test/vuln-scan/secret-scan). No `codeberg_package_token` / `from_secret` / TWINE reference anywhere in  | Vacuously no token leaks (nothing to store), but the secret-store discipline is unrealised because the publish workflow it governs does not exist. Coupled to the WF-03 gap: when the publish chain is added, the `codeberg_package_to — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-05 | ✅ | No tag-coherence check in .woodpecker.yml. Note __version__/AP-13 risk: pyproject.toml:7 version=0.1.5 but a stale dist/mgf_tele_teltonika-0.1.4 wheel sits in the tree — exactly the dri | No CI step compares the v<X.Y.Z> tag against pyproject.toml::version and __version__ before build. Part of the missing publish chain (WF-03). ⏳. — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-06 | ✅ | No build/upload step in .woodpecker.yml. A stale `dist/mgf_tele_teltonika-0.1.4-py3-none-any.whl` + .tar.gz exist in the repo while pyproject is at 0.1.5 — a concrete instance of the dr | There is no publish run that builds a fresh wheel; any release was cut outside CI, the exact 'reuse a local dist/ wheel' footgun this rule prohibits. ⏳. — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-07 | ✅ | No pre-publish smoke step (.woodpecker.yml:18-74 has no ephemeral-venv wheel install / __version__ assertion). Header :5-6 defers the smoke chain to a future copy from mgf-common. | No step installs the freshly-built wheel into an ephemeral venv and imports the package + __version__. Part of the absent publish chain. ⏳. — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-08 | ✅ | No post-publish verify step polling `…/api/v1/packages/magogi-admin/pypi/mgf-tele-teltonika/<version>` in .woodpecker.yml. | No Codeberg REST-API verification that the uploaded artifact surfaced. Part of the absent publish chain (WF-03). ⏳. — FIXED 2026-06-09: tag-driven Codeberg publish chain added to existing CI (build→tag-coherence→smoke→publish→verify-codeberg) + scripts/verify_codeberg.py |
| WF-09 | ✅ | .woodpecker.yml images all pinned: python:3.12-slim (:21,30,47,62), python:3.11-slim (:41), python:3.13-slim (:53), zricethezav/gitleaks:v8.30.1 (:71). No `latest`/`@v1`/bare-`slim` ref | [verify-upheld] Every external dependency in the present workflow is pinned — interpreter images by minor-slim (WORKFLOWS §7.1 accepts rolling-minor for security updates) and the gitleaks plugin to exact v8.30.1. No unpinned refs. |
| WF-10 | ✅ | .woodpecker.yml:18-74 — all 7 steps (lint, typecheck, test-3.11/3.12/3.13, vuln-scan, secret-scan) declared with NO `depends_on:`. | [verify-upheld] In Woodpecker, steps without `depends_on` fan out in parallel. All independent gates have no serial dependency declared, so no needless serialisation. The per-version test jobs are explicit (not a step-level matrix |
| WF-11 | ⏳ | uv.lock is checked in (189KB), images pinned, no host-machine state. BUT every step installs via `pip install -e ".[dev]"` (.woodpecker.yml:24,33,43,50,57,66) — from pyproject.toml, NOT | The reproducibility core holds (pinned images, no implicit host state, no live-network test deps), but the strict lock-install clause is unmet: CI resolves deps fresh from pyproject each run rather than from the committed uv.lock, |
| WF-12 | ✅ | .woodpecker.yml: no `\|\| true` anywhere; each step is a fresh python:X-slim container with explicit named commands; header :8-9 'does its own pip install so failures are per-step in th | [verify-upheld] No silent-failure patterns: no `\|\| true`, no swallowed errors; every command is explicit and per-step so Woodpecker surfaces the failing command + exit code + logs. `--strict` makes the vuln scan fail loudly rath |
| WF-13 | ✅ | .woodpecker.yml line 1: `# GENERATED — do not hand-edit. Source: mgf-standard/templates/woodpecker/` (rendered by `mgf-fed`). | WF-13 MUST: generated pipeline carries the canonical GENERATED header so maintainers never patch it in place. Header present and correct; app-doctor ✓. |

### RELEASING

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| REL-01 | ✅ | git for-each-ref refs/tags: v0.1.0..v0.1.4 are objecttype=tag (annotated, Tagger: Bassam Alsanie <bassam@magogi.org>), but v0.1.5 -> objecttype=commit (LIGHTWEIGHT). The latest release  | REL-01 MUST: every release has a corresponding ANNOTATED git tag of the form v<version> on the exact build commit. Annotated tags carry author/date/message and resist GC; lightweight tags do not (RELEASING §3.1: "Annotated tags on — 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 | ✅ | [verify-upheld] git tag -l shows six distinct version strings v0.1.0..v0.1.5. CHANGELOG.md has a dedicated dated entry per version (0.1.0..0.1.5). dist/ holds only mgf_tele_teltonika-0. | REL-02 forbids reusing a version string across differing wheel contents. The per-version CHANGELOG + distinct tag sequence + bump-then-build discipline are visible; dist/ shows no content-swap of a published version. The stale 0.1 |
| REL-03 | ✅ | No PUBLIC_API.md or PUBLIC_API.json exists in the repo (ls PUBLIC_API.* returns nothing; grep for banner/master HEAD/__version__/pip-show found nothing). Also `__version__` is absent fr | REL-03 requires PUBLIC_API.md/.json to carry the banner stating they reflect master HEAD (not the published surface) and that consumers verify via __version__ in PUBLIC_API.json. The library ships neither file, and has no __versio — FIXED 2026-06-09: PUBLIC_API.md master-HEAD banner |
| REL-04 | ✅ | [verify-upheld] grep over .woodpecker.yml for twine/publish/upload/codeberg_package_token => none (the file explicitly comments "No publish flow yet"). No token file or token string com | REL-04 forbids tokens in chat/shell-history/env-vars/workflow files; the credential must live in a chmod 600 file (local) or the tag-scoped codeberg_package_token CI secret. No publish credential is present anywhere, and gitleaks  |
| REL-05 | ✅ | [verify-upheld] Six distinct tags v0.1.0..v0.1.5 with distinct dated CHANGELOG entries; no delete-and-re-upload note, no duplicated version row in CHANGELOG. dist/ shows a single 0.1.4  | REL-05 = immutability-by-policy: a published version must never be content-swapped; fixes ship as a new version. No evidence of a content swap of an already-published version exists. Refute attempt (any same name+version re-upload |
| REL-06 | ✅ | .woodpecker.yml contains NO publish step — it explicitly states "# No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf- | REL-06 requires the strictly-ordered release flow ending in upload-to-Codeberg + REST verify (PKG-04/PKG-05). The CHANGELOG + tags show the early steps (bump/test/commit/tag) were done manually, but the publish tail (upload + REST — FIXED 2026-06-09: ordered tag→build→tag-coherence→smoke→publish→verify |
| REL-07 | N/A | FEEDBACK.md is a pure template: §2 holds only the placeholder `PAPER-01` — <one-line title> with Status OPEN; the strings SHIPPED/DEFERRED/WONTFIX appear only in the §1 workflow-descrip | REL-07 obliges a release commit to flip every FEEDBACK entry it ships to SHIPPED. No release shipped/closed any FEEDBACK entry (none exist beyond the template placeholder), so there is nothing to flip — the rule has no subject her |
| REL-08 | ✅ | No pre-publish wheel-smoke step exists in CI (.woodpecker.yml has no publish/smoke chain — "No publish flow yet"). dist/ holds only the STALE 0.1.4 wheel/sdist (Jun 4), while pyproject. | REL-08 MUST: pre-release smoke installs the actually-built wheel into an ephemeral venv and imports every public top-level name. There is no smoke step in CI and no built 0.1.5 wheel in dist/, so the freshly-built wheel was not sm — FIXED 2026-06-09: smoke installs the wheel + imports before publish |
| REL-09 | N/A | Same as REL-07: FEEDBACK.md contains only the `PAPER-01` template placeholder (Status OPEN); no FEEDBACK entry was flipped to SHIPPED/DEFERRED/REJECTED in any release. CHANGELOG closes  | REL-09 obliges a consumer-facing notification paragraph for every FEEDBACK entry flipped to SHIPPED/DEFERRED/REJECTED. No status flip occurred in any release, so no notification is owed — the rule has no trigger here. N/A. |

### PACKAGE_DISTRIBUTION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PKG-01 | ✅ | pyproject.toml [[tool.uv.index]] name="codeberg-mgf" url="https://codeberg.org/api/packages/magogi-admin/pypi/simple/" explicit=true, with [tool.uv.sources] mapping mgf-common, mgf-tele | Every mgf-* dependency is pinned to the Codeberg index; third-party deps legitimately resolve from public PyPI. Conformant channel split. |
| PKG-02 | ✅ | The only registry endpoint in committed config is the canonical simple index url="https://codeberg.org/api/packages/magogi-admin/pypi/simple/" in [[tool.uv.index]]. No hand-typed varian | The one canonical endpoint present is exactly the §3 form; no non-canonical variants exist. |
| PKG-03 | ✅ | No tokens or credentials appear in any committed file. .woodpecker.yml has a secret-scan step (gitleaks v8.30.1) gating against committed secrets; uv reads index creds from UV_INDEX_* e | Token discipline satisfied for what exists; no token committed. (Absence of a wired codeberg_package_token secret is a consequence of the missing publish step, scored under PKG-09.) |
| PKG-04 | ✅ | .woodpecker.yml header comment: "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→uplo | The strictly-ordered publish ritual is neither implemented in CI nor evidenced locally for 0.1.5; stale 0.1.4 dist artifacts contradict an in-order current release. — FIXED 2026-06-09: ordered publish ritual |
| PKG-05 | ✅ | No verify step in CI (.woodpecker.yml: lint/typecheck/test-3.11/3.12/3.13/vuln-scan/secret-scan only; header explicitly says the publish+verify chain is not yet added). Stale dist/ 0.1. | Post-publish Codeberg REST verification is not implemented and there is no proof the released version was verified against the registry. — FIXED 2026-06-09: verify-codeberg REST poll |
| PKG-06 | ✅ | [verify-upheld] pyproject.toml: [[tool.uv.index]] name="codeberg-mgf" url="https://codeberg.org/api/packages/magogi-admin/pypi/simple/" explicit=true; [tool.uv.sources] maps mgf-common, | Refute attempt: searched every committed config surface for a bare --extra-index-url — none found. All three mgf-* deps (2 runtime + 1 dev-extra) mapped to the named explicit index. Exact §6 dependency-confusion-safe shape. UPHELD |
| PKG-07 | N/A | PKG-07 governs the operator-time delete+re-upload recovery / immutability policy for botched uploads — a release-session runtime action, not a repo-config obligation. CHANGELOG.md recor | Immutability/recovery is an operator-time policy, not something the package repo declares or violates in its committed state. |
| PKG-08 | N/A | PKG-08 is a registry-account-level retention/quota obligation on the magogi-admin account (keep current+previous minor; prune via DELETE; track org quota). A registry-operator concern,  | Retention against the shared account quota is the registry operator's responsibility; an individual vendor package cannot satisfy/violate it from its own tree. |
| PKG-09 | ✅ | .woodpecker.yml has NO tag-driven publish step. Header: "No publish flow yet — when a release is cut, copy the tag-coherence -> smoke -> publish -> verify-pypi chain from mgf-common." N | CI publishing must be tag-driven + build-in-CI + secret-token-authenticated; none is present. Repo has CI but no publish flow → open gap, not N/A. — FIXED 2026-06-09: tag-driven secret-token publish step |
| PKG-10 | ✅ | [verify-upheld] pyproject.toml [project] name="mgf-tele-teltonika" (hyphenated dist name); built artifacts mgf_tele_teltonika-0.1.4-py3-none-any.whl + .tar.gz (PEP 503 normalized: hyphe | Refute attempt: confirmed hyphenated dist name, dotted import path, PEP 503-normalized artifact filenames. Defers correctly to NM-01..04. (The pypi.org/project mgf-* page links are scored under PKG-12.) UPHELD. |
| PKG-11 | N/A | PKG-11 binds an AGENT doing a sibling published-version check to query Codeberg (not pypi.org/not local dist). An agent-behavior obligation, not a repo-config artifact. This vendor code | No agent version-resolution surface exists in this library to adjudicate; the obligation falls on agents/tooling, not this package's tree. |
| PKG-12 | ✅ | Residual pypi.org mgf-* surface remains: (1) SECURITY.md line 3 links the sibling as [`mgf-common`](https://pypi.org/project/mgf-common/); (2) CHANGELOG.md line 83 links [`mgf-tele-core | Two committed pypi.org/project links for mgf-* siblings + a verify-pypi CI comment + absence of the no-pypi-surface grep gate are exactly the residual surface PKG-12 forbids and enforces. Refuted as a gap. — RECONCILED ✅ 2026-06-09: no residual pypi.org surface |

### DOCUMENTATION

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DOC-01 | ✅ | Root holds README.md, LICENSE, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, FEEDBACK.md, AGENTS.md, STARTHERE.md, DEPS.md, FEDERATION.md, LESSON_LEARNED.md + docs/ — the universal baseli | The ten-doc baseline is complete, but the library-only PUBLIC_API.md/.json pair (MUST for cornerstone+sibling per §2.0.1, AP-10/DOC-08) is missing. DOC-01 says shipping fewer than the applicable set is a defect. Not fully conforma — FIXED 2026-06-09: PUBLIC_API.md + PUBLIC_API.json present |
| DOC-02 | ✅ | docs/ contains only claude/ (CLAUDE.md) and inprogress/ (MGF_STANDARDS_CONFORMANCE.md). No docs/standards/ (the §2.0.4 pointer-only README is the expected sibling shape), no docs/cutove | A federation sibling SHOULD ship at minimum docs/claude/, docs/cutover/ (empty until first break), docs/recipes/, and a pointer-only docs/standards/README.md (§2.0.3). The standards/ pointer-README — the canonical anti-drift artif — FIXED 2026-06-09: docs/{standards,cutover,recipes} buckets |
| DOC-03 | ✅ | `find docs -name README.md` returns nothing. Neither docs/claude/ nor docs/inprogress/ carries a charter README.md. | DOC-03 requires every long-lived bucket to have its own README.md stating the charter and a one-line description per file. The two existing buckets have no charter README. Gap. — FIXED 2026-06-09: bucket README charters |
| DOC-04 | ✅ | CHANGELOG.md:1-22 — Keep a Changelog v1.1.0 + SemVer header, a top [Unreleased] section (lines 20-22, 'Nothing yet'), per-release entries 0.1.0..0.1.5 with Added/Changed/Engineering/Not | [verify-upheld] CHANGELOG is in required format with an [Unreleased] holder for same-commit public-API entries. The public surface (__all__: 7 names) traces to the 0.1.0 entry; 0.1.1-0.1.5 are pin walks with no undocumented public |
| DOC-05 | N/A | No docs/cutover/ directory exists; CHANGELOG 0.1.1-0.1.5 are pin-ceiling walks ('No API change'), 0.1.1 is 'test + dev-config only; no public API or decoder-behaviour change'. No remove | DOC-05 owes a cutover guide only for a breaking change (MAJOR or 0.x MINOR that breaks a documented surface). No such break has shipped since the 0.1.0 maiden release, so no cutover guide is owed. Premise does not apply. |
| DOC-06 | N/A | The only docs/inprogress/ artifact is MGF_STANDARDS_CONFORMANCE.md — a conformance tracker governed by CFM-01..04, not a RA/SH/PC-style living priority tracker. No priority tracker (wit | DOC-06 governs the shape of living PRIORITY trackers (internal audit queues). The repo ships no such tracker; the conformance ledger is a different artifact under the CFM rules. The DOC-06 premise (a priority tracker exists) does  |
| DOC-07 | ✅ | docs/claude/CLAUDE.md present (12435 bytes): mgf-common auto-generated block, project-kind/conformance header (lines 20-24), standing-instructions topic table (lines 43-53), top-30 inli | [verify-upheld] DOC-07 MUST: dense-lookup CLAUDE.md (facts/citations/pre-approvals, not human prose). The file is dense, citation-driven, and follows the §4 mandatory shape. Present and conformant. Upheld. |
| DOC-08 | ✅ | `ls PUBLIC_API.*` → No such file. __init__.py:33 defines __all__ (7 names), but there is no PUBLIC_API.md and no PUBLIC_API.json; `grep PUBLIC_API .woodpecker.yml pyproject.toml` → no p | DOC-08 (with AP-10) requires libraries to ship PUBLIC_API.md tracking __all__ + a generated PUBLIC_API.json, with two CI pins. For this library both files and both pins are absent. Gap. — FIXED 2026-06-09: PUBLIC_API.json + generator + pin |
| DOC-09 | ✅ | README.md and CHANGELOG.md name code symbols (decode_avl_packet, decode_codec8, TeltonikaProvider, _crc.crc16_ibm, _packets) and a behavioral default ('64 KiB frame cap (SH-04)'). `grep | DOC-09 requires docs that name code be drift-detected — either a CI name-existence lint (canonically PUBLIC_API, which is absent) or a back-reference from the named code (no `See:` lines). Neither direction is wired. Behavioral cl — FIXED 2026-06-09: md↔__all__ pin |
| DOC-10 | ✅ | FEEDBACK.md §1 (lines 25-35) is the consumer-facing lifecycle OPEN→SHIPPED/DEFERRED/WONTFIX/MOVED with severity tags (the template at §2 shows 🟢/🟡/🔴); §4 is the consumer submission flow | [verify-upheld] DOC-10 MUST: FEEDBACK = consumer-facing flow; docs/inprogress/*.md = internal posture. The repo keeps consumer signal in FEEDBACK.md (with the lifecycle/severities) and internal hygiene in the inprogress tracker; n |
| DOC-11 | ✅ | docs/inprogress/ holds only MGF_STANDARDS_CONFORMANCE.md, whose header (lines 3-5) declares it an active v0.1 tracker. No fully-closed-and-abandoned tracker sits in the bucket; no docs/ | [verify-upheld] DOC-11 MUST-not: inprogress must not become a graveyard. The sole tracker is active/current, not a closed tracker left unmoved for a release cycle. No violation. Upheld. |
| DOC-12 | ✅ | MGF_STANDARDS_CONFORMANCE.md uses relative links (../../README.md, ../../CHANGELOG.md) + hierarchical §-headings (## §1 You are here). README uses a relative link (../mgf-tele-core). CL | [verify-upheld] DOC-12 MUST: agent-navigable docs — relative cross-references, src/...py path form, hierarchical headings. The internal docs satisfy this; external https links are used only for off-repo federation targets (accepta |
| DOC-13 | ✅ | FEEDBACK.md:7-21 — explicit 'Where to file what': teltonika-specific → file HERE; federation-wide → mgf-common/FEEDBACK.md (linked). §5 cross-references both. Numbering convention (line | [verify-upheld] DOC-13 MUST: FEEDBACK is repo-scoped with sibling-vs-federation routing and the local-per-repo PAPER-NN convention. Explicitly documented. Upheld. |
| DOC-14 | N/A | MGF_STANDARDS_CONFORMANCE.md §1 declares this a federation vendor package ('the first vendor package in the mgf-tele-* family … builds on mgf-tele-core'); DEPS.md §2 confirms it is a le | DOC-14 (MGF_ADOPTION.md) applies to consumer APPLICATIONS only; ADP-04 carves out federation siblings, whose adoption story is the federation_roadmap + their own CHANGELOG. mgf-tele-teltonika is a sibling/vendor library, so the ad |
| DOC-15 | ✅ | docs/inprogress/MGF_STANDARDS_CONFORMANCE.md exists (41996 bytes), declares conformance level L2 (line 27, 'Declared conformance level: L2 (per CFM-02)') and uses ✅/🚫/⏳/N/A markers per  | DOC-15 MUST: every project that adopts the standards maintains the conformance tracker at docs/inprogress/MGF_STANDARDS_CONFORMANCE.md with a declared level. The file is present, active, and declares L2. The DOC-15 presence-and-ma |
| DOC-16 | ✅ | DEPS.md present at root: §1 (lines 14-24) lists mgf-tele-core (>=0.1,<0.2) + mgf-common runtime deps with why/tier; §2 (lines 28-42) lists PlasmaMapper (consumer #1, 23 import sites, su | [verify-upheld] DOC-16 MUST: bidirectional dependency manifest with §1 (depends-on) + §2 (depended-on-by) + import-site coupling proxy + survey method. All present. Upheld. |
| DOC-17 | ✅ | FEDERATION.md:1-18 is a pointer stub — 'The federation contract is not maintained here … Read the contract at mgf-common/FEDERATION.md', cites DOC-17 (line 11), and routes project-speci | [verify-upheld] DOC-17 MUST: every non-mgf-standard project ships FEDERATION.md as a pointer stub, never a copy. This is exactly a pointer stub. Upheld. |
| DOC-18 | ✅ | The mechanizable public-symbol↔PUBLIC_API.{md,json}↔CHANGELOG ripple is not pinned: no PUBLIC_API files exist and `grep PUBLIC_API .woodpecker.yml pyproject.toml` finds no pin. CI (.woo | DOC-18 requires mechanizable couplings to be pinned. The canonical mechanizable coupling for this library (public symbol ↔ PUBLIC_API.{md,json} ↔ CHANGELOG, per DOC-04/08) has no pinned test/check because the PUBLIC_API artifacts — FIXED 2026-06-09: DEPS.md re-verified |
| DOC-19 | ✅ | LESSON_LEARNED.md present at root (960 bytes): append-only header (lines 3-6: newest-first, supersede-not-rewrite, cites DOC-19 + template) and a seed entry dated 2026-06-07 from the DO | [verify-upheld] DOC-19 MUST (since v2.21): every project keeps a root LESSON_LEARNED.md; a seeded file with no real lessons is conformant (presence is the bar). File exists with correct append-only discipline framing. Upheld. |

### OPERABILITY

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| OP-01 | ⏳ | src/mgf/tele/teltonika/__init__.py has __all__ but NO __version__; grep '__version__' over src/ returns nothing; pyproject.toml version=0.1.5 (line 7) is the only version source. | OP-01 applies to all shapes — 'which revision is running?' must have a one-command answer. A pure library's equivalent is exposing a programmatic version (a module __version__ mirroring importlib.metadata.version), which a consume |
| OP-02 | N/A | No console_scripts/entry-points in pyproject.toml (grep 'scripts\|console\|entry' = NONE); src is 5 pure modules (codec/_crc/_packets/provider/__init__) with no command surface; OPERABI | doctor probes 'the app's OWN state' (config validity, persistent stores, lock files). A pure in-process decoder+provider library has no app state, no config store, no persistent files of its own — the consuming application owns th |
| OP-03 | ✅ | pyproject.toml has NO [project.urls] block at all (grep 'urls' = 'NO urls block'); Homepage/Documentation/Issues are entirely absent. The .woodpecker.yml runs lint/typecheck/test/vuln/s | OP-03 (applies to all) requires pyproject's Homepage/Issues URLs to point at the real repo so PyPI/registry metadata routes bug reports somewhere. With NO [project.urls], the published package's 'report issue' link is missing — is — FIXED 2026-06-09: CI no-placeholder grep |
| OP-04 | ⏳ | Two headline-fact mismatches: (1) STARTHERE.md:9 claims decoder is 'Codec 8 / 8 Extended / 12' but codec.py:173-178 raises UnsupportedCodecError for CODEC_12 and README.md:39 correctly  | OP-04 (applies to all): front-matter facts must match reality because a newcomer/AI builds their 60-second mental model from them. STARTHERE claiming Codec 12 support that the code refuses, plus pointing at a missing PUBLIC_API.md |
| OP-05 | N/A | No CLI/console-scripts and no MgfSettings/config object in src (the library takes runtime args — e.g. TeltonikaProvider(imei_authoriser=...) in provider.py — not a persisted config). OP | OP-05 (config introspectable from a running app) presupposes an app with an active configuration to inspect. A pure decoder/provider library has no config surface of its own; its parameters are passed in by the consumer, whose app |
| OP-06 | N/A | provider.py:107 uses logging.getLogger(__name__) — a stdlib logger with NO handler/FileHandler/RotatingFileHandler attached (grep for FileHandler/RotatingFile = none); the library write | OP-06 (recent logs accessible without path knowledge) targets the project that OWNS a rotating log file (a CLI 'logs' command / GUI viewer / service aggregator). This library emits records into a logger that the consuming app sink |
| OP-07 | N/A | No bundle/diagnostic command (no console_scripts); no crash-report dir, no redacted-config snapshot, no doctor output to bundle (the library has none of these). Source writes only to th | OP-07 (diagnostic-tarball command bundling redacted config + log tail + crash reports + doctor output + sysinfo) presupposes the app artefacts (config, logs, crashes, doctor) that this pure library does not own. The bundle command |
| OP-08 | N/A | No state directories, crash-report writers, log rotation, or caches in src (no open()/Path()/mkdir for state; logging is unhandled getLogger; the only bound is the 64 KiB in-memory fram | OP-08 (bounded growth of crash reports / log rotations / caches) targets projects that write growing on-disk state. This library writes no persistent state to disk — it decodes bytes in-process and writes only to the network socke |
| OP-09 | N/A | Rule table 'applies to' column = service. No service/listener, no environment=production config validator, no validate_production() in src. | OP-09 is explicitly service-only (production safety guards in a config validator before a listener opens). This is a library with no production-environment config and no listener of its own; any TCP server is owned by the consumer |
| OP-10 | N/A | Rule 'applies to' = service. The package ships a wheel (dist/) published via the federation registry, not a deployable container image; no compose/k8s manifests in the repo. .woodpecker | OP-10 is service-only — it forbids 'image: <project>:latest' in production manifests and requires a stable-tagged deployable artefact + rollback runbook. A pure Python library has no deployable image/manifest; its version-tagging  |
| OP-11 | N/A | Rule 'applies to' = service. No Prometheus rules, alertmanager, or docs/runbooks/ in the repo. | OP-11 is service-only (every alert pages a human + links a runbook). A library has no alerting surface or on-call path; alerting belongs to the deployed consumer service. Premise does not bind. |
| OP-12 | N/A | Rule 'applies to' = service. No backup scripts, no S3/R2/GCS target, no docs/drills/ in the repo. | OP-12 is service-only (backup + restore drill against the real production target). A pure decoder/provider library persists no data and has no backup target to restore. Premise does not bind. |

### DEPLOYMENT

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| DEP-01 | N/A | DEPLOYMENT.md:7-24 (Scope) + Rules-box 'Applies to: service'; repo is a wheel package (pyproject.toml:name="mgf-tele-teltonika", README.md:1-19 'pip install mgf-tele-teltonika'); no dep | Infrastructure-as-Code binds web-service consumer apps that provision cloud resources. A vendor codec+provider LIBRARY consumed in-process owns no cloud infra (VMs, LBs, security groups). DNS/IaC is the downstream consumer's conce |
| DEP-02 | N/A | DEPLOYMENT.md:20-21 (out of scope: federation libraries) + Rule DEP-02 'Applies to: service'. No production VM/container owned by this package; src/mgf/tele/teltonika/ is 5 importable m | Configuration-as-Code (Ansible/cloud-init) binds VM/container state for a running service. This library has no VM or container to configure; it is installed into a consumer's environment. N/A. |
| DEP-03 | N/A | DEPLOYMENT.md Rule DEP-03 'Applies to: service'; §9 ties DEP-03 to PKG-07 for the DEPLOY-time install of a consumer app. This repo's own release immutability is governed by RELEASING/PA | Immutable release artefacts here means a deployed service installs only from a lock with no index lookup. This library does not deploy itself to production; its consumers do. The library's release/publish discipline is REL-*/PKG-* |
| DEP-04 | N/A | DEPLOYMENT.md Rule DEP-04 'Applies to: service'; §9 maps to SC-01. No production secrets concept in a pure codec/provider library (provider.py takes an imei_authoriser callable; no comm | Production-secrets-at-rest binds a deployed service holding DB creds/signing keys. A library holds no production secrets; secret management belongs to the consuming application's deploy. N/A. |
| DEP-05 | N/A | DEPLOYMENT.md Rule DEP-05 'Applies to: service'; canonical deploy/scripts/deploy.sh shape (§3:185-191). No deploy/ dir and no operator deploy scripts in this repo. | Environment-explicit deploy/backup/restore scripts bind a deployed service. A library has no deploy.sh; the deploy commands live in the downstream consumer's deploy/scripts/. N/A. |
| DEP-06 | N/A | DEPLOYMENT.md Rule DEP-06 'Applies to: service'; rollback shapes (blue/green, symlink-swap) §4.6 all presuppose a running service. No service to roll back here; CHANGELOG.md governs the | Reversible deploys with a drill-measured rollback runbook bind a deployed service. A library is rolled back simply by pinning a prior wheel version (a consumer/RELEASING concern). N/A. |
| DEP-07 | N/A | DEPLOYMENT.md Rule DEP-07 'Applies to: service' + §3:146-217 (deploy/ shape for 'every web-service consumer'). ls: 'deploy' No such file or directory — correctly absent for a library. | The standard deploy/ directory shape binds web-service consumers. A federation library has no deploy/ tree; its root is library hygiene (README/SECURITY/CHANGELOG/pyproject/.woodpecker.yml), which is present. N/A. |
| DEP-08 | N/A | DEPLOYMENT.md Rule DEP-08 'Applies to: service'; requires committed deploy/docs/slo.md + burn-rate alerts + paging channel. No such files; this is a synchronous in-process decoder/provi | End-to-end observability (metrics/logs/traces to a queryable sink, SLO file, paging alerts) binds a running service. A library emits no production telemetry of its own; observability is wired by the consumer. N/A. |
| DEP-09 | N/A | DEPLOYMENT.md Rule DEP-09 'Applies to: service' + §4.9 (DNS records in IaC; auto-renewed TLS). A codec/provider library has no DNS records or TLS certificates; the TCP listener/socket i | DNS + TLS as managed infrastructure binds a deployed service with a public endpoint. This library parses bytes handed to it; it terminates no TLS and owns no DNS. N/A. |
| DEP-10 | N/A | DEPLOYMENT.md Rule DEP-10 'Applies to: service' + §4.10 (deploy/docs/first-production-cut.md green-field provisioning sequence). No deploy/docs/ in this repo; no green-field infra to pr | A first-production-cut runbook documents provisioning a service's infra end-to-end. A library has no infra to cut over to production; the runbook belongs to the consumer app. N/A. |
| DEP-11 | N/A | DEPLOYMENT.md Rule DEP-11 'Applies to: service' + §4.11 (deployable to prod + staging from same IaC+config code via env file). No IaC/environments tree; the library is installed identic | Multiple-environments-from-the-same-code binds a deployed service parameterised by env files. A library has no per-environment deployment; environment parameterisation is the consumer's deploy concern. N/A. |

### PRESENTATION_LAYER

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| PR-01 | ✅ | src/mgf/tele/teltonika/*.py imports only: struct, datetime, typing, asyncio, logging, collections, dataclasses, mgf.common, mgf.tele.core, and internal _crc/codec modules (grep of all i | The no-UI-import outcome is met, but PR-01 explicitly requires the UI-framework prohibition to be MECHANICALLY enforced via an import-linter forbidden contract, and that contract is absent (only a consumer-app forbid exists). Smal — FIXED 2026-06-09: import-linter UI-forbid contract (2 kept/0 broken) |
| PR-03 | N/A | This is a pure vendor codec+provider SDK (src/mgf/tele/teltonika/: codec, _crc, _packets, provider, __init__ — 5 modules; no GUI/CLI/web). There is no <app> repo and zero UI consumers i | PR-03 (app-repo three-layer core/view/ui separation) binds 'An app repo with multiple UIs'. PR-07 explicitly permits a pure-SDK sibling with zero UI consumers to mark PR-02..PR-05 as N/A — the rule activates at the first UI. No ap |
| PR-04 | N/A | No app repo and no ui/<framework>/ layers exist (src/ is a flat vendor codec package). The only consumer-facing surface is decoders + a TelematicsProvider. The repo's sole import-linter | PR-04 (mechanical import-linter enforcement of the core/view/ui layer hierarchy) presupposes the PR-03 app layout. With no app repo and no UI layers, there is no layer hierarchy to enforce. N/A per PR-07's zero-UI-consumer allowan |
| PR-05 | N/A | No ui/<framework>/ adapters exist anywhere; this package ships no UI at all (pure codec + asyncio provider). There are no sibling UIs that could import one another. | PR-05 (ui/<framework>/ adapters consume view/ only, never a peer UI) is only meaningful when >=2 UIs exist. With zero UIs, the rule has no subject. N/A per PR-07. |
| PR-07 | ⏳ | docs/inprogress/MGF_STANDARDS_CONFORMANCE.md DOES contain a PR — PRESENTATION_LAYER section (rows for PR-01/03/04/05/07 at lines 386-390 and a duplicate scaffold block at 619-623), so a | PR-07 requires the tracker classify each PR-* rule per the ✅/❌/⚪/🚫/⏳/N/A taxonomy with rationale per row. A scaffold of identical 'pending audit' ⏳ rows (duplicated) is a placeholder, not a classification. Smallest fix: replace th |

### AGENTS

| Rule | Status | Evidence | Notes |
|---|---|---|---|
| AG-01 | ✅ | /home/bassam/PycharmProjects/mgf-tele-teltonika/AGENTS.md:1-37 — thin pointer ('Run `mgf-common agents`'), explicitly states 'do not edit it; project-specific notes belong in CLAUDE.md' | Repo ships AGENTS.md at root as a federation-identical thin pointer; project-specific notes correctly directed to docs/claude/CLAUDE.md. MET. |
| AG-02 | ✅ | AGENTS.md:9 points at single source 'mgf-common agents'; project lookup lives in docs/claude/CLAUDE.md (present, 12435 bytes) with auto-managed block (CLAUDE.md:3 'BEGIN mgf-common auto | Canonical briefing is delegated to `mgf-common agents` (the single federation source); no per-repo briefing copy. Project-specific content correctly in CLAUDE.md auto-block. MET. |
| AG-03 | ✅ | git config user.email=bassam@magogi.org, user.name='Bassam Alsanie'; commit.template=.gitmessage. .gitmessage:9-10 carries both trailers (Signed-off-by: Bassam Alsanie <bassam@magogi.or | Canonical identity, .gitmessage template, and actual recent commit history all carry both mandatory signing trailers. MET. |
| AG-04 | ✅ | FEEDBACK.md present (/home/bassam/PycharmProjects/mgf-tele-teltonika/FEEDBACK.md:1-91) with §1 Process ('File freely … MUST file a PAPER-NN'), PAPER template, and `mgf-common feedback`  | The repo provides the FEEDBACK paper channel and documents the file-a-paper discipline; the per-session friction-filing behavior is agent-runtime, but the standing artifact (FEEDBACK.md + discipline block) is in place. MET (artifa |
| AG-05 | N/A | AG-05 ('verify memory claims before recommending') is a runtime/agent-behavior rule; AGENTS.md §9 marks it 'Self-enforcement; runtime memory subsystem already prompts'. No checkable rep | Premise binds agent session behavior, not a static artifact a vendor codec repo can carry; nothing in the repo to adjudicate. N/A. |
| AG-06 | N/A | AG-06 (risky-action confirmation) is harness/runtime-enforced per AGENTS.md §9 ('Runtime / harness enforcement … Not a code rule'). No repo artifact governs it. | Confirmation-on-risky-ops is a harness permission concern, not a static deliverable of a vendor codec library. N/A. |
| AG-07 | N/A | AG-07 (TaskCreate/TaskUpdate for ≥3-step work) is self-enforced runtime behavior per AGENTS.md §9. No repo artifact. | Task-tracking is per-session agent behavior with no static repo deliverable. N/A. |
| AG-08 | N/A | AG-08 (federation-scoped vs session-scoped memory) is runtime auto-memory discipline per AGENTS.md §9 ('runtime auto-memory rules cover this'). No repo artifact. | Memory-write discipline is an agent-runtime concern, not a vendor-codec-repo artifact. N/A. |
| AG-09 | N/A | AG-09 (verify sibling version against Codeberg registry, not local dist/) is review-only agent behavior per AGENTS.md §9. The repo pins mgf-common>=0.41,<0.47 and mgf-tele-core>=0.1,<0. | Premise binds the agent's version-verification act at pin time, not a static repo deliverable. N/A. |
| AG-10 | N/A | AG-10 (standards drift → file a PAPER, not silent edit) is review-only agent behavior per AGENTS.md §9. The FEEDBACK channel exists (FEEDBACK.md) but the behavior is per-session. | Drift-handling discipline is agent session behavior; the file channel that enables it is covered under AG-04. N/A here. |
| AG-11 | ✅ | FEEDBACK.md:5-17 documents cross-project routing exactly per AG-11: 'mgf-tele-teltonika-specific → file HERE; Federation-wide → file in mgf-common/FEEDBACK.md' with a live link to mgf-c | The repo's FEEDBACK.md encodes the target-project routing rule and the required paper detail shape. The standing artifact is correct. MET. |
| AG-12 | ✅ | mgf-common is a hard dependency (pyproject.toml:30); `import mgf.common` succeeds and .venv/bin/mgf-common is installed, so `mgf-common agents --project` resolves docs/claude/CLAUDE.md  | Project-scoped briefing is exposed via the installed mgf-common CLI reading the present docs/claude/CLAUDE.md. MET. |
| AG-13 | N/A | AG-13 ('only mgf-common supervisor may change a library; others file FEEDBACK') is a federation governance/behavior rule. The FEEDBACK routing artifact exists (FEEDBACK.md:5-12) but the | Governance of who-may-edit-what is a federation process behavior; the enabling FEEDBACK channel is covered under AG-04/AG-11. No additional static artifact in a vendor codec repo binds AG-13. N/A. |
| AG-14 | N/A | AG-14 (propagate a change to coupled surfaces / walk the DOC-18 matrix in the same change) is per-change agent behavior, enforced mechanically by `mgf-common doctor --standards`. No sta | Same-change propagation is a session-behavior rule; nothing static in the repo to adjudicate as MET/gap for the AGENTS auditor. N/A. |
| AG-15 | N/A | AG-15 (Agent Empowerment Charter / six licenses) is review-only per AGENTS.md §9 ('the charter grants license, not a checkable artifact'). docs/claude/CLAUDE.md:288 references decline-w | Empowerment standing is an agent-behavior license with no checkable artifact in a vendor codec repo. N/A. |
| AG-16 | ✅ | pyproject.toml dependencies (line 30) declare plain `mgf-common>=0.41,<0.47`; `grep -n standards pyproject.toml` returns NO match — the [dev] optional-dependencies (lines 33-43) list mg | AG-16 MUST: depend on `mgf-common[standards]` so the standards corpus CLI resolves for agents. This repo depends only on plain mgf-common, so `mgf-common standards`/`agents` federation map/`design` degrade loudly here. Smallest fi — FIXED 2026-06-09: separate [standards] extra |
| AG-17 | ✅ | docs/memory/MEMORY.md present at repo root (app-doctor ✓). | AG-17 MUST: docs/memory/MEMORY.md tracks durable session memory for the project. File confirmed present. |
| AG-18 | ✅ | my_stuff/ listed in .gitignore; .githooks/prepare-commit-msg hook present (app-doctor ✓ both checks). | AG-18 MUST: my_stuff/ git-ignored + prepare-commit-msg hook installed. Both halves verified. |
| AG-19 | N/A | ~/.claude/projects/<slug>/memory/ absent — no runtime auto-memory store exists for this project (app-doctor: "nothing to track"). | AG-19 binds only when a runtime auto-memory store is present. None exists here; premise does not apply. |
| 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). |


---

## AG-20 — owned-infrastructure discipline (standards v2.26)

✅ **Accounted 2026-06-29.** Review-enforced agent conduct (like AG-04 / AG-05 /
AG-15 — no single checkable artifact). Before declaring an action **blocked /
human-gated / impossible**, an agent consults
[`MGF_INFRA.md`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/MGF_INFRA.md)
(the fleet infrastructure / access registry) and attempts the self-owned recovery
path — an auth/access failure on Magogi-owned infra (expired token, 401, stopped
service) is a solvable sub-problem, not a terminal wall. Recorded "blocked" /
"gated" notes carry an unblock-path + as-of date and are re-derived against current
capabilities. Added with the v2.26 corpus bump (AG-20 + the MGF_INFRA registry).
