# FEEDBACK — mgf-standard

> **Repo-scoped feedback queue** (rules FB-01..FB-04 / DOC-13). This is the
> channel for friction with **the standards, the federation contract, the
> library registry, the federation-process recipes, or the
> `mgf.standard` package** — everything this repo owns.

## Routing — file here when…

| Friction is about… | File at… |
|---|---|
| A standards **rule** (AP/CF/EH/LG/SC/TS/DP/DOC/…): wrong, missing, ambiguous, or a new rule is needed | **here** |
| The **federation contract** (`FEDERATION.md`): a duty is unclear / wrong / missing | **here** |
| The **library registry** (`MGF_LIBRARIES.md`) / its generator / the REL-13 gate | **here** |
| A **federation-process recipe** (cohort release, sibling rename, sync prompt, a template) | **here** |
| The `mgf.standard` package (resolver API, packaging) | **here** |
| `mgf-common` **library code or CLI** behaviour | [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md) |
| A specific **sibling**'s API/behaviour | that sibling's `FEEDBACK.md` |

> **Routing migration note.** Historically, cross-cutting / standards-level
> papers went to `mgf-common/FEEDBACK.md`. With the standards extracted to
> this repo, standards / contract / registry papers now belong **here**.
> `FEDERATION.md §7` and the `mgf-fed agents` briefing are updated to
> match.

## Entry template

```
### PAPER-NN — <title>
- **Severity:** 🔴 Blocker | 🟡 Sharp edge | 🟢 Nice-to-have | 💭 Idea
- **Filed by:** <project / who> on <YYYY-MM-DD>
- **What hurts:** <the concrete friction>
- **Why it matters:** <impact / who it blocks>
- **Evidence:** <rule ID, file:line, commit SHA, version hit>
- **Suggested shape:** <what a fix might look like>
- **Status:** OPEN
```

`PAPER-NN` is a monotonic ID, per this repo.

---

## Open

### PAPER-118 — GUI-01 carve-out for shared Qt *brand-kit* libraries (`mgf-brand-qt`)

- **Severity:** 🟡 Sharp edge
- **Filed by:** vm-vmanager-app (desktop-UI redesign C10) on 2026-07-10
- **What hurts:** GUI-01 forbids ANY `mgf.*` library from importing PySide6 at
  module level. That is exactly right for domain libraries — but it also
  forbids the Qt analog of `mgf-brand-web`: a presentation-layer *kit* library
  (tokens, QSS builder, fonts, icons, brand widgets) that desktop consumers
  share. vm-vmanager-app has now built that layer app-local and kit-ready
  (`vmanager.gui.design`: a `design-system-is-kit-ready` import-linter
  contract forbids `vmanager.*`/`mgf.*` imports; the token/QSS core is
  additionally Qt-free), but extraction to `mgf-brand-qt` is blocked on the
  rule as written.
- **Why it matters:** three PySide6 consumers exist or are planned
  (vm-vmanager-app, hrb-app, figrecovery Phase 7 — whose REQUIREMENTS.md
  literally says "styled after VManager"). Without a sanctioned kit, each
  copies the design system — the exact drift `mgf-brand-web` was created to
  stop on the web.
- **Evidence:** GUI_CONSUMERS.md GUI-01; vm-vmanager-app `.importlinter`
  contract 5; `src/vmanager/gui/design/` (Qt-free `tokens.py`/`_mix.py`/
  `qss.py`; Qt only in `fonts/icons/theme/widgets`).
- **Suggested shape:** GUI-01 distinguishes *domain* libraries (Qt forbidden)
  from *UI-kit* libraries (Qt allowed, consumers import them only from their
  presentation layer — same discipline `mgf-brand-web` follows for CSS).
- **Status:** RESOLVED 2026-07-10 — founder-enacted: GUI-01 amended with the
  UI-kit carve-out (same commit); `mgf-brand-qt` extraction proceeds.

### PAPER-119 — Desktop visual-design guidance gap (a DESKTOP_BRAND recipe)

- **Severity:** 💭 Idea
- **Filed by:** vm-vmanager-app (desktop-UI redesign C10) on 2026-07-10
- **What hurts:** GUI_CONSUMERS.md §Non-scope declares visual design
  per-project, and no federation surface says what a Magogi *desktop* app
  should look like — the web has `mgf-brand-web`; desktop had nothing.
- **Why it matters:** vm-vmanager-app just derived the full translation
  (tokens→QPalette+QSS, chrome-vs-readout split, one-gold-per-view in Qt,
  self-theming widgets, reduced-motion via settings). The next adopter should
  read it, not re-derive it.
- **Evidence:** `vm-vmanager-app/docs/archive/2026/desktop-ui-redesign/`
  (docs 00–04 + 11 chunk records + verification/state-coverage.md).
- **Suggested shape:** a `docs/recipes/desktop-brand.md` pointing at the
  vm-vmanager-app design system as the reference implementation, graduating
  to a standard when `mgf-brand-qt` exists.
