Changelog
Every release in plain English, newest first — generated from the project's living notes (notes/version/).
2026-07
2026-07-21 — Release v1.3.7 (PATCH): badge wall + supply-chain/quality tooling parity with random-ai-prompt
Owner asked to match the sibling project random-ai-prompt's
README badge wall and security tooling, adapted from its JavaScript/Node stack to this
Kotlin/Gradle/Paper one. Plan: notes/plans/badges-and-security-2026-07-21.md.
- README badge wall expanded from 5 → 21 badges: contributors/stars/forks/watchers, last-commit/commits, version, Java 21 + Paper 1.21.x (replacing the sibling's Node badge), CI, Codecov coverage, CodeFactor, Sonar quality gate + tech debt, OpenSSF Scorecard, docs, Pages deploy (replacing the sibling's Netlify badge), open/closed issues, open/closed PRs, license. Plus a commented-out Hangar + Modrinth usage-badge block, ready to enable on publish (both support CI publishing).
- Security workflow (SHA-pinned):
scorecard.yml(OpenSSF Scorecard — weekly + branch-protection + push-to-main). Dependabot already covered gradle + actions. CodeQL deferred (tried, doesn't work yet): GitHub's CodeQL extractor (2.26.x) doesn't support this project's Kotlin (2.4.10) — compiled build modes die onKotlinInterceptor$KotlinVersionTooRecentError, andbuild-mode: noneextracts no Kotlin ("no source code seen"). A CodeQL gate would be permanently red, so it's removed rather than shipped broken. Kotlin SAST/quality is covered meanwhile by SonarQube Cloud + Detekt (both Kotlin-aware); revisit CodeQL when it supports Kotlin 2.4.x. - Coverage → Codecov:
ci.ymlnow runskoverXmlReportand uploads the Kover XML (build/reports/kover/report.xml, verified 156 KB) to Codecov (non-blocking, tokenless-ok). - SonarQube Cloud:
sonar-project.properties(Kotlin, scoped tosrc, Kover coverage import), documented for Automatic Analysis (default) or a CI scan (for in-Sonar coverage). - Naming fix:
SECURITY.mdno longer used the forbidden bareDespawnedItemsform (→ "PaperMC Despawned Items" /plugins/papermc-despawned-items/). - Docs subnav restructured to match
random-ai-prompt(owner-flagged gap): added an Overview pill, broke the single Notes door into Project Notes / Systems / Reference (with new generated section landing pages —renderDocsSiteemitsnotes/<section>/index.htmlfor each non-empty notes section), and added a Notes ↗ GitHub link beside Repository ↗. Both chrome surfaces updated (_shell.htmlhand pages +header.ftlDokka API pages); verified rendering on both + all links resolve. - Raised to the fairyfox hub:
notes/fairyfox-reports/2026-07-21-subnav-standard-proposal.mdproposes making the docs-site subnav standard adaptive (Overview + Project Notes + a door per notes section + Notes ↗) so mesh nodes stop diverging, with a reference implementation and a proposed conformance check.
Needs owner-side external setup for the account-gated badges to go live (Codecov app +
CODECOV_TOKEN; SonarCloud project import + SONAR_TOKEN; CodeFactor repo add; optional
SCORECARD_TOKEN). ./gradlew build + koverXmlReport green; doc-link gate + workflow
YAML validated. VERSION → 1.3.7.
2026-07-21 — Release v1.3.6 (PATCH): self-hosted legal pages + Legal subnav item
Fairy Fox caught the legal pages missing. Two findings: (1) the legal-docs standard is
mandatory for every repo ("every repo ships the three pages, self-hosted") — the
adoption manifest's "N/A — hub serves /legal/" was another wrong lenient read, and the
chrome footer's …/legal/{privacy,terms,cookies}/ links had been 404ing on the live
site since the chrome adoption; (2) the owner wants Legal reachable from the subnav.
- Three self-hosted, code-accurate legal pages (from
hub/templates/legal/, rewritten to reality): no accounts/analytics/cookies; reader-prefs + coins local storage disclosed as device-only with the in-app Reset / Clear-my-data controls; coins no-monetary-value + sharedfairyfox.io/legal/coins/explainer linked; GitHub Pages named as processor; Google Fonts IP exposure flagged; plugin-side reality stated (despawn-location records live in the operator's storage; no telemetry); Apache-2.0 "as is" terms;legal@fairyfox.iocontact; Last updated 2026-07-21. Emitted atlegal/<slug>/index.htmlso the footer's pretty URLs resolve, plus alegal/index page. - Legal subnav item added on every page (hand + API) — an owner-directed addition
to the canonical centre set, recorded in
decisions/architecture.md; sanctioned by legal-docs rule 4 (primary-menu placement). - Manifest legal-docs row corrected N/A → implemented; CLAUDE.md notes-maintenance table gains the legal-docs trigger (data-practice change → update pages + date).
VERSION → 1.3.6. Docs-site only.
2026-07-21 — Release v1.3.5 (PATCH): notes README pages rendered — live 404s fixed
Live-deploy verification of v1.3.4 (fetching the served pages, not trusting the build)
found the notes landing linking sessions/README.html / fairyfox-reports/README.html
— pages the renderer had excluded → 404 on the live site (a no-orphan/quality-bar
violation). Section READMEs are now rendered (links resolve) while staying
out of the sidebar (docs-site 06: the sidebar lists notes, not section overviews).
VERSION → 1.3.5. Docs-build only.
2026-07-20 — Release v1.3.4 (PATCH): docs site rebuilt to the canonical shape — overview landing, Notes/Tutorials/Changelog pages, API boundaried under /api/
Fairy Fox caught that v1.3.3's subnav "fix" had satisfied the letter by shrinking the subnav to what existed instead of building what the standard expects — and that the site's default page was the raw Dokka module page. Rebuilt as docs-site Case A (hand-authored site, generator boundaried):
- Overview landing is the default page (
index.html) — pitch, pipeline, features, section cards; the left subnav pill is now a real overview, not the API root. - Full canonical subnav everywhere:
[PaperMC Despawned Items] · Notes · Tutorials · Changelog · API · Download · [Repository ↗]— every centre link a real, chrome-wearing page; per-page.active+aria-current. - Notes rendered on the site (docs-site 06 shape): single Notes door, landing =
root-note intro, left sidebar listing every note grouped by section (READMEs
excluded), 44 pages, note pages carry
data-read(reading controls live), the landing does not. - Tutorials (getting started / commands / permissions / config,
data-read) and Changelog (generated at build fromnotes/version/*.md— cannot go stale) pages added. - API boundaried under
/api/: the Dokka tree moved off the site root, reached via the subnav API item (active on every generated page), way-back links to the overview (homepageLink+ subnav); generator sidebar/footer rules re-verified on the new output. - Fox brand icon vendored (
fox.pngat site root + api tree) — the last hot-link to fairyfox.io removed (self-hosted-assets gap #94 closed). - Build: new
renderDocsSite(flexmark md→HTML, shell + fragments) +assembleDocsSite→build/docs-site; docs.yml publishes that. Verification caught and fixed a malformed home-pill attribute before commit.
VERSION → 1.3.4. Docs/build/workflow only — no plugin behaviour change.
2026-07-20 — Release v1.3.3 (PATCH): full hub-standards audit — nav/subnav corrected, Downloads page, mandatory branch protection, provenance attestation
Ran the complete hub-standards audit (100+ items, notes/plans/standards-audit-2026-07-20.md)
and applied every gap under the standing adopt-standards-by-default grant:
- Docs chrome corrections (docs-site standard 05/08): primary nav now marks
Projects active (a sub-project is always under Projects — the earlier "Docs active"
was wrong); subnav canonicalized to
[project name] · API (active) · Download · [Repository ↗]— the invented "Guide" item and raw-GitHub centre links removed. - Real Downloads page (
downloads.html, required since we ship release jars): chrome-wearing, perma-latest section onreleases/latest, install notes, all-releases link; vendored into the docs root byvendorChromeAssets. - Release workflow hardened:
mainbranch-protected (solo config — require PR, 0 approvals, strict checks, enforce-admins),devdeletion/force-push protected, branch auto-delete on. Releases now go through the PR path; directdev → mainpushes are retired.release.ymlgains build-provenance attestation (supply-chain-hardening §4/§5). - Repo hygiene gates:
scripts/check-links.mjsdoc-link gate wired into CI;scripts/check-tidy.mjsfor session-end tidiness. - 17 hub standards adopted into
notes/reference/(agent-tooling, planning, process-reports, testing, repo-hygiene, compliance, …); 4 recorded N/A with reasons.
VERSION → 1.3.3. No plugin behaviour change — docs, CI/release workflow, and
repo/notes meta only.
2026-07-20 — Release v1.3.2 (PATCH): docs site wears the shared fairyfox chrome (bundle v2.2.1)
The Dokka API docs now carry the shared fairyfox chrome bundle (VERSION 2.2.1) instead of being a bare generator island — the masthead (Home · Projects · Farms · Docs · Updates · About), this project's subnav (overview · Guide · Changelog · Download · Repository), the reader "Aa"/coins controls, and the shared footer, on every page.
Injected through Dokka's FreeMarker templatesDir (the Doxygen-style "full-page generator"
adapter): docs-theme/dokka-templates/includes/{page_metadata,header,footer}.ftl paste the
bundle's head/header/subnav/footer verbatim with only the FF_* slots filled. The four
master assets (main.css, reader.js, nav.js, coins.js) are vendored under
docs-theme/chrome/ and copied into the docs root at build time (vendorChromeAssets),
referenced per-page via ${'$'}{pathToRoot} so they resolve at any depth — vendored, never
hot-linked, renders with fairyfox.io offline. "Wear the chrome, boundary the reference":
Dokka's API body stays Dokka, harmonised to the palette by dokka-fairyfox.css. Verified
in-browser at root + a deep package page; full build green. Deviations (footer in the
content column; Dokka's own bar kept as API controls) recorded in decisions/architecture.md.
VERSION → 1.3.2. No plugin behaviour/artifact change — docs-only.
2026-07-20 — Release v1.3.1 (PATCH): package namespace → io.fairyfox.papermc.despawneditems
Moved the Kotlin source out of com.popupmc.despawneditems into
io.fairyfox.papermc.despawneditems and set the Gradle group to io.fairyfox
to match. The plugin.yml main: now points at
io.fairyfox.papermc.despawneditems.PaperMcDespawnedItems. ktlint import ordering
re-sorted automatically (io.fairyfox sorts differently than com.popupmc did).
No behaviour, command, config, permission, artifact-name, plugin-id, or data-folder
change — for a server admin the drop-in jar is identical (papermc-despawned-items-*.jar,
plugin id papermc-despawned-items). This is purely the internal package/Maven namespace.
VERSION → 1.3.1. Full build green (ktlint + detekt + kover + tests).
2026-07-20 — Release v1.3.0: consistent naming + real static analysis & coverage
Addressed three follow-ups, none of which cut corners:
- Naming made consistent — the built artifact,
plugin.ymlname(Bukkit plugin id), and data folder are now allpapermc-despawned-items; the Kotlin main class isPaperMcDespawnedItems; the human-facing name stays "PaperMC Despawned Items". No more bareDespawnedItems. (Breaking for anyone who installed ≤1.2: renameplugins/DespawnedItems/→plugins/papermc-despawned-items/.) - Kover coverage now works — its plugin marker lives on Maven Central, not the Gradle
portal, so
pluginManagementnow includesmavenCentral()and Kover 0.9.9 (Kotlin-2.4 compatible) generates a coverage report.koverXmlReportgreen. - Detekt rules re-enabled and the code actually fixed — reverted the earlier mistake of
disabling
MagicNumber/ReturnCount/etc. All default rules are on with no baseline: every magic number extracted to a named constant,isHazardousrefactored from a complexity-48 boolean chain to an O(1)Set, empty overrides made explicit, the JDBC catch narrowed toSQLException, an unused parameter removed, and the few genuinely-branchy strategy methods carry a narrow@Suppress+ reason. The only config overrides are ktlint-aligned line length and documented guard-clause/facade thresholds.
VERSION → 1.3.0. Ktlint + Detekt + Kover all gate the build and CI.
2026-07-20 — Release v1.2.0 (MINOR): Brigadier commands + quality gates
Milestone since 1.1.1: /despi and /recycle rewritten on Paper's Brigadier command
API (typed args, permission-scoped visibility, native tab-completion; logic in testable
DespiActions/RecycleAction) — verified under MockBukkit (dispatch) and enabling on real
Paper 1.21.11. Added Ktlint and Detekt quality gates to the build (JDK-21 daemon
pin so detekt runs; tuned config + baseline). Minor /despi syntax tweaks noted below.
VERSION → 1.2.0.
2026-07-20 — Add Ktlint + Detekt quality gates; format the codebase
Adopted two static-quality tools and wired them into build (they run as part of
check):
- Ktlint (formatting + Kotlin style) — formatted the whole codebase to a consistent
style;
.editorconfigsets line length 140 and enables no-wildcard-imports. - Detekt (static analysis: complexity, smells, potential bugs) —
config/detekt/detekt.ymllayers on the defaults (line length 140,MagicNumber/ReturnCounttuned for the branchy despawn strategies); a 12-entry baseline (config/detekt/baseline.xml) accepts pre-existing findings so detekt gates only new issues.
Detekt 1.23.8 can't run on a JDK 25 daemon, so gradle/gradle-daemon-jvm.properties pins
the Gradle daemon to JDK 21 (foojay-provisioned) — which also makes the whole build
run on the plugin's real target JVM. Kover (coverage) was attempted but is currently
blocked (0.9.1 is Kotlin-2.4-incompatible; 0.9.8's plugin marker isn't on the portal) —
deferred until a working release. Build green with both gates + tests + jar.
2026-07-20 — Modernize commands to Paper's Brigadier API
Replaced the legacy plugin.yml command + hand-rolled string parsing with Paper's modern
Brigadier command API, registered via the lifecycle event system. /despi and
/recycle are now proper command trees with typed arguments, permission-scoped
visibility (.requires), native tab-completion, and client-side validation.
Structure: all behaviour lives in a testable DespiActions (and RecycleAction);
DespiCommand builds the tree and registers it; CommandFeedback holds shared helpers.
Removed the old framework (OnDespiCommand, AbstractDespiCommand, DespiSubcommandsA/B,
OnRecycleCommand) and the plugin.yml commands: block. MockBukkit supports the
lifecycle command API, so the enable test still passes and a new test dispatches /despi
end-to-end through the registration. Minor syntax tweaks: clear player <name> (was
clear <name>) and despawn create-material-amount <name> <amt> alongside
create-material <names> (Brigadier can't follow a greedy string with an int).
2026-07-20 — Release v1.1.1 (PATCH): regression pins + 26.1 forward-compat verified
Post-1.1.0 hardening release. Pinned regression tests for the fixed bugs (recycle reward,
particle data) by extracting RecycleProgress and ParticleData as pure units
(behaviour unchanged), and verified forward-compat by booting the jar on a real Paper
26.1.2 server (enables cleanly, no registry-change breakage). No user-facing behaviour
change. VERSION → 1.1.1.
2026-07-20 — Forward-compat verified on real Paper 26.1.2
Booted a headless Paper 26.1.2 server with the 1.1.0 jar (built against 1.21.11) and
confirmed the forward-compat claim: the api-version: '1.21' descriptor is accepted, the
libraries: (HikariCP + JDBC drivers) load, and onEnable completes cleanly —
DespawnedItems is enabled, Done (10.3s)!, no exceptions despite the 26.x registry
changes (Material iteration in RewardPool, Particle.valueOf in ParticleData, etc.
all work). Closes the last verification gap; the README's "loads on 26.x" is now proven.
2026-07-20 — Regression tests for the fixed bugs (§87): recycle reward + particle data
Post-1.1.0 hardening. Per the testing standard, every fixed bug now has a permanent,
pinned test. Extracted the two bug-prone pieces into pure, server-independent units and
tested them: RecycleProgress (the "reward every 64 recycles" threshold that used to
never fire) and ParticleData (resolves a particle + its required data safely, falling
back with a warning instead of crashing for DUST/BLOCK/ITEM/unknown keys). 12 new pure
tests. Also a small readability win — /recycle and FileConfig now delegate to these
units instead of inlining the logic.
2026-07-20 — Release v1.1.0 (MINOR): full refactor to a modern, tested, scalable plugin
Cut the v1.1.0 milestone from the 2026-07-20 refactor work below. Highlights since
1.0.2: retargeted to Paper 1.21.x / Java 21; fixed real bugs (/recycle rewards,
data-bearing particles, stale static strategy list, RemoveMaterials IOOBE, /despi reload); indexed LocationStore with O(1) lookups; pluggable YAML / SQLite /
MySQL storage with incremental off-thread persistence and migration; throttled
DespawnScheduler for large-server safety; per-user location limits via
despi.limit.<n> group permissions; a JUnit 5 + MockBukkit test suite (~49 tests, CI
green) plus a real-server smoke pass; rewritten README, filled context notes, and a
GitHub Pages Dokka workflow. VERSION → 1.1.0; release.yml/ci.yml on Java 21.
2026-07-20 — Runtime-verified on real Paper 1.21.11 (headless smoke)
Booted a headless Paper 1.21.11 server with the built jar and confirmed a clean
enable: Paper's library loader auto-downloaded and loaded HikariCP 5.1.0, sqlite-jdbc
3.49.1.0, and mariadb-java-client 3.5.3 from Maven Central, then
DespawnedItems is enabled with no stack traces (Done (23.16s)!). The server logged it
was "2 releases behind 26.1.2" — confirming the 1.21.11-vs-26.1 target. The smoke also
caught (and corrected) that VERSION is 1.0.2 (status.md had said 1.0.0) and that the
distributable jar is DespawnedItems-1.0.2.jar. Forward-compat load on an actual 26.1
server is still to verify.
2026-07-20 — Fill in the context notes (project/architecture/principles/overview)
Phase 4 docs. Filled the previously-templated notes/context/{project,architecture, principles}.md and notes/systems/overview.md to describe the current architecture
(the location store/manager/repository seams, the throttled pipeline, limits), the
repo layout, the data-integrity/scale/fail-safe principles, and the end-to-end system
map. Refreshed notes/status.md to reflect the largely-complete refactor.
2026-07-20 — GitHub Pages docs workflow; CI on Java 21
Phase 4. Added .github/workflows/docs.yml — builds the Dokka API docs and deploys them
to GitHub Pages on every release to main (SHA-pinned Pages actions; dokkaGenerate
verified locally, output build/dokka/html). Enabled Pages with the GitHub-Actions build
source. Also fixed ci.yml to use Java 21 (was still 25 after the retarget); the CI
build already runs the full test suite.
2026-07-20 — Rename display name to "PaperMC Despawned Items"; rewrite README
Phase 4. Set the human-facing/listed name to PaperMC Despawned Items in the README
title, Dokka moduleName, and CLAUDE.md heading. The repo slug
(papermc-despawned-items) and the Bukkit plugin id (DespawnedItems) are unchanged —
the latter can't contain spaces. Also set plugin.yml author to Fairy Fox. Rewrote the
README to cover the 1.21.x target (loads on 26.x), the storage backends, per-user limits,
performance/scaling, updated commands (dropped indexes), and the test suite.
2026-07-20 — Per-user location limits with permission/group tiers
Phase 3. Players self-manage their own despawn locations up to a cap. DespawnLimits
resolves the cap as: limits.unlimited / despi.limit.bypass → unlimited; else the
highest despi.limit.<n> permission the player holds (assign these to LuckPerms groups
for per-rank caps); else limits.default (config). /despi add this enforces it for
self-service adds, while admins adding for others (already behind despi.elevated) are
uncapped. New limits: config section, LimitSettings, and the despi.limit.bypass
permission node. Tests: settings parsing/boundary (3) + a full MockBukkit permission
matrix (6). 49 tests green.
2026-07-20 — Throttled despawn scheduler (large-server safety)
Phase 1 scaling, final core piece. The old model started a chunk-loading DespawnProcess
immediately for every despawning item with no upper bound — a flood of despawns could
storm the server. Added DespawnScheduler: despawning items are enqueued and a
once-per-tick drain starts at most performance.max-per-tick new relocations, never
exceeding performance.max-concurrent in flight, with the queue capped at
performance.max-queue and a configurable drop policy. New performance: config section
and PerformanceSettings. The ItemDespawnEvent listener now enqueues; /despi despawn count-ongoing reports active + queued. Tests: config parsing/boundary (3) + MockBukkit
enqueue/drain (2). 40 tests green.
2026-07-20 — Wire the new store + storage into the live plugin
Phase 1 integration. The plugin now actually uses the indexed store and pluggable
storage instead of the old flat list: a new LocationManager (on plugin.locations)
owns the LocationStore + configured LocationRepository, exposes all the read/mutate
operations the pipeline and commands need, and persists incrementally and off-thread
(a mutation marks its owner dirty and debounces a 5s async flush; disable//despi save
flush synchronously). StorageFactory builds the yaml/sqlite/mysql backend from config
and auto-migrates yaml → database on first switch. New storage: config section.
Rewired the despawn pipeline (DespawnProcess draws from the manager and uses an
instance-scoped strategy list — fixing the old static list that captured the first
plugin instance across reloads), decoupled DespawnEffect to take a plain Location,
and updated RemoveMaterials (also fixed a latent index-out-of-bounds when a target
owner had no locations). Updated every /despi subcommand to the manager; removed the
now-obsolete indexes subcommand. Deleted FileLocations, LocationEntry,
DespawnIndexes. Also fixed /despi reload to actually re-read config.yml from disk
(it reused Bukkit's cached config before). Added a MockBukkit LocationManager
integration test (add/remove/query + save-and-reload). 35 tests green; shaded jar builds.
2026-07-20 — Adopt the Kotlin-Paper testing checklist as the project standard
Added notes/plans/testing.md: adopts the comprehensive Kotlin-PaperMC testing checklist
as this project's testing standard, maps its 95 sections onto this plugin (applicable /
partial / N-A with status), sets the testing-pyramid targets and source organisation, and
records the tool stack. A living document the suite is grown against. Current suite: 31
tests across unit, serialization/fuzz, database (SQLite), performance, and MockBukkit.
2026-07-20 — Pluggable storage backends (YAML + SQLite + MySQL/MariaDB) + perf tests
Phase 1 scaling. Turned persistence into a LocationRepository interface with two
implementations: YamlLocationRepository (the flat userdata/<uuid>.yml files —
default, zero-config, unchanged on-disk format, now writing incrementally per owner
instead of rewriting every file on every change) and a dialect-agnostic
JdbcLocationRepository for SQLite (embedded) and MySQL/MariaDB (network-shared),
with transactional per-owner writes. JDBC drivers + HikariCP load at runtime via Paper's
libraries: loader, so the jar stays small.
Tests: YAML roundtrip/resilience/incremental (5), JDBC-over-SQLite CRUD/replace/persist (4), and a performance-guardrail suite that fails the build if the store's inserts, lookups, random draw, or bulk removal regress from O(1)/O(log n) at 100k+ entries (3). 31 tests green.
2026-07-20 — Indexed location data model (DespawnLocation + LocationStore)
Phase 1 scaling foundation. Added an immutable, server-independent DespawnLocation
value type (world name + block coords + owner; x;y;z;world serialisation kept
byte-compatible with the old on-disk format) and a LocationStore that replaces the
old flat ArrayList — the one that was copied and linearly scanned on every query
and rewritten to disk in full on every change. The store gives O(1) add/remove/contains,
a spatial index (block → owners), an owner index (owner → locations, for per-user
limits and bulk ops), an O(1) swap-remove flat bag for uniform random draw, and
dirty-owner tracking so persistence can write only what changed.
Both types are pure and fully unit-tested (roundtrip + fuzz for the value type; add/remove/index-integrity + a 5,000-op randomized integrity fuzz for the store). 19 tests green. Additive for now — wiring the store into the plugin comes next.
2026-07-20 — Stand up the test harness (JUnit 5 + MockBukkit), first tests green
Phase 2 foundation, brought online early so the refactor is tested as it lands. Added
JUnit 5 + MockBukkit (mockbukkit-v1.21:4.110.0) to the build; test uses the JUnit
platform and build runs it. First suite (PluginEnableTest) boots a mocked Paper
1.21 server, loads the plugin, and asserts it enables cleanly, both commands
register, and the reward pool is populated/filtered — all green.
Two changes were needed to make the plugin testable, both improvements in their own
right: DespawnedItems is now open (Kotlin classes are final; MockBukkit subclasses
the main class via ByteBuddy), and RewardPool filters by stable enum name instead of
calling server-backed Material.isAir() (which routed through the block registry —
fragile and slower; MockBukkit can't mock the legacy path at all).
2026-07-20 — Fix fragile particle handling (data-bearing particles crashed)
Phase 1 correctness fix. The effect always called spawnParticle with no data,
so any configured particle that requires data (DUST, BLOCK, ITEM, …) threw at spawn
time. Now the particle's data requirement is resolved and validated once at config
load: data-less particles pass null, DUST-style particles are built from new optional
particles.color (#RRGGBB) and particles.dust-size keys, and any particle needing
data the plugin doesn't model is swapped for the happy_villager default with a
console warning instead of crashing. Unknown particle keys warn and fall back too.
Build green on 1.21.11.
2026-07-20 — Fix silently-broken /recycle rewards; rename reward pool
Phase 1 (refactor) correctness fix. /recycle tracked its reward progress in three
scoreboard objectives (recycleCountPart/recycleCount/recycleCountPaid) that the
plugin never created, so on any normal server the reward path hit a null objective and
silently granted nothing. Rewrote it to store progress in the player's
PersistentDataContainer — persists across sessions, needs no scoreboard setup, and
actually works. Behaviour preserved: one random reward every 64 recycles.
Also renamed the misnamed BlacklistedItems object to RewardPool (it is the
allow-list of reward-eligible materials, not a blacklist) and tidied its filtering.
Build green against Paper 1.21.11.
2026-07-20 — Retarget Paper 26.1 → 1.21.x (Java 21) + refactor plan
Kicked off a full refactor/modernisation/testing/scale-up effort (plan:
notes/plans/refactor-2026-07.md). First step: retargeted the build from Paper
26.1 / Java 25 to Paper 1.21.11 / Java 21. Rationale — the MockBukkit test
framework supports the 1.21.x line but not the newer 26.x line yet, so 1.21.x
unlocks full integration/e2e testing; 1.21.11 is also the single most-installed
version (~25% of tracked public servers, ~47% for the whole 1.21.x line) and a
1.21-built plugin still loads on 26.1 servers via Paper forward-compat (to verify).
Bonus: Mineflayer supports 1.21.11, so in-game client automation is now possible.
Changes: build.gradle.kts Paper API → 1.21.11-R0.1-SNAPSHOT, Kotlin toolchain
and jvmTarget → 21; settings.gradle.kts adds the foojay resolver (1.0.0, the
Gradle-9-compatible release) to auto-provision JDK 21; plugin.yml api-version →
1.21; CLAUDE.md and notes/status.md updated. ./gradlew build is green against
the real 1.21.11 API — the existing code compiled with zero API changes.
2026-07-20 — Project icon
Added the project icon (assets/icon.png, 1024×1024, transparent) to the repo: shown
in the README header and wired as the Dokka docs header logo (via customAssets).
Released as v1.0.2. The GitHub social-preview image still needs a one-time manual
upload in repo Settings — gh/the API can't set it.
2026-07-19 — Make the Gradle wrapper executable for CI
Set the executable bit on gradlew so the Linux GitHub Actions runners can invoke it.
Committed from Windows, the wrapper landed non-executable and CI failed with
./gradlew: Permission denied (exit 126). Released as v1.0.1.
2026-07-19 — Kotlin/Paper 26.1 rewrite + fairyfox mesh onboarding
Modernised the plugin and folded it into the fairyfox mesh in one pass.
Rewrote the entire plugin from Java 11 / Maven / Paper 1.16.5 to Kotlin / Gradle /
Paper 26.1 on Java 25: all ~30 classes (the five-stage despawn pipeline, every
/despi subcommand, /recycle, config and per-player location storage) ported to
idiomatic Kotlin. Modernised churned APIs — ChatColor → Adventure components,
Effect/playEffect → a sound key via playSound(String, …), Particle.VILLAGER_HAPPY
→ a resolved particle key, dropped removed MaterialData calls, and switched
overloaded-setter properties to explicit setX(...) calls. Reconciled the plugin's
own config holder to plugin.settings to avoid clashing with JavaPlugin.getConfig(),
and reconciled plugin.yml permissions to match the code. The build shades the Kotlin
stdlib into a single runnable jar and is wired for Dokka docs and Hangar publishing.
Verified ./gradlew build green against the real Paper 26.1.2 API, and confirmed the
built jar loads and enables cleanly on a headless Paper 26.1.2 / Java 25 server.
Adopted the fairyfox standards: mesh-aware CLAUDE.md, the notes/ living-notes
system, VERSION seeded at 1.0.0, LF .gitattributes, SECURITY.md, Dependabot,
and SHA-pinned CI / branch-sync / release workflows, plus a reconciled README with the
badge block.