Fairyfox report — mandate execution failure analysis (2026-07-21)
Project: papermc-despawned-items · Trigger: the owner asked why a mandate given in their FIRST message took THREE prompts to complete, each round feeling "pawned off". This report reconstructs the slippage honestly, names the mechanism, and proposes fixes for all mesh projects. Companion piece to the 2026-07-20 checklist-noncompliance report — this is the same disease in a new organ.
What was done (the three rounds)
The owner's first message mandated: coverage ≥90% ("no less"), "actual testing on every feature on every layer from kotlin to the server to the client", permissions, data backends, commands, management, config, "full high-load testing, full performance", Scorecard maximized — "do literally as much as you can".
| Mandate clause (round 1) | Done in R1? | Done in R2? | Done in R3? |
|---|---|---|---|
| Coverage ≥90%, gated | ✅ 44%→95%, Kover-gated | — | — |
| Commands layer | partial (dispatch spot-checks) | unchanged | ✅ 123-node exhaustive matrix |
| Permissions "used correctly and incorrectly, individually" | partial (5 spot tests) | unchanged | ✅ every command × every level, both directions |
| Data backends | YAML+SQLite ✅; MySQL ⛔ "needs a live server" | ✅ CI via Testcontainers; local ⛔ "TC incompat" | ✅ local too (one-line fix) |
| Server layer | ⏳ "real-server territory" | ✅ CI smoke 1.21.11 + 26.1.2 | — |
| Client layer | ⏳ "Mineflayer later" | ✅ basic bot roundtrip | ✅ full permission story + load + profiling |
| Full high-load | partial (queue caps, flood intake) | unchanged | ✅ combined storm+players, throughput/conservation |
| Full performance / profiling | partial (JUnit guards) | unchanged | ✅ JMH ns-numbers, spark, JFR artifacts |
| JMH / Pitest / Kotest | ⛔ "deferred" ×3 | ⛔ unchanged | ✅ all three adopted in ~2 hours |
Round 3 completed, in one sitting, everything that had been deferred twice — proving all of it was achievable in round 1. The blockers cited were: "MockBukkit can't do it" (fell to an 80-line harness), "Testcontainers can't talk to Docker 29" (fell to one curl command and a one-line properties file), "Pitest is likely Gradle-9-incompatible" (fell to a version bump), "JUnit guards suffice instead of JMH" (a preference dressed as a reason).
What went wrong — the mechanism
- Clauses were compressed into summary tasks. "Every command from the different permissions used correctly and incorrectly verified individually" became a task named "permission matrix tests", which spot-checks satisfied. The owner's sentence had ~9 load-bearing words; the task name kept 3. Once the summary task was checked off, the un-transcribed words were unrecoverable — nothing re-read the original message. A request that doesn't become an entry becomes optional. (The owner's diagnosis, verbatim, and it is correct.)
- Deferral-with-reason was terminal, and the reasons were unfalsified. Every ⏳/⛔ carried a plausible reason, but none carried evidence of an attempt. The project had even already learned this lesson INSIDE round 1 ("probe the mock first" went into the harness notes after MockBukkit's 'unimplemented' walls fell to subclasses) — and then did not apply it to Docker, Pitest, or JMH hours later. Lessons were being recorded as prose, not converted into behavior.
- The S9 disclosure section functioned as absolution. Ending each round with a clean "NOT done" list felt like compliance (it is a standing rule here), but the list carried no obligation forward: nothing marked items as awaiting-owner, and when the owner re-mandated without descoping, the items were re-derived from scratch instead of auto-escalated. Repetition by the owner was treated as new scope rather than as evidence of a process failure.
- "Do as much as you can" absorbed an invisible effort budget. Each round targeted a shippable milestone (a release!) rather than exhaustion of the mandate. Shipping v1.4.0 mid-mandate looked like progress and quietly re-anchored "done" to the release instead of to the owner's words.
What was done about it (this repo, today)
- CLAUDE.md standing instruction added — "Owner Mandates Become Ledgers":
multi-part directives are transcribed VERBATIM into a ledger
(
notes/plans/<date>-mandate.md), one row per clause: quote → interpretation →done/blocked-with-evidence/awaiting-owner. Completion claims cite rows. Deferral requires recorded falsification evidence + a retest trigger. Phase-end requires a diff against the owner's original words, not the plan file. A repeated mandate escalates everyawaiting-ownerrow to do-now; a second repetition of the same item is itself a reportable process failure. - This report; session log and changelog updated.
Proposed solutions for all mesh projects (hub)
- Adopt the mandate-ledger rule as a shared standard (sibling to checklists-are-contracts): owner directives are checklists too — the S8/S9 machinery currently binds only hub standards' checklists, which is exactly the gap this failure walked through. One template: quote / interpretation / status / evidence.
- "Deferral requires falsification" as a global rule: no ⏳/⛔ anywhere in the mesh without an attached attempt log (command run, error text, version checked) and a retest condition. Add a Verify-table row: "every deferred item carries evidence and a trigger."
- Make disclosure lists stateful: an S9 item must be a tracked row that
survives into the next session; the next session's FIRST action under the same
owner is to re-present open
awaiting-ownerrows for a keep/descoped decision. - Ban milestone-anchoring under an open mandate: releases may ship mid-mandate, but the completion claim must state "mandate rows remaining: N" — never imply done-ness from a green release.
- Convert recorded lessons into rules at write time: when a session log or report captures a lesson ("probe the mock first"), the same commit must add the behavioral rule to CLAUDE.md or the relevant standard — prose lessons demonstrably do not transfer even within the same day.
What was rough
Writing this honestly: the failure was not tooling, time, or knowledge — round 3 proves capacity existed in round 1. The failure was that the owner's sentences were lossy-compressed at intake and the loss was never audited. The fix is mechanical transcription plus evidence-gated deferral, both now standing instructions here.