- **Resolution:** SHIPPED (recipe surface, 2026-07-10 sweep) — the paper's
  precondition was met the same day it became answerable: `mgf-brand-qt` 0.1.0
  entered the registry (PAPER-118 carve-out + REL-13 regen, PRs #205/#206) and
  the design core is already extracted into it (`mgf.brand_qt.{tokens,_mix,qss,
  theme,fonts,icons,motion,widgets/}` — verified on disk). Authored
  [`docs/recipes/desktop-brand.md`](docs/recipes/desktop-brand.md): the kit's
  module map, the five derived translation rules (tokens→QPalette+QSS ·
  chrome-vs-readout · one-gold-per-view · self-theming widgets ·
  reduced-motion-as-setting), the GUI-01 consumer discipline, the
  vm-vmanager-app archive as deep reference, and the adopt-in-a-new-consumer
  short path; indexed in `docs/recipes/README.md`. **Graduation stays open by
  design:** promote rules 1–5 to a DESKTOP_UI standard (EXTEND, founder) when
  the second consumer lands (hrb-app / figrecovery Phase 7 named).
- **Status:** SHIPPED (recipe; standard-graduation deliberately deferred to the second adopter)

> These five papers came out of a 2026-06-06 red-team of the federation-sync
> mechanism (simulating a sibling-repo agent handed only "run `mgf-common
> sync-prompt` and follow it") against three dimensions: (1) full resource
> access — always the latest, no guesswork; (2) full understanding + value
> capture — lessons + feedback never lost; (3) a recorded, resumable, multi-
> session plan. The **recipe-level** surface of each shipped the same day
> (federation-sync-prompt.md + the new lesson-learned template); the **binding
> rule / mgf-common CLI** parts remain OPEN below and are tracked in
> `pending-feedback/mgf-common.md` where they cross into mgf-common.

### PAPER-03 — `LESSON_LEARNED.md` as a first-class (eventually binding) artifact
- **Severity:** 🟡 Sharp edge
- **Filed by:** mgf-standard (red-team session) on 2026-06-06
- **What hurts:** The federation has no home for "the non-obvious thing I
  learned working in THIS repo that the next agent would otherwise rediscover."
  CHANGELOG records what shipped, FEEDBACK routes friction to *another* repo, a
  conformance ledger records rule state — none capture a local lesson. A core
  mgf value (never waste or duplicate effort) had no artifact behind it.
- **Why it matters:** Lessons live only in chat transcripts and evaporate; the
  same gotcha (e.g. the `VIRTUAL_ENV`/uv footgun) gets re-hit across sessions
  and repos.
- **Evidence:** `grep -r LESSON_LEARNED docs/` returns only prose
  (API_DESIGN.md:104); it is absent from the DOCUMENTATION.md required-docs set.
- **Suggested shape:** (a) ✅ SHIPPED recipe surface — new
  `docs/recipes/lesson-learned-md-template.md` + the sync prompt's PHASE-B
  LESSON-OUT step appends a dated entry at the repo root. (b) OPEN — promote to
  a binding **DOC-rule** in DOCUMENTATION.md (append a new id; update README
  Index of MUSTs + the FEDERATION required-docs list, DOC-18 ripple) and gate
  its presence in `mgf-fed catch-up`/`app-doctor --standards` (the CLI change is
  in `pending-feedback/mgf-common.md`). Stage it advisory-now / binding-later so
  the corpus isn't retro-failed before repos create the file.
- **Status:** ✅ SHIPPED as a SHOULD — **DOC-19** (v2.20) drafted + the §2.0.1 matrix / FEDERATION.md / What's-new ripple done (0.1.19). Deliberately staged as a SHOULD (kept OUT of the Index of MUSTs so it doesn't retro-fail every repo's ledger). The remaining step is the future **promotion to MUST** (Index of MUSTs + the `doctor --standards` presence gate = mgf-common PAPER-99), to run as a federation-sync wave once the file is broadly adopted.

### PAPER-04 — Durable cross-repo feedback outbox (`pending-feedback/`)
- **Severity:** 🟡 Sharp edge
- **Filed by:** mgf-standard (red-team session) on 2026-06-06
- **What hurts:** The sync prompt tells an agent to route cross-repo findings to
  the target's `FEEDBACK.md` but also that the federation is READ-ONLY ("never
  reach in"). Those two collided: the agent could neither write the paper nor
  keep it — findings degraded to a sentence in a final report that nobody
  relays. Value leaked exactly where the prompt claimed to capture it.
- **Why it matters:** A real bug found in a dependency, or a justified challenge
  to a standard, was lost unless a human happened to read the transcript —
  breaking "never lose value" and the "everything is up for evolution" value.
- **Evidence:** federation-sync-prompt.md GUARDRAILS (pre-fix) "routed to the
  right FEEDBACK.md — never reach in", with no writable destination.
- **Suggested shape:** ✅ SHIPPED recipe surface — a `pending-feedback/<target>.md`
  **outbox in the agent's OWN repo** (the one place it may write): the agent
  drafts a proper `feedback-md-template` paper there, the supervisor relays it
  into the target's `FEEDBACK.md`. OPEN — a short supervisor-side **relay
  recipe** (drain `pending-feedback/` → targets' FEEDBACK via `tea`/PR) and a
  decision on whether `pending-feedback/` presence/format is a binding
  convention or stays recipe-level.
- **Status:** SHIPPED (found already resolved on the 2026-07-10 sweep; the paper was never flipped) — both pending items landed 2026-06-13: the **binding decision** = **FB-05 promoted SHOULD→MUST** (README Index footer, "FB joins as the feedback-discipline family"; `DOCUMENTATION.md#FB-05`, v2.23) and the **supervisor-side relay recipe** = [`docs/recipes/feedback-relay.md`](docs/recipes/feedback-relay.md) + the `mgf-fed feedback` tool (shipped mgf-fed 0.3.0; `--check` is the no-silent-rot gate FB-05 names as its enforcement).

### PAPER-05 — "Latest" is ambiguous; sync can silently bind to a stale pin
- **Severity:** 🔴 Blocker (correctness of the core guarantee)
- **Filed by:** mgf-standard (red-team session) on 2026-06-06
- **What hurts:** The seams resolve the corpus from the version THIS project's
  pin RESOLVES TO — which may lag the latest release. Nothing asserted
  installed == latest, so an agent behind a stale ceiling (`mgf-common<0.47`
  while 0.47 is published) syncs to OLD standards and reports success. "I
  synced" silently meant "to whatever my stale pin resolved," not "to current."
- **Why it matters:** Directly undermines dimension 1 (always get the latest):
  a confident, green, WRONG sync.
- **Evidence:** prompt resolved "from the version THIS project pins" with no
  registry-vs-installed comparison; the live `catch-up` latest-check is an
  unwired TODO (project-state-handoff item #4).
- **Suggested shape:** ✅ SHIPPED prompt surface — STEP 0 **ASSERT-LATEST**:
  compare installed vs the Codeberg simple/REST index, and if the pin ceiling
  excludes the latest, record a REQUIRED pin-walk + FLAG rather than treat
  stale-as-current. OPEN — wire a real live-latest check into `mgf-common
  catch-up` (registry HEAD vs installed snapshot), routed in
  `pending-feedback/mgf-common.md`.
- **Status:** SHIPPED (status reconciled on the 2026-07-10 sweep — both halves had landed) — prompt guard shipped 0.1.18 (STEP 0 ASSERT-LATEST); the CLI half resolved as mgf-common PAPER-98 with a **recorded disposition that differs from the original ask, deliberately**: `catch-up` stays offline and ships a no-network snapshot-provenance advisory (so "green" states *what corpus it checked against*), while the live-latest comparison remains agent-side per the prompt guard (PR #9, commit `100b52e`). The stale-as-current failure mode is closed from both directions; nothing further is pending here.

### PAPER-06 — No ASK gate: the prompt over-weights autonomy on ambiguous calls
- **Severity:** 🟡 Sharp edge
- **Filed by:** mgf-standard (red-team session) on 2026-06-06
- **What hurts:** PHASE A ended "proceed autonomously" with no boundary for
  when to STOP and verify with the human. An agent could unilaterally mark a
  rule N/A, or apply an irreversible/cross-cutting fix, on a genuine judgment
  call — a silent conformance hole or an unsafe change with no checkpoint.
- **Why it matters:** Dimension 3 explicitly wants questions/verification when
  needed; wrong unilateral calls are the costly failure mode.
- **Evidence:** federation-sync-prompt.md (pre-fix) line "proceed autonomously"
  with no ask condition anywhere in PHASE B.
- **Suggested shape:** ✅ SHIPPED — a PHASE-B **ASK gate**: decide autonomously
  when clear, but stop and ask (recording the question in the plan's OPEN
  QUESTIONS first) on ambiguous applicability, irreversible/cross-cutting fixes,
  or rule conflicts. OPEN — decide whether to also encode it as a general
  agent-behaviour MUST in AGENTS.md.
- **Status:** SHIPPED (decision tail closed on the 2026-07-10 sweep, Court verdict recorded per AG-15) — the "encode as a general agent-behaviour MUST" question has been **answered by the corpus's own evolution** since filing: **AG-06** (risky/irreversible/shared-state ops MUST get explicit confirmation) + **AG-15**'s never-block protocol (record the open question, sensible default, mark dependents `INFERRED`, continue — bounded by the STOPPERS list) together encode the ASK gate's substance corpus-wide, and the sync prompt keeps the operational PHASE-B gate. A third, duplicate encoding would add surface without adding force — reviewed; no further rule needed.

### PAPER-07 — Plan ≠ ledger; no multi-session resume protocol
- **Severity:** 🟢 Nice-to-have
- **Filed by:** mgf-standard (red-team session) on 2026-06-06
- **What hurts:** The conformance ledger (state) doubled as the plan (sequence),
  with no sequenced steps, blockers, or open-questions section, and no
  instruction for resuming across sessions — a fresh session might redo PHASE A
  or, worse, assume the work was done.
- **Why it matters:** Big out-of-sync projects need multi-session work;
  dimension 3 wants the plan recorded and resumable.
- **Evidence:** prompt had only ⏳ ledger rows; `grep -i resume|session`
  returned no resume protocol.
- **Suggested shape:** ✅ SHIPPED — PHASE-A **A4 PLAN**
  (`docs/inprogress/<sync>-plan.md`, separate from the ledger, with ordered
  fixes + blockers + OPEN QUESTIONS) and an explicit **RESUME** step (re-run
  STEP 0 + ASSERT-LATEST; if corpus unchanged, continue ⏳ items). OPEN — decide
  whether the `<sync>-plan.md` location is a named convention worth a one-line
  rule.
- **Status:** SHIPPED (decision tail closed on the 2026-07-10 sweep, Court verdict recorded per AG-15) — the `docs/inprogress/<sync>-plan.md` location **stays recipe-level, deliberately**: the sync prompt is the only surface that creates these files and it already prescribes the path (the operative convention lives exactly where it is exercised); `docs/inprogress/**` is the established in-flight-work home and is explicitly a working surface (LOCK §1). A standalone one-line rule would duplicate the recipe without changing behavior — reviewed; no rule needed.

### PAPER-128 — Three repos had NO `clone:` block at all — silently inheriting BOTH the OAuth flake and the WF-09 partial-clone 504

- **Severity:** 🟡 Sharp edge (two known, papered defects were being inherited by default, invisibly)
- **Filed by:** Court/CTO (the PAPER-109 rollout wave) on 2026-07-11
- **What hurts:** `mgf-hrb`, `mgf-test-supervisor` and `mgf-cost-watch` ship **no `clone:` section whatsoever**. They therefore use Woodpecker's **default** clone — which is (a) credential-injected from the short-lived OAuth token (**PAPER-109**), and (b) a **treeless partial clone**, the exact shape that intermittently 504s on Codeberg's lazy promisor tree-fetch and which **WF-09 / `partial: false` exists to prevent**. Both defects were being inherited **by omission**: nothing in the repo says "we opted out", because there is nothing there at all.
- **Why it matters:** the fleet believed `partial: false` was universal — it is in the canonical template, so every *generated* pipeline has it. But these three are bespoke or non-generated, so "we fixed that fleet-wide" was quietly untrue for them. **An absent block is the most invisible kind of drift: a grep for the wrong value finds nothing, and a grep for the right one finds nothing either.** This is the PAPER-120 class (bespoke pipelines don't inherit) in a form that is harder to see, because there is no line to be wrong.
- **Evidence:** `sed -n '/^clone:/,/^steps:/p' .woodpecker.yml` → **empty** for all three (on `origin/main`, 2026-07-11), while every generated member carries the explicit block. The same three were the only ones the PAPER-109 rollout could not patch by pinning an existing block.
- **Resolution:** SHIPPED (this wave) — each of the three now carries an explicit, hand-maintained `clone:` block with **both** fixes: the pinned `codeberg_clone_token` credential (PAPER-109) **and** `partial: false` (WF-09), with a comment saying why it is hand-maintained (PAPER-120).
- **Durable ask (relayed to mgf-fed):** the **gate floor** should assert `partial: false` and the pinned credential as **required clone properties** on every member — the `ci-command` check reads step *commands*, and a clone block is configuration, not a command, so it currently sails past. **A missing block must be a finding, not a silence.**
- **Status:** SHIPPED (instances); the gate-floor extension is relayed

### PAPER-129 — `mgf-cost-watch` and `mgf-website` are outside the registry too: they have pipelines, so nothing flags them

- **Severity:** 🟡 Sharp edge
- **Filed by:** Court/CTO (the PAPER-109 rollout wave) on 2026-07-11
- **What hurts:** PAPER-122 established that a repo outside `MGF_LIBRARIES.json` is invisible to every registry-scoped tool, and mgf-fed's new **`ci-ungoverned`** check closes that — **but only for repos with NO pipeline**. `mgf-cost-watch` and `mgf-website` are **also** non-members, and they **do** have pipelines — so they are *not* flagged, yet `mgf-fed scaffold` will not manage them either. They sit in a blind spot between the two checks: **governed enough to look fine, ungoverned enough to be skipped.** They were both missed by the PAPER-109 re-render (the generator simply does not know them) and had to be patched by hand.
- **Why it matters:** every future template change will silently skip them, exactly as this one did. That is the PAPER-120 failure mode again, but arising from *registry membership* rather than the exempt list — and it is quieter, because these repos have green pipelines that look entirely healthy.
- **Evidence:** `MGF_LIBRARIES.json` (37 members) contains neither `mgf-cost-watch` nor `mgf-website`; both have a committed `.woodpecker.yml` on `origin/main`; `mgf-fed scaffold <repo>` left both unchanged during the PAPER-109 wave (verified: the fix had to be hand-applied).
- **Suggested shape:** decide each repo's status **explicitly** — either register it (if it is a fleet artifact whose CI we own) or record it in a `[exempt].unmanaged` list with a reason — and have `drift-check` **fail on any cloned `mgf-*` repo that is neither a registry member nor explicitly exempted**, pipeline or not. Today the check only fires when a pipeline is *absent*; the real invariant is *"every repo we own is accounted for."*
- **Status:** OPEN

> **A note on method, recorded against my own work (2026-07-11).** While verifying PAPER-122 I used a membership check keyed on `name` — but registry entries are keyed on **`repo`**, so my check returned "not a member" for **every** repo, including the ones that are. It happened to agree with the (independently correct) agent finding about `mgf-brand`, so the conclusion stood — but **the verification was worthless, and I reported it as evidence.** A check that cannot fail proves nothing. Re-verified properly: `mgf-brand` is genuinely absent from the 37 members; `mgf-hrb`, `mgf-test-supervisor` and `mgf-standard` are present.

> **Renumbered on arrival (2026-07-14).** These two papers were filed on a branch as
> `PAPER-123` and `PAPER-124`. While that branch sat open, `main` grew its own — and
> entirely different — 123, 124, 125, 126 and 127. Merging the branch as written would
> have put **two different papers under each ID**, in a queue whose own spec says
> "`PAPER-NN` is a monotonic ID, per this repo." They are relanded here as **128** and
> **129**, with their evidence re-verified first-hand rather than trusted:
> - **PAPER-128's claim is now SHIPPED** — `mgf-hrb`, `mgf-test-supervisor` and
>   `mgf-cost-watch` all carry an explicit `clone:` block on `origin/main` today (the
>   PAPER-127 SSH rollout closed it).
> - **PAPER-129's claim still STANDS** — `mgf-cost-watch`, `mgf-website` and `mgf-brand`
>   are still absent from the 37-member registry.

### PAPER-130 — `REL-13` cannot see the only drift that matters: **main vs. the published artifact**. 28 of 30 distributions' version numbers are lies.

- **Severity:** 🔴 Blocker
- **Filed by:** Court/CTO (the 2026-07-14 full review) on 2026-07-14
- **What hurts:** `REL-13` (`mgf-fed registry --check`) compares a repo's **declared**
  version against the **registry's** version. Both said `0.4.10`, so it passed — while
  `mgf-alembic`'s `main` sat **28 commits past the `v0.4.10` tag**, still declaring
  `0.4.10`. **The published `0.4.10` and main's `0.4.10` were different code under the same
  number.** Nothing in the corpus checks main-vs-**tag** *content*, so nothing noticed.
- **Why it matters:** measured across the fleet, **28 of 30 distributions are in this
  state** (8–54 commits past their tag). Only `mgf-common` and `mgf-http` are honest — and
  `mgf-http` is precisely the one repo whose relicence reached users. That is not a
  coincidence; it is the proof. The consequences were all invisible:
  - **6 of 7 published wheels still shipped `License: MIT`** while `main` said Apache-2.0.
    The Foundation's relicence — recorded as *"done (51/0)"* — was true of `git main` and
    **false of anything installable. It had reached no consumer at all.** MIT carries no
    trademark carve-out and no patent grant, and grants already made under it cannot be
    retracted.
  - Four `FEEDBACK.md` papers marked **"✅ SHIPPED"** carried `Shipped in: next release`.
    That release never happened. A consumer reads the queue, concludes the fix is available,
    and `pip install` hands them a wheel without it.
  - **Eighteen leaves' published wheels still capped `mgf-common<0.50`** — a pin widened on
    `main` and never released. `uv` therefore **backtracked the entire federation's
    co-install to `mgf-common` 0.49.2**, and *that* stale cornerstone is why `mgf-fed`'s
    marker checker kept demanding `slow` (PAPER-131), flagging 24 conformant repos as broken.
    **One unreleased pin bump held the whole federation to the wrong cornerstone.**
- **Evidence:** `mgf/tools/verify_release_currency.py` (shipped with this paper) → **FAIL, 28
  distributions.** The published `mgf-alembic 0.4.10` wheel's metadata: `License: MIT`,
  `Requires-Dist: mgf-common<0.50`. After releasing `0.4.11`: `License: Apache-2.0`,
  `License-File: NOTICE`, `Requires-Dist: mgf-common<1.0,>=0.41` — verified by downloading
  the wheel from devpi.
- **Suggested shape:** a REL-family MUST — **a release gate that fails when `main` carries
  shipped-source changes past the tag matching its declared version.** Docs/CHANGELOG/CI
  changes are exempt; anything a consumer installs is not. Pair it with a metadata check:
  the licence in the latest published wheel MUST equal the licence on `main`.
  > **A fix that isn't released isn't shipped. A version that isn't published is a wish.**
- **Status:** OPEN (gate shipped in the `mgf` workspace; the corpus MUST is the ask)

### PAPER-131 — A checker that demanded a marker the standard had DELETED — and whose auto-fixer would have written it back into 24 repos

- **Severity:** 🔴 Blocker
- **Filed by:** Court/CTO (the 2026-07-14 full review) on 2026-07-14
- **What hurts:** `mgf-fed catch-up` kept its **own hand-copied duplicate** of the federation
  marker taxonomy. `mgf-common` 0.50.0 removed the deprecated `slow` marker per **TS-18** —
  whose migration note says plainly: *"drop the `slow` row from registered markers … under
  `--strict-markers` a leftover usage **fails collection, which is the loud migration
  signal**."* The fleet obeyed. **The checker did not follow**, so it demanded a marker the
  corpus had deleted, and flagged **24 of 40 repos as non-conformant FOR OBEYING THE
  STANDARD.**
- **Why it matters — it is worse than a false alarm.** `catch-up --apply` **writes** missing
  markers. Running the *documented remediation* would have **re-added the dead marker to all
  24 repos and then reported success.** In this very review, a careful reviewer read the
  checker's output and recommended exactly that. **The gate did not merely lie — it recruited
  a good engineer into spreading the lie, one keystroke from corrupting the fleet.** Its own
  message contained its refutation: the snippet it told you to paste read *"Deprecated.
  Removed in v2.0."*
  It was also **mislabelled TS-25** — which is *"flaky tests MUST be auto-quarantined"*, an
  entirely different rule — so anyone who followed the citation landed on the wrong page.
- **Evidence:** corpus TS-19 + §13.11 say **twelve** markers; `mgf.common.testing.MARKER_TIER`
  (which TS-19 names canonical) has **12**; `mgf-alembic`'s `pyproject.toml` registers exactly
  those **12**; `mgf-fed`'s `_CANONICAL_MARKER_DESCRIPTIONS` had **13**. `13 − 12 = slow`.
- **Suggested shape:** **the corpus should forbid a second copy of a canonical set.** Where a
  rule names a canonical mapping (TS-19 → `MARKER_TIER`), tooling MUST *derive* from it, not
  restate it. A copy of a truth is a second thing to forget. (Fixed in `mgf-fed`: the taxonomy
  is now derived from `MARKER_TIER`, the label corrected to TS-19, every hard-coded count
  removed — that literal `13` is *how* it drifted — and the tests, which hard-coded the
  taxonomy themselves and so **pinned the bug**, now derive it too.)
- **Status:** OPEN (fixed in mgf-fed; the "derive, never restate" MUST is the ask)

### PAPER-132 — Codeberg silently rate-limits **similarly-titled** PRs (~2/hour) — which is exactly the shape of every cohort release

- **Severity:** 🟡 Sharp edge
- **Filed by:** Court/CTO (the 2026-07-14 release wave) on 2026-07-14
- **What hurts:** opening a fleet-wide release wave (18 PRs, near-identical titles) fails
  with: `newIssue: "magogi-admin" posted 2 similarly named issues in the last hour: rate
  limited`. This is **not PAPER-110** — that is API *call volume*. This is a **content-similarity**
  anti-spam limiter, roughly **two similarly-titled PRs per hour**, and it is **undocumented**.
- **Why it matters:** a cohort release, a WF-13 re-render wave, a fleet-wide licence sweep —
  every one of them opens N PRs with deliberately uniform titles, because uniformity is the
  point. The federation's own recipes therefore *generate* the exact shape that trips this.
  And the failure is **silent**: the PR simply does not exist, and a driver script that does
  not check will merrily proceed as though it does.
- **Evidence:** 7 of 18 identical-title PRs were created before the limiter fired; the rest
  errored. **Re-issuing them with genuinely distinct titles succeeded immediately** (9 of 10).
- **Suggested shape:** the cohort-release + WF-13 recipes MUST require **per-repo distinct PR
  titles** (naming the repo and version), and any fleet driver MUST **verify each PR exists
  after creating it** rather than assuming success.
- **Status:** OPEN

### PAPER-123 — Retired `mgf-*` artifacts are still live (and installable) on public PyPI — a silent-downgrade channel PKG-01 cannot close

> **⚠️ CORRECTED BY EXECUTION (Court, 2026-07-14). Do not act on this paper as originally
> written — its specifics are wrong in both directions, and the real hole is elsewhere.**
>
> - **It is 15 live, unyanked `mgf-*` distributions on public PyPI (240 files, 0 yanked), not
>   4.** Probed every registry name directly.
> - **`mgf-logship` is NOT "a squatter — not ours". It IS ours** (author `Bassam Alsanie`,
>   Homepage → our Codeberg, summary byte-identical to our `pyproject`). **There is no
>   adversary in the namespace today.** The paper invents one.
> - **The paper misses the actual channel: our own devpi index UNIONS public PyPI.**
>   `magogi/prod` has `bases: ['root/pypi']` with an empty `mirror_whitelist`. Proven:
>   `+simple/mgf-common/` on *our* index serves `mgf_common-0.44.5` whose **sha256 is
>   byte-identical to the public pypi.org wheel**, and `+simple/mgf-doc-standard/` returns
>   **HTTP 200, 10 files** — a package we renamed away from and **do not host**. Every
>   pipeline in the fleet resolves through that URL.
> - **~22 `mgf-*` names on public PyPI are unclaimed.** Because the private index has no
>   releases for them, devpi falls through to the mirror — so anyone who registers one gets
>   it served straight into our fleet. *That* is the dependency-confusion door, and it is
>   open now.
> - **All 15 relics are published under the OLD MIT licence** while the repos are Apache-2.0.
>   **The relicence is not complete while they stand** (see PAPER-130 — it had not reached
>   any consumer at all).
> - The paper's own proposed mitigation (`--frozen`) **does not protect the core ring**:
>   `mgf-fed`, `mgf-fedres` and `mgf-fed-web` have **no committed `uv.lock`**, so `--frozen`
>   is not even available to them.
>
> **Fix the paper first, then act.** The first move is a config change **on our own infra**
> that needs nobody's permission: sever `root/pypi` from `magogi/prod` (giving third-party
> deps their own explicit index), or make the mirror fall-through actually deny `mgf-*`.
> Then claim the ~22 unclaimed names as tombstones, and decide on yanking the 15 relics.

| Field | Value |
|---|---|
| Status | OPEN |
| Submitter | TelePlasma (rescue round S0/S1; commit `9016ae7`) |
| Submitted | 2026-07-10 |
| Severity | 🔴 Blocker |
| Federation paper | relayed by the supervisor from `TelePlasma/pending-feedback/mgf-standard.md` (FB-05) |

- **Severity:** 🔴 Blocker
- **Filed by:** TelePlasma (2026-07-10 rescue round) — relayed 2026-07-12
- **What hurts:** PKG-01 forbids *resolving* `mgf-*` from pypi.org — but the
  pre-cutover uploads are still there and still installable: `mgf-common` up to
  0.44.5 (**55 releases / 110 files, 0 yanked**, last upload 2026-06-03 — the
  cutover day), plus `mgf-tele-teltonika`, the renamed-away `mgf-doc-standard`,
  and — not ours — an `mgf-logship` squatter (≤0.1.1). A rule that says "don't
  resolve from there" cannot stop a resolver that goes there *by accident*.
- **Why it matters:** demonstrated, not theorised. During TelePlasma's rescue
  round a credential-less re-resolve (`uv export` without `--frozen` — the CI
  step's own shape) silently swapped **all nine** `mgf-*` pins from the devpi
  versions to the stale pypi.org ones (mgf-common 0.46.3 → 0.44.5, and the rest
  of the tier) and rewrote `uv.lock`. No error, no warning. Any fresh machine, a
  mis-set CI secret, or a transitive-dep resolution can fall into the same hole,
  and the relics diverge further from the real packages every month — so the
  failure gets *quieter*, not louder.
- **Evidence:** `pypi.org/pypi/mgf-common/json`, re-checked at relay time
  (2026-07-12): 55 releases, 110 files, **0 yanked**; latest 0.44.5; last upload
  `2026-06-03T09:06`. Incident + fix: TelePlasma `9016ae7` (the CI pip-audit
  step that triggered it, now `--frozen`). Distinct from PAPER-112 (which is
  about the *prose* still describing Codeberg-PyPI) — this is about the
  *artifacts*.
- **Suggested shape:** a founder decision plus a one-time sweep — **yank** (or
  publish tombstone releases for) the pypi.org `mgf-*` relics; claim/park the
  squatted names where feasible; and make the pypi.org namespace state part of
  the distribution posture that is **checked, not assumed** (a PKG rule + a
  periodic check, in the spirit of REL-13 registry currency). Interim mitigation
  every repo can adopt today: `--frozen` on every CI `uv` verb (PAPER-125).
- **Status:** OPEN

### PAPER-124 — PAPER-114 residue: the *merge moment* still runs PR + push pipelines concurrently (shared-workspace clobber, new costume)

| Field | Value |
|---|---|
| Status | OPEN |
| Submitter | TelePlasma (rescue round; Woodpecker repo 48 pipelines #22/#23) |
| Submitted | 2026-07-12 |
| Severity | 🟡 Sharp edge |
| Federation paper | relayed by the supervisor from `TelePlasma/pending-feedback/mgf-standard.md` (FB-05) |

- **Severity:** 🟡 Sharp edge
- **Filed by:** TelePlasma (rescue round) on 2026-07-12
- **What hurts:** PAPER-114's push-branch filter removed branch-push↔PR
  concurrency — but **merging a PR still spawns two pipelines at once**: the PR
  pipeline of the merged head and the push pipeline of the new `main`. Observed
  on TelePlasma: pipeline #23 (push) ran its steps in an **empty workspace after
  a successful clone** — `install` → "No `pyproject.toml` found in current
  directory or any parent directory"; `secret-scan` → "open `.gitleaks.toml`: no
  such file or directory"; `commit-trailers` and `frontend-lint` red the same
  way — while #22 (the PR pipeline for the same commit) ran beside it.
- **Why it matters:** every merge in the fleet rolls these dice, and the
  signature is maximally misleading — four unrelated steps "broken" at once,
  each with a plausible file-not-found. It costs a full diagnosis cycle each
  time it fires, and it reads as a bad commit: we nearly re-fixed already-correct
  code before spotting the concurrent sibling.
- **Evidence:** Woodpecker repo 48, 2026-07-12 — #22 (`pull_request`) and #23
  (`push`) on the same commit `2d6e474`; #23's `clone` step is **green**, then
  the empty-tree failures above; #24 (manual, solo, same commit) → full backend
  battery green. Same contention class as the exit-126 case PAPER-114 names.
- **Suggested shape:** (a) infra — per-pipeline isolated workspace volumes on the
  agents (Woodpecker workspace config; `mgf-cloud-provision`); or, at minimum,
  (b) a documented signature in the run-CI recipe ("multi-step *file not found*
  immediately after a merge = concurrency clobber; **re-run solo before
  diagnosing**"), and let the agent2 watcher flag concurrent same-repo pipelines.
  The fleet already pays for (b) in diagnosis time.
- **Status:** OPEN

### PAPER-125 — CI `uv` verbs without `--frozen` re-resolve and rewrite `uv.lock` mid-pipeline

| Field | Value |
|---|---|
| Status | OPEN |
| Submitter | TelePlasma (rescue round S0.3; commit `9016ae7`) |
| Submitted | 2026-07-10 |
| Severity | 🟡 Sharp edge |
| Federation paper | relayed by the supervisor from `TelePlasma/pending-feedback/mgf-standard.md` (FB-05) |

- **Severity:** 🟡 Sharp edge
- **Filed by:** TelePlasma (2026-07-10 rescue round) — relayed 2026-07-12
- **What hurts:** `uv export` / `uv run` **without `--frozen`** re-resolve the
  graph and rewrite `uv.lock` as a side effect. In an audit-shaped step
  (`uv export …` → `pip-audit`) the pipeline therefore mutates the very lockfile
  it is auditing; in a credential-less or misconfigured environment it composes
  with the still-live PyPI relics (PAPER-123) into a **silent downgrade** instead
  of a loud failure. Newer uv additionally emits the project itself (`-e .`)
  unless `--no-emit-project` is passed, which breaks `pip-audit` outright — so a
  step written against an older uv **rots silently** under a floating uv image.
- **Why it matters:** the failure product is a *wrong lockfile*, which is worse
  than a red build. TelePlasma's step carried this shape from its template era
  and was structurally red since 2026-06-06 with nobody knowing (its CI had never
  been registered — a separate finding). Any repo that copied the
  export-then-audit pattern carries the same latent hazard.
- **Evidence:** reproduced by execution 2026-07-10 — an un-frozen `uv export`
  swapped all nine `mgf-*` pins to the pypi.org relics and rewrote `uv.lock`
  (restored via `git restore`). Working reference shape, TelePlasma `9016ae7`:
  `uv export --frozen --no-emit-project` → strip private `mgf-*` (absent from the
  public advisory DB) → `pip-audit --strict --no-deps` (the lock already *is* the
  resolved graph). Note the canonical `templates/woodpecker/base.yml` has **no**
  `uv export` step today — so this is guidance-shaped, not a template bug.
- **Suggested shape:** a one-line WF-family rule (or a run-CI recipe note): every
  CI `uv` verb that must not re-resolve carries `--frozen`; audit-shaped exports
  add `--no-emit-project`; private `mgf-*` are stripped before a public-advisory
  audit. Cheap to state — and it closes the door PAPER-123's artifacts leave open.
- **Status:** OPEN

### PAPER-126 — OB-02 is ambiguous for consumer applications: does the `mgf_` metric prefix bind an app's OWN domain metrics?

| Field | Value |
|---|---|
| Status | OPEN |
| Submitter | TelePlasma (rescue round S2.5 ledger audit; commit `474b635`) |
| Submitted | 2026-07-10 |
| Severity | 🟢 Nice-to-have |
| Federation paper | relayed by the supervisor from `TelePlasma/pending-feedback/mgf-standard.md` (FB-05) |

- **Severity:** 🟢 Nice-to-have
- **Filed by:** TelePlasma (2026-07-10 rescue round) — relayed 2026-07-12
- **What hurts:** OB-02 says "**Every** metric name MUST follow the federation
  convention `mgf_<domain>_<unit>_<aggregation>` … The `mgf_` prefix **isolates
  federation metrics** from third-party metrics in shared dashboards." For a
  consumer application's *own* domain metrics the two halves pull in opposite
  directions: TelePlasma emits `teleplasma.telemetry.records.ingested`
  (app-namespaced, dotted OTel form). Renaming those to `mgf_*` would put app
  metrics *into* the very namespace the rule exists to keep clean.
- **Why it matters:** every consumer app hits this on its first OB-02 ledger row,
  and CFM-03 completeness forces each of them to write *something*. Without a
  ruling, some will rename to `mgf_*` (polluting the federation namespace) and
  others will not (ledger inconsistency across the app tier).
- **Evidence:** `docs/standards/OBSERVABILITY.md` OB-02 (wording unchanged at
  corpus 0.1.62; re-checked at relay time 2026-07-12);
  `TelePlasma/src/teleplasma/observability/metrics.py:83-118` (the three app
  metrics); its ledger row deliberately holds at ⏳ pending this ruling rather
  than renaming speculatively (`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`
  §2.3).
- **Suggested shape:** a CLARIFY-class amendment — the `mgf_` prefix binds metrics
  emitted **by `mgf-*` libraries**; consumer applications use their own app prefix
  with the same `<domain>_<unit>_<aggregation>` grammar and the OB-03 canonical
  labels (`app` / `version` / `env`), which already disambiguate the same metric
  across consumers in a shared dashboard. One sentence in OB-02 closes it for the
  whole app tier.
- **Status:** OPEN

## Closed

### PAPER-02 — Umbrella-drain slices need a "verify the neutral surface" gate before declaring cross-repo
- **Severity:** 🟡 Sharp edge
- **Filed by:** vm-vmanager-app (Claude Code session) on 2026-06-04
- **What hurts:** When draining a consumer off a retiring umbrella, it is easy
  to MISCLASSIFY a slice. Two failure modes, both hit during the
  vm-vmanager-app providers drain:
  1. *False cross-repo:* `security_facts` was deferred as a 3-repo campaign
     (assumed it needed `domain_pid`/`libvirt_uri`/`network_xml` added across
     core/sdk/libvirt). It was fully **app-only**: `libvirt_uri` was already in
     app config; network isolation was already on `NetworkProfile.forward_mode`
     (`None` ⇒ isolated); and `domain_pid` was **never implemented on the real
     provider**, so the PID-derived facts were dormant no-ops — nothing to
     port. (PR #35.)
  2. *False app-only:* `storage` reads and `vm snap info` LOOK like a clean
     client repoint but are genuinely blocked by **thin observation contracts
     missing fields** — `PoolInfo` lacks `available_bytes`/`active`,
     `_SnapshotInfoContract` lacks `state`. Repointing naively would silently
     drop data.
- **Why it matters:** Misclassification either burns a cross-repo campaign on
  app-only work, or ships a data-losing slice against a too-thin contract.
  Recurs on every umbrella-consumer drain across the federation.
- **Evidence:** vm-vmanager-app PR #35 (security_facts, mapped cross-repo →
  shipped app-only); storage/snapshot-info deferred for real contract gaps.
- **Suggested shape:** Add a "drain slice classification" gate to the
  federation drain recipe. For each provider/umbrella call a slice removes,
  confirm BOTH: (a) the neutral SDK/contract value type actually carries every
  field the consumer *reads* (a same-named method is not enough), and (b)
  whether the legacy implementation even populates it in production — a
  never-implemented method is a dormant no-op, not a feature to preserve. Only
  after both checks fail may a slice be declared cross-repo-blocked.
- **Resolution:** Recipe shipped — [`docs/recipes/umbrella-drain.md`](docs/recipes/umbrella-drain.md):
  the **slice classification gate** is its centerpiece. Per removed
  provider/umbrella call, answer **Q-A** (does the neutral SDK/contract value
  type carry every field the consumer *reads* — fields, not a same-named
  method?) and **Q-B** (does the legacy actually *populate* it in production,
  or is it a dormant no-op?). Decision table: neutral type carries everything →
  app-only; missing field but legacy never populated it → app-only (drop the
  no-op); missing field that legacy genuinely produced → cross-repo-blocked
  (file the field gap to the sibling's FEEDBACK, name the field). Both
  vm-vmanager-app failure modes (false-cross-repo `security_facts`,
  false-app-only `PoolInfo`/`_SnapshotInfoContract`) are worked into it.
  Indexed in `docs/recipes/README.md`; cross-linked from `library-app-separation`
  / `codeberg-pr` / `cohort-release-wave`. Closed 2026-06-04.
- **Status:** CLOSED

### PAPER-01 — No documented CLI recipe for agents to open Codeberg PRs
- **Severity:** 🟡 Sharp edge
- **Filed by:** vm-vmanager-app (Claude Code session) on 2026-06-03
- **What hurts:** Landing a slice ends in a Codeberg (Forgejo) PR, but the
  federation had no documented or installed CLI for PR operations. `gh` is
  GitHub-only; `tea` (the Gitea/Forgejo CLI) was not installed. So an agent
  that finishes a green branch can `git push` but cannot open the PR — it can
  only hand back the `…/compare/main...<branch>` URL for a human to click.
- **Why it matters:** Every federation repo lands work as Codeberg PRs (e.g.
  vm-vmanager-app #27–#34). The missing tool wedges a manual step into an
  otherwise fully-automatable land-the-slice loop, across all repos and agents.
- **Evidence:** vm-vmanager-app `drain-providers-snapshot` slice — branch
  pushed, PR opened manually via the compare URL because no `tea`/`gh` was on
  PATH; remote `ssh://git@codeberg.org/magogi-admin/vm-vmanager-app.git`.
- **Suggested shape:** Adopt `tea` as the standard Codeberg CLI in a
  federation-process recipe: install (static binary → `~/.local/bin/tea`, from
  dl.gitea.com), a one-time `tea login add` with a scoped Codeberg token, and
  the canonical `tea pr create --base main --head <branch> --title … --body …`
  invocation (plus `tea pr merge`). Document it beside the existing
  land-the-slice / PR process so any agent can open and merge PRs directly.
- **Resolution:** Recipe shipped — [`docs/recipes/codeberg-pr.md`](docs/recipes/codeberg-pr.md):
  `tea` install, a **repository-scoped** Codeberg login (distinct from the
  `write:package` publish token — a package-only token `403`s on `pr create`),
  and the real flag surface (`tea pr create --base/--head/--title/`**`--description`**
  — note it's `-d`, not `gh`'s `--body`; some repos branch-protect `master`).
  Indexed in `docs/recipes/README.md`; cross-linked from the sync-prompt and
  cohort-release recipes. Merging stays the supervisor's call (WF-01 + release
  discipline). Closed 2026-06-04.
- **Status:** CLOSED

### PAPER-127 — Fleet CI clones died when the forge shed *anonymous* git traffic — the mechanism behind PAPER-109, and why SSH was the right permanent fix

| Field | Value |
|---|---|
| Status | ✅ RESOLVED ON ARRIVAL (2026-07-12) |
| Submitter | TelePlasma (rescue round S1.5; Woodpecker repo 48 pipelines #1–#3) |
| Submitted | 2026-07-10 |
| Severity | 🟡 Sharp edge |
| Federation paper | relayed by the supervisor from `TelePlasma/pending-feedback/mgf-standard.md` (FB-05) |

- **Severity:** 🟡 Sharp edge
- **Filed by:** TelePlasma (2026-07-10 rescue round) — relayed 2026-07-12, already closed by the PAPER-109 SSH wave; filed for the **mechanism + independent confirmation**, which the original PAPER-109 record did not carry.
- **What hurt:** during a multi-hour Codeberg degradation on 2026-07-10,
  **anonymous** git smart-HTTP returned 504/503 while *authenticated* requests
  answered in ~0.5 s. git/libcurl send credentials only **after** a 401
  challenge — which never arrived — so every Woodpecker clone failed for the
  duration even though valid credentials were present on the runner. PAPER-109
  recorded this failure class as a "credential-injection flake — restart the
  pipeline, don't investigate"; restarting cannot help while the forge's
  anonymous tier is shedding, because a fresh injection changes nothing about
  *when* credentials are offered.
- **Why the mechanism is worth recording:** it explains why SSH is not merely a
  workaround but the **correct permanent fix** — SSH authenticates inside the
  handshake and never touches the challenge-response path — and it retires the
  "restart it, don't investigate" folklore for this signature.
- **Evidence:** probed by execution from **both** the CI host (`195.15.241.48`)
  and a workstation, 2026-07-10: anonymous
  `GET …/TelePlasma.git/info/refs?service=git-upload-pack` → **504 in ~30 s**
  (later 503), same for `mgf-common`; the identical URL with `curl -u` → **200 in
  0.5 s**; the REST API → 200 throughout. Three consecutive Woodpecker pipelines
  (repo 48, #1–#3) died at `clone`. Recovery announced itself when the endpoint
  finally answered **401** (i.e. *challenged*) instead of timing out.
- **Resolution:** SHIPPED upstream as the PAPER-109 SSH-clone wave — `clone` over
  SSH with a read-only deploy key (mgf-standard#222; e.g. mgf-fed `a73fa4c`).
  TelePlasma adopted it 2026-07-12 (deploy key `ci-clone-2026-07-12`; PR #47,
  `d1739df`) and has cloned green over SSH since. Nothing further pending.
- **Status:** ✅ RESOLVED

---

## Plasma project feedback cohort — filed 2026-06-11 (PAPER-100..106)

External feedback from the **Plasma project** (Magogi Foundation), submitted as
`docs/inprogress/Plasma_To_Magogi_Feedback_Papers_v0_1.md` (staging ids P-A..P-G; bundle
review of v0.1.48). All seven re-verified against the live corpus on filing. Six shipped
same-day; PAPER-100 (the audit-spine gap) shipped in mgf-standard 0.1.50 — the full 218→258 re-baseline. **All seven now resolved.**

### PAPER-100 — Index of MUSTs omits 5 families + REL-10..13 (~40 binding rules); headline propagates the gap
- **Severity:** 🔴 Blocker
- **Filed by:** the Plasma project on 2026-06-11 (staging id P-A)
- **What hurts:** The *Index of MUSTs* lists 218 rows across 16 families, but the corpus ships 21 binding families. TM/PF/OB/NM/GUI have **zero** index rows and REL-10..13 are defined-yet-unindexed (~40 MUST-level rules). Verified live: index = 218; 0 rows for each of those families; 0 for REL-10..13.
- **Why it matters:** The Index is the audit spine — CFM-03 and `doctor --standards` gate ledger completeness against it. A project passes "complete" against an incomplete denominator (~40 binding rules never audited). The index's own maintenance note shows the manual same-commit discipline has lost this race before.
- **Evidence:** `docs/standards/README.md` Index (218 rows); `TIME.md` / `PERFORMANCE.md` / `OBSERVABILITY.md` (OB-11) / `NAMING.md` / `GUI_CONSUMERS.md` binding families; `RELEASING.md` REL-10..13.
- **Suggested shape:** Generate the Index + headline counts from the per-doc Rules boxes (`gen_rules_index.py` + a `rules.json` companion, `--check` CI staleness gate) — apply REL-13's own medicine to the index. Interim: manual refill of the 5 families + REL-10..13 + the headline, in one DOC-18 commit.
- **Fleet impact (why it was held OPEN):** expanding the audited set (218 → 258) re-baselines **every repo's §Z ledger** — a federation event requiring an explicit go-ahead, not a doc tweak. The go-ahead was given; executed in full this session.
- **Resolution:** SHIPPED (`mgf-standard` **0.1.50**, 2026-06-13) — the suggested shape, end to end, in four phases: (1) Index of MUSTs refilled **218→258** across 21 families; (2) `scripts/gen_rules_index.py --check` anti-drift gate + `rules.json` companion (REL-13's own medicine applied to the index) + `tests/unit/test_rules_index.py`; (3) **all 21 federation §Z ledgers re-baselined to 258** — forward-compatible, since `doctor --standards` reads the Index dynamically via `mgf.standard.read_standard`, so no fleet break until the release; (4) released `mgf-standard 0.1.50` — a fresh install ships `read_standard("README")` = 258 rows, so every doctor enforces 258 on pin-refresh; REL-13 GREEN + CURRENT. Runbook: `docs/archive/2026/PAPER-100-rollout.md`; seed helper `scripts/seed_ledger_rebaseline.py`. (Built on top, same court-intake spirit: **FB-05** + the `mgf-fed feedback` relay tool, closing the outbox-handling seam this review's filing implicitly exercised.)
- **Status:** SHIPPED

### PAPER-101 — README navigation text stale vs the docs it indexes (P-B)
- **Severity:** 🟡 Sharp edge
- **Filed by:** the Plasma project on 2026-06-11 (P-B)
- **What hurts:** The directory tree said OBSERVABILITY "OB-01..OB-08" (ships OB-01..**11**, incl. OB-10 the cross-language wire rule); the reading-order table said RELEASING "REL-01..REL-08" (ships REL-01..**13**). A DOC-18 propagation miss inside the doc that defines DOC-18.
- **Suggested shape:** correct the ranges now; permanently subsumed by PAPER-100's generation.
- **Resolution:** SHIPPED — `docs/standards/README.md` ranges corrected to `OB-01..OB-11` and `REL-01..REL-13`.
- **Status:** SHIPPED

### PAPER-102 — Cookiecutter overlay invites a non-existent conformance level (P-C)
- **Severity:** 🟡 Sharp edge
- **Filed by:** the Plasma project on 2026-06-11 (P-C)
- **What hurts:** `PROJECT_OVERLAY.md` said "Replace `<n>` with `0`, `1`, or `2`" but the ladder is L1/L2/L3 (CFM-02) — the first conformance declaration a scaffolded project makes was off-by-one (invites L0).
- **Suggested shape:** fix to `1`/`2`/`3`; (follow-up) a `doctor --standards` lint that declared level ∈ {L1,L2,L3}.
- **Resolution:** SHIPPED — template line corrected to `1`/`2`/`3`. The validating-lint is noted as a follow-up enhancement.
- **Status:** SHIPPED

### PAPER-103 — Paper-closure status vocabulary drifts between REL-09 and the FEEDBACK template (P-D)
- **Severity:** 🟢 Nice-to-have
- **Filed by:** the Plasma project on 2026-06-11 (P-D)
- **What hurts:** REL-09 said `SHIPPED / DEFERRED / ❌ REJECTED`; the canonical FEEDBACK template's set is `OPEN/SHIPPED/DEFERRED/WONTFIX/MOVED` — two sanctioned vocabularies for one lifecycle field.
- **Suggested shape:** the template's set is canonical (FB-01..04 own the lifecycle); reword REL-09 `REJECTED → WONTFIX`.
- **Resolution:** SHIPPED — REL-09 reworded to `SHIPPED / DEFERRED / WONTFIX`.
- **Status:** SHIPPED

### PAPER-104 — `evidence_level` term drifts between the principle and the recipe (P-E)
- **Severity:** 🟢 Nice-to-have
- **Filed by:** the Plasma project on 2026-06-11 (P-E)
- **What hurts:** `audit-outcomes` defines `VERIFIED_BY_EXECUTION`; `architecture-review` instructed `CONFIRMED_BY_EXECUTION` — synonym drift makes "show me everything execution-verified" a two-pattern query forever.
- **Suggested shape:** the principles doc owns the vocabulary; align the recipe to `VERIFIED_*`.
- **Resolution:** SHIPPED — `docs/recipes/architecture-review.md` aligned to `VERIFIED_BY_EXECUTION` (all occurrences).
- **Status:** SHIPPED

### PAPER-105 — Bundle agent briefing self-identifies as a different version than the bundle provenance (P-F)
- **Severity:** 🟢 Nice-to-have
- **Filed by:** the Plasma project on 2026-06-11 (P-F)
- **What hurts:** the shared `magogi-standard-bundle` said "captured from v0.1.48" while its embedded `AGENT_BRIEFING.md` said "you are here: mgf-standard 0.1.46" — OP-04 (headline-facts) applied to the export artifact.
- **Suggested shape:** the export regenerates + stamps both files from one source.
- **Resolution:** SHIPPED — the bundle briefing stamped to `0.1.48` to match the capture. **Durable fix recorded:** any future bundle export MUST stamp the briefing version from the same source as the capture provenance (one source of truth).
- **Status:** SHIPPED

### PAPER-106 — Raw NUL byte in TESTING.md flips the largest rules doc to "binary" (P-G)
- **Severity:** 🟡 Sharp edge (VERIFIED_BY_EXECUTION)
- **Filed by:** the Plasma project on 2026-06-11 (P-G)
- **What hurts:** `TESTING.md` held one literal NUL (offset 26561, line 355) inside a Hypothesis example meant to read `@example(value="\x00")` — the raw byte was embedded instead of the escape text. The file decoded as valid UTF-8 (hid from encoding checks) but `file(1)` reported `data` and GNU grep's binary heuristic suppressed matches — a daily DOC-12 (AI-navigability) cost on the doc holding TS-01..28.
- **Suggested shape:** replace the byte with the literal `\x00`; add a CI gate rejecting control bytes outside `\t\n\r` over `docs/standards/`.
- **Resolution:** SHIPPED — NUL replaced with the literal text `\x00` (example now renders as intended; `file(1)` → UTF-8 text). Prevention gate added: `tests/unit/test_no_control_bytes.py` scans the corpus for control bytes (catches the class in CI).
- **Status:** SHIPPED

---

### PAPER-107 — No shared-code / duplication discipline; a mature adopter independently built one
- **Severity:** 🟡 Sharp edge
- **Filed by:** Claude Code session (Magogi intake review of the Plasma "Commons" standard) on 2026-06-11
- **What hurts:** The corpus has SP (scope) + DP-08 (replaceability swap-recipes) but **no design-time discipline against code duplication / for shared-code admission**. There is no rule-of-three, no DUP-defect class, no junk-drawer-module-name ban, no blame-free-eviction stance, and no "duplication is cheaper than the wrong abstraction" counterweight. Verified: grep of `docs/standards/` finds **0** hits for duplication-as-defect, commons/shared-code, junk-drawer naming, rule-of-three, or eviction; NAMING.md has no `utils/misc/helpers` ban.
- **Why it matters:** "Silent divergence — two implementations of one rule drifting until they disagree" is a real consistency risk in a federation of ~22 libraries that **share** concepts (canonical encoders, ID schemes, time, hashing) through mgf-common. Without a design-time check, duplication accretes; with over-eager extraction, a god-library forms — both defects the corpus currently names neither. The Plasma project — a sophisticated adopter that took our principles trio nearly verbatim — independently authored a full "Shared Code Standard (the Commons)" to fill exactly this gap. An adopter reinventing a discipline is strong signal the gap is real.
- **Evidence:** reference design — `soothfast/ops/Plasma_Shared_Code_Standard_v0_2.md` (Commons Check §1, R1 rule-of-three §2, DUP defects §5, blame-free eviction §4, blast-radius tiers §3, failure modes §8). Our gap — `docs/standards/` grep = 0; DP-08 covers only swap-recipes (`DESIGN_PRINCIPLES.md:407`).
- **Suggested shape:** a short new discipline (a DP SHOULD-cluster or `docs/recipes/shared-code.md`) folding back the field-proven elements: (1) a design-time **Commons/DUP check** before adding a module ("does an existing module own this behavior?"); (2) a **rule-of-three** extraction trigger (two confirmed consumers OR a third occurrence — speculation doesn't count); (3) **duplication-as-defect** with cheap waivers (incidental dup needs none — judgment over bureaucracy); (4) a **junk-drawer-name ban** (no `utils/misc/helpers/common` — domain names only), likely a NAMING (NM) rule; (5) **blame-free eviction** as first-class (reversing a wrong abstraction is a success, not a failure); (6) the **"duplication is cheaper than the wrong abstraction"** counterweight stated up front so the discipline can't be weaponised into premature abstraction; (7) a **blast-radius tier** on shared code (output bytes that reach the wire/contract = a contract tier — ties to OB-10 + AP stability). Credit the Plasma Commons standard as the reference.
- **Resolution:** SHIPPED — authored as `docs/recipes/shared-code.md` (the full discipline, federation-adapted) + **NM-05** (junk-drawer-name SHOULD); indexed in `docs/recipes/README.md`; cross-referenced from NAMING. Kept recipe/SHOULD-level (not a new MUST) so it binds the fleet without reopening every §Z ledger (the PAPER-100 blast radius) — promotable to a MUST if/when the Index re-baseline lands. Fleet scan: no junk-drawer modules present (already compliant). Added to the cookiecutter for new projects.
- **Status:** SHIPPED

### PAPER-108 — Federation Zig CI template: pin not bound to the lock, no retry-wrap, no validator lane, no `verify_ci` checker
- **Severity:** 🟡 Sharp edge (two of the four are silent reliability/determinism hazards)
- **Filed by:** Claude Code session (sooth-family CI hardening sweep, relayed via sooth-standard) on 2026-06-30
- **What hurts:** Four gaps in the root Zig CI template + the CI tooling, each inherited by everything derived from it:
  1. **Pin not bound to the lock.** `templates/woodpecker/base-zig.yml` pins `ziglang==0.16.0` as a bare literal, not tied to a committed `toolchain.lock`. CI can build on a different compiler than the lock pins and nothing notices — a determinism break (the PRV-10 "pins bound to ground truth" idea, unenforced for CI).
  2. **No retry-wrap.** The `pip install ziglang` is not retry-wrapped, so a transient PyPI/network blip reds the pipeline. sooth hit this exact flake — a doc-only push reded while the *identical* step passed on the PR check.
  3. **No bundle-validator lane.** The gate runs build/test/fmt but never the repo's bundle validator (the "definition of done").
  4. **No `verify_ci` checker.** mgf-standard ships no `tools/verify_ci.py`; there is no machine check that a federation repo's `.woodpecker.yml` conforms (full-clone, secret-scan, triggers, the strict-YAML quoted-colon footgun, pin↔lock, validator-in-CI). Conformance is by eye.
- **Why it matters:** `base-zig.yml` is the root template the whole federation (~22 libs) and the sooth family derive from — every gap propagates fleet-wide. (1)+(2) are silent reliability/determinism hazards; (4) leaves CI conformance unenforced. Related federation hazard: `base-zig.yml` **and** soothai pin Zig **0.16.0**, but the sooth kernel (`soothfast`) and every path-dep sibling pin **0.15.1** — sharing Zig across that boundary is a determinism break, and soothai's own `toolchain.lock` already flags the split as needing reconciliation.
- **Evidence:** `mgf-standard/templates/woodpecker/base-zig.yml:30,37` (bare `ziglang==0.16.0`; no `--retries`/`--timeout`/`||`-retry); absent `mgf-standard/tools/verify_ci.py`. Field-proven reference (sooth, offered for promotion as-is): `sooth-standard/tools/verify_ci.py` (the checker incl. the new required pin↔lock check + the validator-in-CI advisory), `sooth-standard/templates/woodpecker/zig.yml` (retry-wrap + pin-from-lock + a `validate-bundle` lane), `sooth-standard/docs/recipes/ci-onboarding.md`, commit `8565641`.
- **Suggested shape:** fold the sooth hardening back into `base-zig.yml` + mgf CI tooling: (a) retry-wrap the toolchain fetch (`pip install --retries 8 --timeout 60 … || (sleep 10; retry)`); (b) require `ziglang==X.Y.Z` to equal a committed `toolchain.lock`, enforced by a `verify_ci`-style required check; (c) add an opt-in bundle-validator lane; (d) adopt `verify_ci.py` federation-wide (stdlib, no network → usable as a CI step itself); (e) declare a coherent federation Zig line (or a documented policy for the 0.16/0.15 split). Credit the sooth-standard CI work as the reference; `verify_ci.py` is offered for direct promotion.
- **Resolution:** SHIPPED — branch `feedback/ship-paper-108-113-114` (EXTEND per LOCK §2/§4-1: the status becomes true at the founder's enacting merge; the WF-13 fleet re-render wave follows it). Per gap: (a) retry-wrap had already landed pre-lock (`base-zig.yml` toolchain fetches). (b) pin↔lock: sooth `verify_ci` check-#10 **semantics folded into the template itself** — an inline coherence gate in `base-zig.yml`'s `zig-build-and-test` (reads the pin from the rendered pipeline, no duplicated literal; committed `toolchain.lock` + mismatch = red, no lock = noted-not-fatal — no mgf Zig repo ships a lock yet, so the gate arms per-repo as locks land). (c) validator lane ships as a commented opt-in block in `base-zig.yml` (no mgf Zig repo has a bundle validator today; enabling is a WF-13 template/generator event, never a hand-edit of a rendered pipeline). (d) `tools/verify_ci.py` adoption **deliberately deferred**: the Court-blessed disposition (LOCK §4-1) is an *EXTEND-class template change*, and both REQUIRED semantics the checker would add (#9 branch-filter — via PAPER-114 — and #10 pin↔lock) now execute in the pipeline itself; promoting the stdlib checker as an mgf tool + gate is a clean follow-up if wanted. (e) the 0.16/0.15 federation-Zig-line split stays a founder policy call — the inline gate makes any per-repo split explicit rather than silent. The push branch-filter (gap-1's sibling hazard) shipped under PAPER-114 in the same branch.
- **Status:** SHIPPED

### PAPER-109 — Base `clone` step flakes on auth ("could not read Username"); fleet pays a manual-re-trigger tax
- **Severity:** 🟡 Sharp edge
- **Filed by:** Claude Code session (sooth CI + federation sweep) on 2026-07-01
- **What hurts:** The base `clone` step intermittently fails with `fatal: could not read Username for 'https://codeberg.org': No such device or address` (exit 128) — the clone gets **no credentials** for the private repo, so every downstream step is skipped and the pipeline reds through no fault of the change. This is distinct from WF-09 (the treeless-partial-clone 504 that `partial: false` already fixes) — it is a clone-**auth/credential-injection** flake. The fleet's current mitigation is a **manual re-trigger**.
- **Why it matters:** it reds PRs spuriously, costs a human a manual re-trigger each time, and — worse — erodes trust in "red" (people learn to shrug off reds, so a real failure eventually slips through). It hits Python and Zig repos alike (it's the base clone, not the toolchain).
- **Evidence:** in one sweep, **5 sooth PRs** reded on the `clone` step (soothfast/soothweave/first-light/sooth-brand + a re-run of mgf-standard); e.g. soothfast pipeline #6 clone log: `git fetch … +be80406…: → fatal: could not read Username for 'https://codeberg.org': No such device or address; exit status 128`. **All cleared on a plain restart** (soothfast #7, soothweave #4, first-light #5, sooth-brand #4, mgf-standard #193 — all success), confirming it's transient. Correlated with a Woodpecker agent wedge/restart window (see PAPER-111). The fleet already re-triggers for this by hand — `mgf-fed` pipeline **#154** message: *"ci: re-trigger pipeline (clone step flaked)."*
- **Suggested shape:** (a) wrap the base clone in a **bounded retry** (the git-plugin supports retries, or a retry step) — the same discipline already blessed for the toolchain fetch; (b) OR auto-retrigger on a *clone-only* failure; (c) root-cause the credential gap (does the repo netrc drop on `pull_request` events or during an agent reconnect?); (d) at minimum, document in `run-ci.md` that a clone-only red is a known-transient, so it isn't re-investigated from scratch each time.
- **Investigation (2026-07-10 sweep):** (d) was already shipped (run-ci.md §"Known transient: a clone-only red", citing this paper). (a) is now **ruled out with evidence**: `woodpeckerci/plugin-git` retries by default (`attempts: 5` / `backoff: 5s`, per its docs.md) and our clone config inherits those defaults — so every observed flake already survived five in-step retries. That fits the failure shape: the netrc/credential injection is absent for the step's *whole lifetime*, so in-step retry re-fails identically; only a fresh pipeline start (fresh injection) clears it — exactly why plain restarts fix it. Recorded as comments on both templates' clone blocks so (a) isn't re-proposed. (b) auto-retrigger-on-clone-only-red relayed to mgf-fed (`pending-feedback/mgf-fed.md` — it already drives pipeline restarts). (c) root-cause correlates with the PAPER-111 agent-wedge window; tracked with that investigation.
- **Update 2026-07-11 — (b) ENACTED.** The auto-retrigger shipped in mgf-fed as the new **`mgf-fed ci-heal`** verb (merged): it restarts a pipeline whose **only** failed step is `clone`, **bounded to once** (skipped when any later pipeline exists for the same commit), polling Woodpecker only — never Codeberg (the PAPER-110 budget). Exercised live during the 2026-07 wave, where it would correctly have declined the *other* reds (docker-teardown / config-fetch flakes fail more than the clone step). (a) in-step retry stays **ruled out with evidence** (plugin-git already retries `attempts: 5`). (c) the credential root-cause is the only piece left — the netrc is absent for the step's whole lifetime, and it correlates with agent restart windows; the PAPER-111 net (now deployed + self-observing) is the instrumentation that should finally catch it in the act.
- **Update 2026-07-11 — the exit-126 SIBLING is ROOT-CAUSED (and it was self-inflicted).** The `exit 126` / `failed to set up container networking: network wp_… not found` / "Canceled with every step failed at once" family — long treated here and in PAPER-114 as forge/agent "contention" — was **our own self-heal cron killing healthy pipelines**. Its wedge test was `created >= 1 && running == 0` with **no age check**, which is exactly what a pipeline looks like *during startup*; when the `*/5` cron landed in that window it `docker rm -f`'d the containers that were about to run and restarted the agent. Caught red-handed on mgf-fed pipeline #255 (`WEDGE: 2 stuck Created, 0 running -> restarting agent` → all 14 steps failed, "Canceled"). Fixed in `mgf-cloud-provision` **PAPER-09** (only *stale* Created containers count; fire-tested both ways) — after which the same commit that had failed three times passed cleanly (#257 success). **This paper's own `could not read Username` credential-injection flake is a DIFFERENT failure and remains open** — but a large share of what the fleet has been calling "CI flakiness" now has a name.
- **ROOT CAUSE FOUND + FIXED 2026-07-11 (VERIFIED_BY_EXECUTION, both directions).** **The clone credential is a one-hour OAuth token.** Woodpecker derives the clone's netrc from the repo owner's **Forgejo OAuth access token** — read straight from the Woodpecker DB: `login=magogi-admin · token_present=true · refresh_present=true · expiry=2026-07-11 22:45 · expires_in_h=0`. It is short-lived and continuously refreshed. **A pipeline that starts inside the expiry/refresh window is handed a DEAD credential for the step's entire lifetime** → `fatal: could not read Username`, **exit 128**. Every symptom this paper recorded now follows exactly: *why no in-step retry helped* (the netrc is bad for the whole step — plugin-git's own `attempts: 5` cannot fix a rotten credential), *why a plain restart "cured" it* (the token had refreshed by then), and *why it hit every repo equally* (they share one user token).
  **And the chain closes on us.** Woodpecker refreshes that token **by calling Codeberg** — and **Codeberg rate-limits us (PAPER-110)**. Fleet automation at speed plausibly starved the refresh, which is precisely why the flake **clustered during sweeps** (this paper's own evidence: *5 sooth PRs reded in one sweep*). **PAPER-109 and PAPER-110 are one causal chain, not two neighbours** — our own API hammering was breaking our own clones.
  **PROVEN, both directions, on `mgf-brand` (a real repo, real pipelines):** ① `CI_NETRC_PASSWORD` set to a **deliberately wrong** value → clone **fails, exit 128** — *the flake's exact signature, reproduced on demand* (pipeline **#10**, 0/6 steps ran); ② the same clone with the credential **pinned to the long-lived `codeberg_clone_token`** org secret → clone **succeeds, whole pipeline green** (pipeline **#11**, 6/6). ① also proves an explicit `CI_NETRC_*` env **overrides** Woodpecker's injected credential — which is what makes the fix possible at all.
  **FIX (this branch):** both canonical templates (`base.yml`, `base-zig.yml`) pin the clone credential to `codeberg_clone_token` (a long-lived org secret, already present and scoped to `push`/`pull_request`/`tag`). The clone no longer depends on an expiring OAuth token. Rolls out to the fleet on the next WF-13 re-render wave; the stale *"restart it, it's a known transient"* comment is deleted from both templates — **it is no longer a transient, it is fixed.**
- **CORRECTION then RESOLUTION (2026-07-12).** The first fix — an `environment: CI_NETRC_*` pin — was **wrong**, and "proven both ways on mgf-brand #10/#11" was a lucky sample of two. At fleet scale it flaked (mgf-alembic clone red #97, green on restart #98, identical config) and it is **schema-invalid for the clone section** (Woodpecker: "should not configure both environment and settings"), so `partial: false` could be dropped and re-expose the WF-09 504 — i.e. it may only have TRADED the OAuth flake for a partial-clone flake. The DIAGNOSIS (expiring ~1 h Forgejo OAuth token, starved by our own PAPER-110 rate pressure) was correct; the fix was not.
  **RESOLVED — clone over SSH with a READ-ONLY deploy key.** `use-ssh: true` + `ssh-key-private`/`ssh-host-key` (Woodpecker org secrets), passed via `settings` — schema-clean, and it removes the expiring OAuth token from the loop **entirely** (a static key that never expires). Setup: a dedicated ed25519 CI key (separate from the operator key), its public half added as a **read-only deploy key on all 48 pipeline repos** (Codeberg allows one deploy key across repos — verified). **PROVEN DETERMINISTIC this time:** on mgf-alembic the clone succeeded across repeated runs (pipelines 100/101/102); the one pipeline red was an unrelated `sbom` docker-network flake, not the clone. Both templates carry the SSH block; fleet inherits it on the re-render wave. **Least-privilege bonus:** the clone credential is now clone-ONLY (cannot push), unlike the OAuth token it replaced — a small #6 win.
  **Lesson kept:** a fix proven on a sample of two is not proven; verify at the scale you will deploy, and isolate the flake (the SSH clone's determinism was only visible once I separated the clone failure from the unrelated docker-network failure).
- **Status:** SHIPPED (SSH clone, deterministic; supersedes the fragile env pin)

### PAPER-110 — Automated fleet orchestration exhausts Codeberg's API rate limit (429), then can't merge for ~an hour
- **Severity:** 🟡 Sharp edge
- **Filed by:** Claude Code session on 2026-07-01
- **What hurts:** Driving the fleet through the **Codeberg REST API** at automation speed — polling commit-status, opening PRs, restarting pipelines, merging — exhausts Codeberg's shared fair-use quota and returns **HTTP 429 Too Many Requests**. The 429 then **blocks merges** (`POST /pulls/{i}/merge` 429s) for what looks like an hourly window, so an agent can wedge itself out of the very merges it was driving. `activate-ci.md` warns rollout is "paced," but there's no concrete **API-budget** guidance, so an agent naturally polls aggressively and trips it. `mgf-fed` (which orchestrates exactly this) is the prime exposure.
- **Why it matters:** it turns a finished, all-green batch into a "can't land for an hour" stall, and retries compound the window. It silently caps how fast the federation can move PRs/releases, and it's invisible until you hit the wall.
- **Evidence:** after opening 9 PRs + polling 8 commit-statuses every ~20s + restarting 5 pipelines, **every** merge `POST` returned `429` even when spaced 25s apart; `GET /pulls/{i}` returned the 429 HTML page. Recovered only by **stopping all Codeberg calls**. (Contrast: Woodpecker — self-hosted — was never rate-limited, so status polling should go there.)
- **Suggested shape:** bake a **Codeberg API rate-budget** into the recipe + `mgf-fed`: (a) poll **Woodpecker** (self-hosted, unmetered) for pipeline status in loops, **never** Codeberg commit-status; (b) cap Codeberg API calls (e.g. ≤N/min) with **exponential backoff on 429**; (c) space PR opens/merges generously and batch them; (d) prefer **SSH git** (unmetered) over the HTTP API where possible (push, not `tea` where avoidable); (e) document the observed limit (hourly bucket) in `activate-ci.md` beside the existing "fair-use" note.
- **Resolution:** SHIPPED (recipe surface) — found already landed on the 2026-07-10 sweep: `e707df4` (2026-07-01, "CI-reliability notes from the sooth sweep") added the full **"Codeberg API rate-budget — avoiding the 429 wall"** section to `activate-ci.md` covering all five suggested items — (a) as §4's poll-via-Woodpecker-never-Codeberg instruction (citing this paper), (b) cap + exponential backoff on 429, (c) space + batch (plus the distinct-PR-title limiter dodge), (d) prefer SSH git, (e) the observed hourly-window evidence inline. The paper was never flipped. The remaining half of the ask — the budget baked into **mgf-fed's own Codeberg client** (code-level cap/backoff, not guidance) — is relayed via `pending-feedback/mgf-fed.md`.
- **Status:** SHIPPED (recipe; mgf-fed client-side budget relayed to its queue)

### PAPER-111 — The `*/5` agent self-heal cron did not clear a 15-min wedge (silent-failing safety net)
- **Severity:** 🟡 Sharp edge
- **Filed by:** Claude Code session on 2026-07-01
- **What hurts:** `MGF_INFRA.md §2` documents that a `*/5` self-heal cron restarts a wedged Woodpecker agent (`worker_count=0`). But an observed wedge **persisted 15+ minutes** — the whole fleet's CI stalled (queue `worker_count:0, pending:13, running:4`) — and cleared only after a **manual** `sudo docker compose restart woodpecker-agent`. So the documented auto-recovery either isn't running, doesn't detect this wedge shape (workers=0 with a backlog, vs container-down), or restarts-but-re-wedges.
- **Why it matters:** the self-heal cron is the safety net for the single most critical shared resource — the CI runner. If it silently doesn't fire, one wedge takes the **entire fleet's CI** down until a human notices and SSHes in. It also defeats the AG-20 promise (owned infra self-recovers) precisely where that promise is automated.
- **Evidence:** queue `{worker_count:0, pending_count:13, running_count:4}` sustained 15+ min (all 9 pipelines stuck `pending`); the manual `docker compose restart woodpecker-agent` (MGF_INFRA §2) immediately drained it (pending 13→6→0, worker_count→4, two agents cap 2 each). No sign the `*/5` cron acted in that window.
- **Suggested shape:** verify the self-heal cron exists and runs (inspect its crontab + logs); make its trigger the **wedge condition** (`worker_count==0 && pending>0`), not just container-down; and **emit a signal** (OpenObserve record / push) whenever it fires *or* when a wedge persists > N minutes — a silent-failing self-heal must itself be observable. Consider a compose `healthcheck` that restarts the agent on failed heartbeat.
- **Investigation (2026-07-10, on-host, VERIFIED_BY_EXECUTION) — root cause found; net restored:** there are **two** self-heal crons on woodpecker-prod covering different wedge shapes: `*/5 self-heal.sh` (docker-debris: `Created`-stuck && `running==0` — healthy, 13 historical recoveries, and **correctly** didn't fire on this paper's wedge, which had `running:4`) and `*/3 agent-reconciler.py` (agent DOWN, or **schedulable+idle while work pends** — exactly this paper's shape, with a 2-hit guard + force-recreate recovery, agent2 included). The reconciler had recovered such wedges on 2026-06-21/22 — then went **silently dead on 2026-06-23**: its static `/opt/woodpecker/reconciler.token` JWT began 401ing (Woodpecker JWTs are HS256-keyed on the user's DB `hash`; a hash rotation kills static mints), and every run since logged `api error` (~1,950 lines) with **nothing paging** — the paper's "silent-failing safety net," confirmed with a mechanism. **That is why the 2026-07-01 wedge outlived the net.** Repair applied 2026-07-10 (AG-20 self-owned path): fresh JWT minted on-host via the `woodpecker_api.sh` mint (old token kept as `.bak-2026-07-10`), validated `queue/info` 200, subsequent cron ticks clean. The estate watcher (agent2) is separately **live** (uptime probes + Codeberg-issue pager, heartbeating same-day) but does **not** yet probe the wedge condition. MGF_INFRA §2/§5 rewritten to the verified state.
- **CLOSED 2026-07-11 — both durable pieces SHIPPED + DEPLOYED (founder-authorized), VERIFIED_BY_EXECUTION.** (1) `agent-reconciler.py` now **mints its JWT per-run from the Woodpecker Postgres** — no static token left to rot; unattended cron runs log `ok token=db-mint pending=…` and there are **zero api-errors** since (vs 1,951 lifetime, all from the 06-23→07-10 outage). (2) The **signal exists at last**: the reconciler + both hosts' `self-heal.sh` POST success-only heartbeats to the OO `backup_heartbeats` stream, and the agent2 `*/10` watcher runs a freshness check that **pages via its deduped Codeberg issue** when one goes stale (>30 min) — proven both ways at deploy (never-heartbeating job → exit 1 → pages; healthy set → exit 0 → silent). The blocker turned out to be that **the heartbeat credential never existed** (`/etc/mgf/backup-heartbeat.{url,token}` absent on every host — §5's mechanism was documented but never wired); a **least-privilege OO service account** was minted for it. **The self-heal net is now self-observing:** if it dies again, it pages within ~30 min instead of rotting for 17 days. Code + deploy record: `mgf-cloud-provision` PAPER-01/02; infra truth: `MGF_INFRA.md` §2/§5. Residual (tracked, NOT part of this paper): the nightly **backups** still do not heartbeat → `mgf-cloud-provision` **PAPER-03** (🔴).
- **Status:** SHIPPED (deployed + verified 2026-07-11)

### PAPER-112 — Standards corpus still describes Codeberg-PyPI publishing after the devpi cutover
- **Severity:** 🟢 Nice-to-have (documentation-vs-running-system coherence; the cutover itself is intentional)
- **Filed by:** Claude Code session (federation CI/remote review) on 2026-07-01
- **What hurts:** The fleet is mid-cutover from the Codeberg PyPI registry to the self-hosted **devpi** index, and the two sources of truth disagree on the load-bearing "where do releases go?" question. The **running system** publishes to devpi: the generated `templates/woodpecker/base.yml` publish step targets `195.15.203.250/magogi/prod/` with `TWINE_USERNAME: magogi` / `TWINE_PASSWORD: {from_secret: devpi_password}`; `templates/policy.toml` lists `verify-devpi` as a required step; `frag-no-pypi-surface.yml` (PKG-12) guards against `pypi.org`; `MGF_INFRA.md` names devpi as "the private PyPI the fleet publishes to + resolves from." But large parts of the **standards corpus** still describe the Codeberg path: `docs/standards/WORKFLOWS.md` (WF-03/04/08 — `codeberg_package_token`), `PACKAGE_DISTRIBUTION.md` (PKG-*), `docs/standards/AGENTS.md` (AG-09), and `docs/recipes/cohort-release-wave.md`.
- **Why it matters:** an agent (or human) reasoning about the *current* publish flow from the corpus reaches the **wrong** (Codeberg) answer — exactly the silent running-system-vs-written-standard divergence the federation guards against (it's the sibling of PAPER-05's "stale-as-current"). It's low-severity because the cutover is deliberate and the generated CI is already correct — but the corpus is what agents read.
- **Evidence:** running truth — `templates/woodpecker/base.yml` (publish → devpi), `templates/policy.toml` (`verify-devpi` required), `frag-no-pypi-surface.yml` (PKG-12), `MGF_INFRA.md §1/§2` (devpi + `devpi_password`). Lagging corpus — `WORKFLOWS.md` WF-03/04/08, `PACKAGE_DISTRIBUTION.md` PKG-*, `AGENTS.md` AG-09, `docs/recipes/cohort-release-wave.md`. Cross-checked in `docs/memory/release-and-registry.md` (which already flags devpi as current).
- **Suggested shape:** a reconciliation sweep of those docs to the devpi truth (retarget WF-03/04/08 + PKG-* + AG-09 + `cohort-release-wave.md`, swap `codeberg_package_token` → `devpi_password`). Until that lands, a one-line banner at the top of each lagging doc — *"CUTOVER IN PROGRESS (Phase-2 B2): devpi is the current publish target; Codeberg-PyPI references below are pre-cutover"* — so a reader knows which source is authoritative.
- **Status:** SHIPPED — (1) the interim banner shipped in PR #179 (2026-07-01); (2) the full reconciliation sweep (`feedback/devpi-retarget`) **merged to main**, verified 2026-07-10: the whole publish corpus (`PACKAGE_DISTRIBUTION.md` PKG-01..12, `WORKFLOWS.md` WF-03/04/06/08, `RELEASING.md` REL-04/05/06, `WORKFLOWS_PATTERNS.md` §4, `AGENTS.md` AG-09/AG-16/AG-18, `NAMING.md`, `COMMON_COMPONENTS.md`, the README *Index of MUSTs*, the recipes + conformance ledger) is retargeted to the devpi model and every interim banner is gone (the sole remaining "CUTOVER IN PROGRESS" string in the repo is this paper's own quoted text; the only `codeberg_package_token` references left sit inside PACKAGE_DISTRIBUTION.md's collapsed *Historical record* block — intentional history). Open tails — **all closed 2026-07-10 (founder-delegated Court calls):** the three host-ops confirmations decided + recorded inline (PACKAGE_DISTRIBUTION.md §4/§6/§8 + MGF_INFRA §2: single identity stands, accepted risk, revisit at the public flip · no stage-pin mandate, single-trusted-index + private prefix is the posture · strictly append-only, version-bump recovery, remove = emergency-only behind a founder decision package), and `scripts/verify_codeberg.py` **removed** (script + template + both policy.toml entries + pyproject carve-out + SCOPE.md currency; superseded by the inline `verify-devpi` step; fleet copies deleted in the WF-13 wave).

### PAPER-113 — Generated Woodpecker templates inline the devpi index endpoint (3× in `base.yml` + 3 fragments) instead of one generated `DEVPI_URL`/anchor — an IP move is a fleet-wide committed-pipeline edit
- **Severity:** 🟡 Sharp edge
- **Filed by:** sooth family — cross-family reuse review on 2026-07-01
- **What hurts:** `templates/woodpecker/base.yml` is a **generated** artifact (header lines 1–3: "GENERATED — do not hand-edit … rendered by `mgf-fed` (mgf.fed._woodpecker) … never patch this file in place"; WF-13), yet it inlines the full devpi endpoint `https://195.15.203.250.sslip.io/magogi/prod/` as a bare literal in **3** separate steps — `--index-url` (`:120`), `--repository-url` (`:239`), and a `url=` string (`:254`). The same host is inlined again in three fragments (`frag-cornerstone-ping.yml`, `frag-fedres-check.yml`, `frag-mgf-common-matrix.yml`) and the cookiecutter, and **nothing** references a single `DEVPI_URL` env / YAML anchor (a `grep` for one finds none). Because the generator renders this text into **every** consumer repo's committed `.woodpecker.yml`, the endpoint is copied fleet-wide. `195.15.203.250.sslip.io` is *not* a stable indirection — sslip.io wildcard-DNS encodes the IP in the hostname (`<ip>.sslip.io` → that IP), so the "hostname" moves with the IP.
- **Why it matters:** A generated file is exactly where an endpoint should be emitted **once** and referenced. Today a devpi host-IP change — which `MGF_INFRA.md §4` explicitly names as a maintenance trigger ("Update when … a host IP changes") — is not a one-line generator edit; it is an N-repos × 3-sites rewrite of committed pipelines, plus the three fragments and the cookiecutter, with every repo then regenerating + re-committing. `MGF_INFRA.md:31` is the declared single source of truth for that endpoint (URL + raw IP), but the generated CI doesn't honor it — it copies the literal. This is the same source-of-truth-vs-N-copies drift shape as PAPER-112, one layer down.
- **Evidence:** `templates/woodpecker/base.yml:120` (`--index-url`), `:239` (`--repository-url`), `:254` (`url=`) — `grep -c '195\.15\.203\.250' base.yml` = **3**. Same host inlined at `frag-cornerstone-ping.yml:21`, `frag-fedres-check.yml:12` (×2, retry), `frag-mgf-common-matrix.yml:21` (×2, retry), + `templates/cookiecutter-mgf-project/.../{pyproject.toml,README.md}`; **0** uses of a `DEVPI_URL`/`INDEX_URL` env or YAML anchor. Generated-artifact provenance: `base.yml:1–3`; WF-13 at `MGF_INFRA.md:72` + `docs/recipes/run-ci.md:18,25`. Endpoint as single-source-of-truth + IP-can-move: `MGF_INFRA.md:31` (endpoint = URL + raw IP `195.15.203.250`), `MGF_INFRA.md:67` ("a host IP changes"). *(Note: mgf-standard has no verbatim "never hardcode an IP" rule; this paper rests on the MGF_INFRA single-source-of-truth + the maintenance-trigger, not an asserted rule.)*
- **Suggested shape:** Have `mgf.fed._woodpecker` emit the index endpoint **once** — a top-of-pipeline `DEVPI_URL` env (or a YAML anchor) sourced from a single generator constant that itself derives from `MGF_INFRA.md`/`policy.toml` — and reference `$DEVPI_URL` at the three call sites (and the fragments/cookiecutter). Then a host move is one generator-constant change + a regenerate wave, not a hand-search across every committed pipeline. Pairs naturally with PAPER-112's reconciliation sweep (one endpoint, one authority).
- **Resolution:** SHIPPED — branch `feedback/ship-paper-108-113-114` (EXTEND; true at the founder's enacting merge; fleet re-render wave follows, WF-13). Landed **stronger than the ask's env-emission**: the endpoint is not emitted per-pipeline at all — every template/fragment site references `$DEVPI_URL`, a Woodpecker **server-level** variable (`WOODPECKER_ENVIRONMENT` on the CI host, live since 2026-07-04; value = the `MGF_INFRA.md §1` endpoint `…/magogi/prod/+simple/`; name coordinated with sooth-standard, whose conversion is already live-green), each consuming step led by a fail-fast guard line. Converted sites: `base.yml` ×3 (`app-doctor --index-url "$DEVPI_URL"`; publish `--repository-url "${DEVPI_URL%%+simple*}"`; verify-devpi `url = '${DEVPI_URL%/}/…'`), `frag-cornerstone.yml` ×2, `frag-fedres-check.yml` ×2, `frag-mgf-common-matrix.yml` ×2, `frag-cornerstone-ping.yml` ×1 — `grep -c '195\.15\.203\.250' templates/woodpecker/` = **0**. `MGF_INFRA.md §2` documents the variable + the host-move procedure. **Consciously left for companions:** the *generator-side* installs (mgf-fed's `_DEVPI_INDEX` constant behind `{{EXTRAS_INSTALL_CMD}}`/`{{SMOKE_INSTALL}}` etc.) still inline the literal — that is `mgf.fed._woodpecker` code, a cross-repo companion change per the paper's own suggested shape; and the cookiecutter `pyproject.toml`/`README.md`, which are committed files a shell variable cannot reach (their endpoint follows PKG-*/PAPER-112 docs truth). A host move is now: server variable + `MGF_INFRA §1` row + mgf-fed constant + re-render wave.
- **Status:** SHIPPED

### PAPER-114 — `base-zig.yml` missing two sooth-proven fixes: `push` not branch-filtered (exit-126 PR/push contention) + no CI-pin↔`toolchain.lock` coherence check
- **Severity:** 🟡 Sharp edge
- **Filed by:** sooth family — cross-family reuse review on 2026-07-01
- **What hurts:** The root Zig CI template `templates/woodpecker/base-zig.yml` — which every mgf Zig repo derives from — is missing two fixes the sooth family already proved out and now ships:
  1. **`push` is not branch-filtered.** The `when:` block triggers on `push` *and* `pull_request` with no `branch:` filter on `push` (`base-zig.yml:10–13`), so on a feature-branch PR both pipelines run **concurrently on the same commit**. The redundant push run contends with the PR run over the shared workspace / container exec; sooth hit exactly this as an intermittent **exit 126**. (The Python `base.yml` shares the unfiltered shape, but the Zig template's `zig build` / container-exec steps are where the contention actually bit.)
  2. **No CI-pin↔lock coherence check.** `base-zig.yml:33,47` pin a bare `ziglang==0.16.0` with no coupling to a committed `toolchain.lock`, and mgf-standard ships no `tools/verify_ci.py` — so nothing verifies the pipeline's compiler equals the repo's pinned toolchain. CI can silently build on a different compiler than the lock pins (a determinism break).
- **Why it matters:** `base-zig.yml` is the shared root for mgf's own Zig libraries (e.g. mgf-hrb); both gaps propagate to each. (1) reds PRs spuriously and trains people to shrug off "red" (the sibling failure mode named in PAPER-109). (2) is a silent determinism hazard — the "pin bound to ground truth" idea, unenforced for CI. Both are field-proven fixes the sooth family is offering back.
- **Evidence:** Gap 1 — `base-zig.yml:11` (`- event: push`, no following `branch:`). Sooth fix: `sooth-standard/templates/woodpecker/zig.yml:14` (`branch: main` under `- event: push`), rationale `zig.yml:7–10` + `sooth-standard/docs/recipes/ci-onboarding.md:69–72` ("runs concurrently with the PR pipeline on the *same commit* … the contention can kill a step (a container exec / exit 126)"); enforced by `sooth-standard/tools/verify_ci.py:104–110` (check #9). Gap 2 — `base-zig.yml:33,47` (bare `ziglang==0.16.0`, no lock). Sooth fix: `verify_ci.py:112–125` (check #10), fail branch `:121–123` — `fail("CI ziglang==%s != toolchain.lock %s (%s) -- the CI pin must match the lock (VAL-08)" …)`; the lock is real + populated (`soothfast`/`soothtroth`/`soothweave` `toolchain.lock` pin `"version": "0.15.1"`). **Overlaps PAPER-108** (OPEN): gap 2 restates PAPER-108's gap-1 (pin not bound to lock) + gap-4 (no `verify_ci`) with the now-shipping sooth check; gap 1 (branch filter) is net-new. (PAPER-108's gap-2 retry-wrap has since landed at `base-zig.yml:33,47`.)
- **Suggested shape:** Fold both into `base-zig.yml` + mgf CI tooling: (a) add `branch: <default>` under the `push` event in the Zig template's `when:` block (and the Python `base.yml`); (b) bind the `ziglang==X.Y.Z` pin to a committed `toolchain.lock` and enforce equality via a `verify_ci`-style required check — adopt `sooth-standard/tools/verify_ci.py` (stdlib-only, no network, runnable as a CI step; it already ships both check #9 and check #10). Best merged with PAPER-108's disposition, since they cover the same Zig-template + CI-tooling surface. Credit the sooth-standard CI work as the reference.
- **Resolution:** SHIPPED — branch `feedback/ship-paper-108-113-114`, merged with PAPER-108's disposition as the paper suggested (EXTEND; true at the founder's enacting merge; fleet re-render wave follows, WF-13). Gap 1: `branch: main` now filters the `push` event in **both** `templates/woodpecker/base-zig.yml` and the Python `base.yml` `when:` blocks, with the sooth rationale comment (exit-126 same-commit contention; lineage `sooth-standard/templates/woodpecker/zig.yml:14` + `ci-onboarding.md:69-72`) — tag/cron/manual events untouched, and every fleet repo's default branch is `main` (verified; a different default becomes a generator param, not a hand-edit). Gap 2: the CI-pin↔`toolchain.lock` coherence check runs **inline in `base-zig.yml`** (check-#10 semantics: mismatch = red, no committed lock = noted; the pin is read from the rendered pipeline so no literal is duplicated). `verify_ci.py` itself deferred with reasons under PAPER-108's resolution. Credit: the sooth-standard CI hardening is the reference for both fixes.
- **Status:** SHIPPED

### PAPER-115 — Canonical CI lint step enforces `ruff check` but not `ruff format --check` — format drifts fleet-wide, unseen until a release
- **Severity:** 🟡 Sharp edge
- **Filed by:** mgf-fed supervisor (cockpit) on 2026-07-03
- **What hurts:** The canonical CI lint step runs `ruff check src tests` (the linter) but **never** `ruff format --check`. So formatting can drift into committed files with CI staying green. The only gate that catches it is the *local* release gate (`mgf-fed release-local`, whose `_drift` runs `ruff format --check`) — so drift surfaces only at release time, per-repo, on whatever ruff version the maintainer happens to have.
- **Why it matters:** On the mgf-fed 0.11.1 release, 3 test files carried accumulated ruff-format drift that CI had passed for weeks; the release **blocked**, needing a separate unblocker PR (mgf-fed #69). This is fleet-wide — every repo's format is ungated in CI — and it couples releasability to the maintainer's local ruff (pin is a wide `>=0.4,<0.16`).
- **Evidence:** `templates/woodpecker/base.yml:39` (`- ruff check src tests`; no `ruff format --check` in `base.yml` or any `frag-*.yml`). The gate that *does* catch it: mgf-fed `_drift` ("ruff format-stability pin"). Repro: mgf-fed PR #69 `chore(format): ruff-format 3 drifted test files` — `tests/test_closed_box.py`, `tests/fleet/refresh_lock.py`, `tests/fleet/test_fleet_coinstall.py` all drifted while CI was green.
- **Suggested shape:** Add `- ruff format --check src tests` to the lint step in `base.yml` (right after `ruff check`). Roll out as a cohort (like AP-16) — a fleet re-scaffold will surface existing format drift, each repo format-cleans or is already clean. Consider pinning ruff to an **exact** fleet version (not a range) so the format baseline is deterministic across machines + CI.
- **Resolution:** SHIPPED — branch `feedback/sweep-2026-07-10` (gate strengthening per LOCK §2: the status becomes true at the founder's enacting merge; the WF-13 fleet re-render wave follows and will surface any latent drift per-repo — the paper's own cohort rollout). `- ruff format --check src tests` now sits directly after `ruff check` in `base.yml`'s lint step, with the mgf-fed #69 rationale inline. The **exact-ruff-pin** question is consciously NOT taken here: `policy.toml`'s `ruff_spec = "ruff>=0.4,<0.16"` ceiling already freezes the format baseline at the fleet's 0.15.x line (its comment documents exactly this intent), so the residual exposure is a *within-0.15.x* format change — real but small; tightening to an exact pin is a fleet-wide pyproject wave and stays a founder policy call. **Pin decision taken 2026-07-10 (founder-delegated):** keep the window — with the CI format gate in place, the release-time-ambush class (the paper's actual harm) is closed at PR time; an exact pin would add a fleet-wide bump chore per ruff release for marginal residual gain. Revisit only if a within-0.15.x format change actually bites.
- **Status:** SHIPPED

### PAPER-116 — Canonical gitleaks template has no `.claude/` allowlist; repos with agent config carry a justified carve-out that reads as scaffolding drift
- **Severity:** 🟢 Nice-to-have
- **Filed by:** mgf-fed supervisor (cockpit) on 2026-07-03
- **What hurts:** `templates/gitleaks.toml` has no allowlist entry for `.claude/`. Repos that keep Claude Code agent config (`.claude/` — permission allowlists / settings embedding credential-**shaped** command patterns with placeholder tokens, e.g. `Authorization: token __TRACKED_VAR__`) must add a `'''\.claude/'''` allowlist path or gitleaks' curl-auth-header rule re-flags the placeholders. mgf-common **and** mgf-standard both carry this justified entry — and `mgf-fed drift-check` reports it as `[exact] .gitleaks.toml differs from template/gitleaks.toml`.
- **Why it matters:** Two of the fleet's most central repos show as "drift" for a correct, security-neutral carve-out — noise that trains maintainers to ignore drift-check, with no clean resolution (removing the entry re-introduces false-positive leak flags; the file can't match the template without losing the protection).
- **Evidence:** `mgf-common/.gitleaks.toml` + `mgf-standard/.gitleaks.toml` each add `'''\.claude/'''` under `[allowlist].paths` (absent from `templates/gitleaks.toml`, ~line 45). `mgf-fed drift-check` → `✗ mgf-common (2)` / `✗ mgf-standard (1)`, item `[exact] .gitleaks.toml differs from template/gitleaks.toml`.
- **Suggested shape:** Add `'''\.claude/'''` (with the placeholder-token rationale comment) to the canonical `templates/gitleaks.toml` allowlist — `.claude/` is now a fleet-standard directory, so every repo should allowlist it; roll out via the SH-05 gitleaks scaffold path. (Companion filed against mgf-fed: make drift-check's `.gitleaks.toml` `[exact]` rule tolerant of *additive* per-repo allowlist entries.)
- **Resolution:** SHIPPED — landed 2026-07-04 in `921f302` ("template gains .claude/ allowlist", the Wave-2 F-M11/F-M12 fleet sweep) as the paper's suggested shape, rationale comment included; the paper was simply never flipped (found on the 2026-07-10 feedback sweep). Verified 2026-07-10: `templates/gitleaks.toml` carries the entry, and both `mgf-standard/.gitleaks.toml` and `mgf-common/.gitleaks.toml` are **byte-identical** to the template — the `[exact]` drift noise is gone. The mgf-fed additive-tolerance companion is thereby **moot for this instance** (no additive entries remain to tolerate); whether drift-check should tolerate additive allowlist entries *in general* is mgf-fed PAPER-03's standing template-vs-subset question (also LOCK §4-2, via mgf-fastapi's extra entry).
- **Status:** SHIPPED

### PAPER-117 — AG-03 `core.hooksPath .githooks` is incompatible with the pre-commit framework (a repo can't run both)

- **Severity:** 🟡 High
- **Filed by:** figrecovery on 2026-07-09
- **What hurts:** `docs/recipes/standardized-git-config.md` §4 instructs `git config core.hooksPath .githooks` to activate the AG-03 `prepare-commit-msg` trailer hook. A repo that uses the **pre-commit framework** (figrecovery does — the four-gate + gitleaks + codename + test-mirror hooks) cannot also set `core.hooksPath`: `pre-commit install` hard-errors *"Cowardly refusing to install hooks with `core.hooksPath` set."* So a project cannot have BOTH the AG-03 hook (via `core.hooksPath`) AND its pre-commit gate — the recipe's activation step and the pre-commit framework are mutually exclusive.
- **Why it matters:** The conflict is **silent** — `mgf-fed app-doctor` passes without `core.hooksPath` set, so nothing flags that the AG-03 hook never fired. A repo can believe its trailer hook is active when it is not, and the recipe offers no guidance for the (very common) pre-commit-framework case.
- **Evidence:** `pre-commit install` refuses when `core.hooksPath` is set (upstream pre-commit behaviour). figrecovery hit this during the 2026-07-09 federation re-sync: it runs the pre-commit framework, so it could not follow §4's `core.hooksPath .githooks` step. Resolution taken: wire the trailer check (`scripts/check_commit_trailers.py`) as a `commit-trailers` pre-commit/pre-push hook-type in `.pre-commit-config.yaml` + author both AG-03 trailers manually; `app-doctor` accepts this.
- **Suggested shape:** Have `standardized-git-config.md` cover the pre-commit-framework path explicitly — wire `prepare-commit-msg` (and the trailer gate) as pre-commit `hook-types` (`pre-commit install --hook-type prepare-commit-msg`) instead of via `core.hooksPath`, OR document the `commit-trailers` pre-push-hook alternative figrecovery used. Consider having `app-doctor` warn when AG-03 is claimed but neither `core.hooksPath` nor a `prepare-commit-msg` pre-commit hook is active.
- **Resolution:** SHIPPED (this branch, 2026-07-10) — the conflict re-verified by execution (pre-commit 4.6.0 refuses install, both plain and `--hook-type`, with `core.hooksPath` set; exact "Cowardly refusing" error). Fix landed as the paper's first suggested shape: (1) `standardized-git-config.md` §4 split into **§4a** (`core.hooksPath`, no-framework repos) / **§4b** (pre-commit repos: the same committed `.githooks/prepare-commit-msg` script as a `repo: local` `stages: [prepare-commit-msg]` hook + `pre-commit install --hook-type prepare-commit-msg`) with the one-liner + Verify sections split to match — §4b **verified end-to-end** (install clean; `git commit -m` gains `Signed-off-by`; merge/squash still skipped). (2) The hook script now falls back to `PRE_COMMIT_COMMIT_MSG_SOURCE` (pre-commit exports it in place of `$2`; pre_commit/commands/run.py:378) so merge-skip parity holds under both wirings. (3) AG-18(c) + its enforcement-table row + the AGENTS.md §4 prose amended to name both wirings and their mutual exclusivity (CLARIFY-class — the MUST's substance, hook-present + authoritative CI gate, is unchanged). (4) CONTRIBUTING.md carries the caveat. (5) The recipe's stale "authoritative gate is tracked in mgf-common PAPER-102" note replaced — the gate ships today as `templates/woodpecker/frag-commit-trailers.yml` per AG-18(c). The `app-doctor` hook-*active* advisory (present-but-inert detection) is relayed to mgf-fed via `pending-feedback/mgf-fed.md`.
- **Status:** SHIPPED

### PAPER-120 — Bespoke-pipeline repos (`[exempt].woodpecker`) silently miss every template gate-strengthening

- **Severity:** 🟡 Sharp edge (VERIFIED_BY_EXECUTION — three repos found ungated *and* drifted, including this one)
- **Filed by:** mgf-standard (WF-13 re-render wave, PAPER-115 rollout) on 2026-07-10
- **What hurts:** four repos carry a hand-maintained `.woodpecker.yml` (`templates/policy.toml [exempt].woodpecker` = `mgf-hrb`, `mgf-test-supervisor`, `mgf-website-template`, **`mgf-standard`**). `mgf-fed scaffold` deliberately does not regenerate their pipelines — so a gate added to the canonical template reaches **every repo except them**, silently. Nothing warns; `drift-check` skips exempt repos by design, and `policy.toml`'s `required` list checks step *names*, not the commands inside them.
- **Why it matters:** the exempt set is not a backwater — it holds the standards repo itself and the test supervisor. Proven live this wave: after PAPER-115's `ruff format --check` shipped and the fleet re-rendered, **all three Python-linting exempt repos still had no format gate, and all three carried real format drift** (mgf-hrb `tests/_broker.py`; mgf-test-supervisor; mgf-standard) — i.e. the gate that was just declared fleet-wide was absent from the repo that authored it. Any future gate (a new lint, a new scan) inherits the same hole.
- **Evidence:** `templates/policy.toml` `[exempt].woodpecker`; post-wave `origin/main:.woodpecker.yml` for mgf-hrb (line ~100 `ruff check src tests`, no `ruff format --check`), mgf-test-supervisor, mgf-standard; `uv run ruff format --check src tests` → 1 drifted file in each of those three (CI-pinned ruff; a stale local ruff reports clean — that mismatch is what let the drift survive).
- **Suggested shape:** (a) make the exemption *narrow and checked*: keep hand-maintained pipelines, but have `drift-check`/`app-doctor` assert a small set of **required commands** (not just step names) in every pipeline — exempt or not — sourced from `policy.toml` (e.g. `required_commands = ["ruff check", "ruff format --check", "check_commit_trailers.py"]`), so an exempt repo REDs when it falls behind a gate; (b) add a "walk the exempt list" line to the WF-13 wave recipe (`run-ci.md`) so a template gate change is not reported complete until the bespoke pipelines are hand-updated; (c) short term (done in this branch): hand-add the format gate + format-clean the three repos.
- **Resolution:** **SHIPPED 2026-07-11** — the durable ask landed in `mgf-fed` as **`ci-command`** (merged): `required_commands` are now asserted against **every** member's committed `.woodpecker.yml` — **exempt or not**. Exempt means "we don't regenerate your file", never "you may silently drop a federation gate". Matching is per **command line** (not step name), so a step merely *named* `lint` is not a gate and a commented-out command is not a gate. Fire-tested by an independent verifier against a purpose-built synthetic federation: repos placed in `[exempt].woodpecker` were **still** flagged — the core claim of this paper, mechanically enforced. (The short-term hand-fix — gate + format-clean on all three drifted exempt repos, mgf-standard included — landed 2026-07-10.)
- **Status:** SHIPPED

### PAPER-121 — Supervisor-driven coverage step requires a pyproject key nothing enforces; repos red with ALL TESTS PASSING

- **Severity:** 🟡 Sharp edge (VERIFIED_BY_EXECUTION)
- **Filed by:** mgf-standard (WF-13 re-render wave) on 2026-07-10
- **What hurts:** the canonical coverage step drives the suite through the federation test supervisor (`coverage run --source=src/mgf -m mgf.test_supervisor --tier 0-2` → `coverage combine`), which runs pytest in **child processes**. That only produces coverage data if the consumer's `pyproject.toml` carries `[tool.coverage.run] patch = ["subprocess"]` (coverage ≥ 7.10). A repo missing that key reds with a *baffling* signature: **every test passes**, then `coverage` reports "No data was collected" and `coverage combine` exits 1 ("No data to combine"). The template comment *states* the requirement; nothing checks it, and the failure names neither the key nor the pyproject.
- **Why it matters:** hit **three repos in one wave** (`mgf-brand-web`, `mgf-cloud-web`, `mgf-fed-web`) the moment they were re-rendered onto the supervisor-driven step — a green suite reported as a red build, with a message that points at coverage internals rather than the missing config. Every future repo scaffolded or re-rendered onto that step walks into the same trap.
- **Evidence:** mgf-brand-web pipeline #31 `test-3.12`: `[mtest] OK: 39 passed (39 total)` → `CoverageWarning: No data was collected` → `coverage combine` → `No data to combine` → exit 1. Working reference: `mgf-http/pyproject.toml` `[tool.coverage.run] patch = ["subprocess"]` (green on the same step). Missing in brand-web / cloud-web / fed-web (fixed in the wave branches).
- **Suggested shape:** (a) `mgf-fed app-doctor` (or the scaffold/drift check) FAILS a repo whose pipeline uses the supervisor-driven coverage step while its `pyproject.toml` lacks `[tool.coverage.run] patch = ["subprocess"]` — the config and the step are a coupled pair (a DOC-18-style coupling); (b) the cookiecutter ships the key by default; (c) TESTING.md §19.8 gets a "if you see 'No data to combine', you are missing this key" line so the symptom maps to the cause. Companion relayed to mgf-fed (the doctor surface).
- **Resolution:** **SHIPPED 2026-07-11** — the durable ask landed in `mgf-fed` as **`ci-coverage`** (merged): it **FAILS** when a pipeline runs `coverage run … -m mgf.test_supervisor` while the repo's `pyproject.toml` lacks `[tool.coverage.run] patch = ["subprocess"]`. Surfaced in three places so the fix is *named* wherever the baffling failure is met (drift-check · app-doctor · app-doctor --standards TS-23) — because the build's own error message names neither the key nor the pyproject. Fire-tested: fires on the real coupling, stays silent when the key is present or there's no supervisor-under-coverage. (The per-repo fixes landed in the 2026-07-10 wave.)
- **Status:** SHIPPED

### PAPER-122 — `mgf-brand` is a registry member with NO pipeline at all: every CI run errors, and nothing notices

- **Severity:** 🔴 Blocker (a fleet library ships with **zero** CI enforcement — its gates have never run on a PR)
- **Filed by:** Court/CTO fleet sweep on 2026-07-11
- **What hurts:** `mgf-brand` ships **no `.woodpecker.yml`** (verified: `git ls-tree origin/main` → absent; it is the only registry member missing one). The repo IS enabled on Woodpecker, so every event still creates a pipeline — which immediately **errors**: `could not load config from forge`. Four consecutive pipelines (#2–#5, push + pull_request) died that way during this sweep. There is **no branch protection** either (`branch_protections` → `[]`), so a PR merges with no check of any kind.
- **Why it matters:** `mgf-brand` is the fleet's **brand source of truth** (tokens + generated assets that `mgf-brand-web` / `mgf-brand-qt` derive from), and it is the one library where **no gate has ever run in CI** — not ruff, not the tokens `--check`, not gitleaks, not the commit-trailers gate. The failure is *silent by shape*: an errored pipeline reports no failing step, so a casual glance at the repo's CI page looks like "nothing to see" rather than "this repo is ungated." The whole fleet-wide gate discipline (WF-02/WF-13, the PAPER-115 format gate, the PAPER-120 exempt-list audit) quietly does not apply to it.
- **Evidence:** `git ls-tree --name-only origin/main` on `mgf-brand` — no `.woodpecker.yml` (every other `mgf-*` member has one); Woodpecker pipelines #2/#3/#4/#5 all `error` with `could not load config from forge: %!w(<nil>)`; `GET /repos/magogi-admin/mgf-brand/branch_protections` → `[]`. It is **not** in `templates/policy.toml [exempt].woodpecker` (so nothing sanctions the absence — it is a gap, not a decision).
- **Suggested shape:** (a) scaffold `mgf-brand`'s pipeline (`mgf-fed scaffold mgf-brand`) — it is an assets/generator repo, so the shape may need a variant (tokens `--check` + gitleaks + trailers, no pytest matrix); (b) enable branch protection requiring the check, as every other member has; (c) **the durable half:** `mgf-fed drift-check` / the ownership audit should FAIL a registry member that has no `.woodpecker.yml` and is not in `[exempt].woodpecker` — today the absence is invisible to every gate we own (this is the same "unchecked exemption" class as PAPER-120, one step further: not exempt, just *missing*).
- **CORRECTION (2026-07-11) — my framing was WRONG, and the truth is worse.** This paper called `mgf-brand` "a registry member with no pipeline". **It is not a registry member at all** — `MGF_LIBRARIES.json` lists 37 members, and `mgf-brand` is not among them (it ships no publishable package, so the generator never listed it). Caught by the mgf-fed agent implementing the fix, who checked the premise instead of trusting it.
  **Why that matters far more than the typo:** *every tool we own iterates registry members.* So the real root cause of "no gate has ever run on mgf-brand" is not a missing pipeline — it is that **a repo outside the registry is invisible to our entire toolchain**: drift-check, the ownership audit, the conformance sweep, the release gate. A member-scoped check would have faithfully reproduced the blind spot instead of closing it. The registry is not just a list of packages; it is the *denominator of everything we verify*, and anything outside it is ungoverned by construction.
- **Resolution:** SHIPPED (2026-07-11) — closed on both halves:
  1. **The instance:** `mgf-brand` now has a **working, fire-tested pipeline** (5 gates: tokens-check · icons-check · naming-check · commit-trailers · secret-scan — each proven to RED on an injected violation), CI **runs green on main for the first time in the repo's existence** (pipeline #9), and **branch protection is enabled** (`ci/woodpecker/pr/woodpecker` required, direct push disabled) so the gate actually blocks a merge. Two defects were caught pre-merge by adversarial verification: an understated lint-debt figure, and — real security — gitleaks allowlist entries that wildcarded whole directories containing hand-authored text, making a plaintext credential in `brand/assets/print/` **invisible** to the scan (now extension-scoped; re-proven both ways).
  2. **The class:** `mgf-fed` ships **`ci-ungoverned`** — drift-check now scans the federation root for **cloned `mgf-*` repos the registry does not list that ship no pipeline**, and fails them. Deliberately narrow (a non-registry repo that HAS a pipeline is running gates and is not flagged). Verified against the real fleet: exactly **one** finding — `mgf-brand` — and zero noise across 48 repos. Alongside it: **`ci-command`** (required *commands*, asserted on every member **exempt or not** — exempt means "we don't regenerate your file", never "you may silently drop a gate") and **`ci-coverage`** (the supervisor-coverage ↔ `patch=["subprocess"]` coupling). All three fire-tested by the verifier against a purpose-built synthetic federation.
  **Honest residual (recorded, not hidden):** `ci-ungoverned` only sees repos **cloned on the machine running drift-check**. A repo that exists on the forge but was never cloned locally is still invisible — closing that needs a forge-side repo listing. The blind spot is narrowed, not eliminated.
- **Status:** SHIPPED (both halves; residual forge-listing gap recorded)
