diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..39bbcbf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +# spigot-getgud agent entrypoint + +The canonical stories, engineering guidance, and **all process documents** are in the private [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/index.md). + +Before work, read the sibling `../somc-okf/index.md`, `../somc-okf/processes/index.md`, `../somc-okf/projects/spigot-getgud/index.md`, `engineering.md` in that project section, and relevant `../somc-okf/user-stories/spigot-getgud/` stories. Also follow the parent workspace `AGENTS.md` when present. + +For standalone checkouts, start at the [project page](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/spigot-getgud/index.md) and [shared process](https://git.garvis.dev/dmg/somc-okf/src/branch/main/processes/development.md). Obtain wiki access before feature work; do not recreate a local knowledge bundle. Source builds do not require private wiki access. + +Development follows [Development cycle](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/development-cycle.md): approved stories, failing tests, passing implementation, verification, then source/wiki commit and push. GitOps updates are committed locally **without pushing**; only [Do release](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/do-release.md) authorizes a reviewed GitOps push. diff --git a/README.md b/README.md index c90affc..ebb4dd0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Leaf is a Spigot 26.2 plugin that lets non-aggressive players opt into visible Resistance and Strength protection. -The approved behavior is specified in the [OKF design bundle](design/index.md). +The approved behavior is specified in the [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/spigot-getgud/index.md). ## Requirements diff --git a/design/index.md b/design/index.md deleted file mode 100644 index 7873332..0000000 --- a/design/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -okf_version: "0.1" ---- - -# Leaf Design - -This bundle documents Leaf, a Spigot plugin that lets players opt into visible Resistance and Strength boosts while requiring them to relinquish that protection when they attack another player. - -## Explore - -- [User stories](user-stories/index.md) -- [Design log](log.md) diff --git a/design/log.md b/design/log.md deleted file mode 100644 index af7514e..0000000 --- a/design/log.md +++ /dev/null @@ -1,105 +0,0 @@ -# Leaf Design Log - -## 2026-08-10 - -### Initial protection design - -- Leaf is available to any player who opts in; the seven-day period applies only to onboarding reminders. -- Protection grants Resistance I by default and remains active until the player opts out, attacks another player, an administrator disables it for the player, or Leaf is globally disabled. -- Receiving an attack does not remove protection, but retaliating does, including attributable Thorns damage. -- PvP attribution covers melee, projectiles, potions, owned animals, Thorns, and indirect damage when Spigot exposes a reliable responsible player. -- Opted-in players receive a leaf prefix in tab, overhead names, and chat where supported. -- New players receive an onboarding reminder on every login for seven calendar days after first joining. -- Global disablement suppresses effects, prefixes, and reminders without erasing player choices. -- Administration uses the permission-aware `/leaf` command tree with autocomplete. -- Builds and releases follow the neighboring Spigot Base Gradle and Gitea pipeline. -- The design bundle is committed together; later implementation work is committed separately for each user story. - -### Implementation started - -- Established the Java 17 Gradle project, strict compilation, JUnit test lifecycle, Leaf plugin metadata, Gradle wrapper, and Gitea CI and semantic-release workflows. -- Verified the initial pipeline with `./gradlew clean check jar` and produced `build/libs/leaf-0.1.0-SNAPSHOT.jar`. -- US-007 remains in progress until the story-by-story implementation commit requirement has been verified. - -### Configuration and persistence checkpoint - -- Added validated settings with Resistance I and seven-day onboarding defaults. -- Added defensive UUID-keyed YAML player state with RFC 3339 timestamps, forward-field preservation, and atomic replacement where supported. -- Added safe plugin initialization and periodic dirty-state persistence. -- US-006 remains in progress pending live runtime setting commands and Leaf effect ownership behavior. - -### US-001 player protection completed - -- Added permission-aware player commands with idempotent choice changes and detailed status reporting. -- Added immediate durable opt-in persistence, join-time restoration, and quiet infinite Resistance reconciliation. -- Leaf tracks its live Resistance fingerprint and conservatively preserves a visibly distinct Resistance effect. -- Verified player choice, lock, persistence, command, autocomplete, and build behavior with `./gradlew clean check jar`. - -### US-003 identity presentation checkpoint - -- Added configurable, color-compatible prefixes to standard chat display names and player-list names. -- Added conservative main-scoreboard team prefixes for overhead names without displacing unrelated teams. -- Name cleanup restores only values Leaf installed; third-party changes are preserved. -- Documented scoreboard, custom chat, tab-list, and per-viewer scoreboard limitations. -- US-003 remains in progress until combat and global administration paths are verified. - -### US-002 combat opt-out completed - -- Added modern Spigot damage-source attribution for direct attacks, projectiles, thrown potions, area clouds, tamed animals, TNT, Thorns, and other reliably attributed sources. -- Ambiguous, cancelled, zero-damage, environmental, and self damage do not opt out a player. -- Combat opt-out bypasses administrative locks, immediately reconciles effect and prefix state, persists once, and sends one explanatory message. -- Verified attribution, victim safety, lock bypass, duplicate suppression, and the full build with `./gradlew clean check jar`. - -### US-004 onboarding completed - -- Join observation now sends the configurable welcome message on each login before the original first-join instant plus the configured calendar-day window. -- The exact expiration boundary and globally disabled behavior suppress reminders without resetting first-join state. -- The default message names all player commands and explains automatic combat opt-out. -- Verified repeated joins, expiration, global suppression, first-join retention, and the full build with `./gradlew clean check jar`. - -### US-005 administration and US-003 identity completed - -- Added the complete `leaf.admin` command tree for global state, live strength, player status, saved choice, and locks. -- Runtime global and strength changes persist before activation and immediately reconcile all online players. -- Target resolution prefers exact online/current names, retains prior names, accepts UUIDs, and rejects ambiguous reused names. -- Added position-specific administrative autocomplete and mutation/no-change reporting with validation before mutation. -- Verified global effect and prefix suppression/restoration, live strength, targets, locks, command permissions, completion, durable aliases, and the full build with `./gradlew clean check jar`. - -### US-006 configuration and persistence completed - -- Runtime global and Resistance-level changes are written through Bukkit configuration before becoming active and survive restart. -- Failed runtime setting persistence leaves the active settings unchanged. -- Leaf records only effects it successfully installs, removes them only while the visible fingerprint matches, and preserves distinct Resistance effects; Spigot's identical-effect ownership limitation is documented. -- Durable player state now retains prior names in addition to the required UUID, latest name, choice, lock, and RFC 3339 first-join timestamp. -- Verified settings failure safety, durable aliases, existing defensive YAML behavior, strict compilation, and the full build with `./gradlew clean check jar`. - -### US-007 build and release completed - -- Verified dedicated conventional implementation commits for protection, identity, combat, onboarding, administration, configuration verification, and logout cleanup in addition to the existing build foundation. -- Kept reserved OKF index and log documents in their specification-defined structures. -- Verified all user stories are done, all acceptance criteria are checked, the repository has no remote, and no push was performed. -- Final verification passed with `./gradlew clean check jar` and `okflint` validation. - -### US-003 sticky-prefix regression fixed - -- Reproduced the cleanup failure that occurred when another plugin changed a Leaf-decorated display name. -- Leaf now removes exact managed-prefix text without discarding surrounding third-party formatting, clears stale prefixes after reload, and normalizes repeated prefixes before applying one leaf. -- Added regression coverage for wrapped names, duplicate stale prefixes, untracked stale prefixes, unrelated formatting, tab names, and scoreboard cleanup. -- Verified the fix with `./gradlew clean check jar`. - -### Strength protection added - -- Opted-in players now receive quiet, infinite Strength I alongside Resistance I by default; both effects must be active for Leaf status to report active protection. -- Added independent `resistance-level` and `strength-level` settings with live `/leaf effect <1-5>` administration and positional autocomplete. -- Retained `/leaf strength <1-5>` as a deprecated compatibility alias for changing Resistance. -- Opt-out, combat, administrative disablement, and global disablement remove both Leaf-managed effects while conservatively preserving distinct external effects. -- Minecraft calculates the initiating PvP hit before Leaf can process the damage event, so that first hit can include the configured Strength bonus before automatic opt-out. -- Verified settings, runtime reconciliation, status, combat cleanup, persistence, command compatibility, autocomplete, and the complete build with `./gradlew clean check jar`. - -### Automatic effect recovery added - -- Leaf now reconciles opted-in online players every 20 ticks so death, milk, commands, and plugins cannot permanently clear configured protection. -- Recovery remains disabled for opted-out players and while Leaf is globally disabled. -- A temporary external Resistance or Strength effect is preserved instead of overwritten; the leaf prefix remains visible and Leaf restores its configured effect after the external effect ends. -- Added regression coverage for the one-second recovery task and eligibility-aware online-player reconciliation. -- Verified the complete build with `./gradlew clean check jar`. diff --git a/design/user-stories/index.md b/design/user-stories/index.md deleted file mode 100644 index d007697..0000000 --- a/design/user-stories/index.md +++ /dev/null @@ -1,9 +0,0 @@ -# Leaf User Stories - -1. [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -2. [US-002: Relinquish protection when attacking](us-002-relinquish-protection-when-attacking.md) -3. [US-003: Identify protected players](us-003-identify-protected-players.md) -4. [US-004: Introduce new players to Leaf](us-004-introduce-new-players-to-leaf.md) -5. [US-005: Administer Leaf](us-005-administer-leaf.md) -6. [US-006: Configure and persist Leaf](us-006-configure-and-persist-leaf.md) -7. [US-007: Build and release Leaf](us-007-build-and-release-leaf.md) diff --git a/design/user-stories/us-001-opt-into-leaf-protection.md b/design/user-stories/us-001-opt-into-leaf-protection.md deleted file mode 100644 index 0134bfc..0000000 --- a/design/user-stories/us-001-opt-into-leaf-protection.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -type: User Story -title: "US-001: Opt into Leaf protection" -description: Let players voluntarily receive and relinquish a persistent Resistance boost. -status: done ---- - -# US-001: Opt into Leaf protection - -As a **player**, I want to opt into Leaf protection so that I can receive a modest defensive boost while playing on a hard server. - -## Acceptance criteria - -- [x] Players with the `leaf.use` permission, granted by default, can use `/leaf on`, `/leaf off`, and `/leaf status`. -- [x] `/leaf on` records the player's opt-in choice and grants Resistance I while Leaf is globally enabled. -- [x] Resistance remains continuously effective without distracting expiry or renewal messages or particles. -- [x] `/leaf off` records the player's opt-out choice and immediately removes only the Resistance effect managed by Leaf. -- [x] `/leaf status` clearly distinguishes the player's saved choice, active protection, administrative lock, and global Leaf state. -- [x] Repeating an already-satisfied `on` or `off` command is safe and explains that no change was needed. -- [x] Opt-in choices are keyed by UUID and survive logout and server restart. -- [x] A player who joins while opted in regains protection when Leaf is enabled. -- [x] Player-command autocomplete suggests only valid next arguments available to the sender. -- [x] Opted-in players receive quiet Strength I alongside Resistance I by default. -- [x] Player status reports active protection only when both configured Leaf effects are effective. -- [x] Missing Leaf effects are restored for opted-in online players within approximately one second after death, milk, commands, or plugins clear them. -- [x] Recovery remains suppressed while Leaf is globally disabled or the player is opted out. - -## Related - -- [US-002: Relinquish protection when attacking](us-002-relinquish-protection-when-attacking.md) -- [US-003: Identify protected players](us-003-identify-protected-players.md) -- [US-005: Administer Leaf](us-005-administer-leaf.md) diff --git a/design/user-stories/us-002-relinquish-protection-when-attacking.md b/design/user-stories/us-002-relinquish-protection-when-attacking.md deleted file mode 100644 index f053417..0000000 --- a/design/user-stories/us-002-relinquish-protection-when-attacking.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -type: User Story -title: "US-002: Relinquish protection when attacking" -description: Remove Leaf protection when an opted-in player attacks another player. -status: done ---- - -# US-002: Relinquish protection when attacking - -As a **player facing PvP**, I want Leaf protection to belong only to non-aggressors so that protected players cannot keep its advantage after fighting back. - -## Acceptance criteria - -- [x] When an opted-in player damages another player, Leaf automatically changes the attacker's saved choice to opted out. -- [x] Automatic opt-out immediately removes Leaf-managed Resistance and the attacker's leaf prefix. -- [x] Automatic opt-out also immediately removes Leaf-managed Strength. -- [x] The attacker receives a clear chat message explaining that attacking another player disabled Leaf and that they may opt in again when permitted. -- [x] Receiving player-caused damage without retaliating does not change the protected player's choice or Resistance. -- [x] Retaliatory damage counts as attacking, including Thorns damage attributable to the protected player. -- [x] Direct melee attacks and player-fired projectiles are attributed to the attacking player. -- [x] Harmful splash or lingering potion damage is attributed to the player who threw the potion. -- [x] Damage caused by a tamed animal is attributed to its player owner when Spigot exposes that ownership. -- [x] TNT, fire, and other indirect damage trigger opt-out only when Spigot exposes a reliable responsible player; ambiguous environmental damage does not opt out an innocent player. -- [x] PvP-triggered opt-out applies even when an administrator has locked the player's preference. -- [x] A single attack produces no duplicate state changes or duplicate notifications. - -## Related - -- [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -- [US-003: Identify protected players](us-003-identify-protected-players.md) -- [US-005: Administer Leaf](us-005-administer-leaf.md) diff --git a/design/user-stories/us-003-identify-protected-players.md b/design/user-stories/us-003-identify-protected-players.md deleted file mode 100644 index 7ac279a..0000000 --- a/design/user-stories/us-003-identify-protected-players.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -type: User Story -title: "US-003: Identify protected players" -description: Mark actively protected players with a visible leaf prefix wherever Spigot supports it. -status: done ---- - -# US-003: Identify protected players - -As a **server participant**, I want protected players to be visibly identified so that their Leaf status is clear before I interact with them. - -## Acceptance criteria - -- [x] An actively protected player has a configurable leaf prefix before their name in the player list. -- [x] An actively protected player has the same prefix before their overhead name where Spigot supports it. -- [x] An actively protected player has the same prefix in standard Spigot chat where the active chat format supports it. -- [x] The default prefix uses a leaf symbol with readable spacing and Minecraft-compatible formatting. -- [x] The prefix is removed immediately when the player opts out or is opted out by PvP or an administrator. -- [x] Prefixes are suppressed while Leaf is globally disabled and restored for online opted-in players when it is re-enabled. -- [x] Prefix updates do not overwrite unrelated display-name text where the Spigot API allows the values to coexist. -- [x] Scoreboard-team and third-party chat-plugin compatibility limitations are documented, and unsupported integrations fail without affecting protection state. -- [x] Repeated opt-in or reconciliation never produces multiple Leaf prefixes. -- [x] Opt-out removes every managed Leaf prefix while preserving unrelated name formatting. -- [x] A stale Leaf prefix left by an earlier plugin instance is normalized instead of duplicated. - -## Related - -- [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -- [US-002: Relinquish protection when attacking](us-002-relinquish-protection-when-attacking.md) -- [US-006: Configure and persist Leaf](us-006-configure-and-persist-leaf.md) diff --git a/design/user-stories/us-004-introduce-new-players-to-leaf.md b/design/user-stories/us-004-introduce-new-players-to-leaf.md deleted file mode 100644 index 13e5ae9..0000000 --- a/design/user-stories/us-004-introduce-new-players-to-leaf.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -type: User Story -title: "US-004: Introduce new players to Leaf" -description: Remind players how to control Leaf during their first seven calendar days on the server. -status: done ---- - -# US-004: Introduce new players to Leaf - -As a **new player**, I want a brief explanation of Leaf when I join so that I know protection is available and understand how to control it. - -## Acceptance criteria - -- [x] Leaf records the first observed join time for each player by UUID as an RFC 3339 UTC timestamp. -- [x] On every login before the first-join timestamp plus seven calendar days, the player receives a concise welcome message explaining Leaf protection. -- [x] The welcome message tells the player to use `/leaf on`, `/leaf off`, and `/leaf status`. -- [x] The message makes clear that attacking another player automatically opts the attacker out. -- [x] Players no longer receive the welcome message once their seven-day onboarding period expires. -- [x] Existing first-join timestamps are not reset by logout, restart, opt-in changes, or global disablement. -- [x] No onboarding reminders are shown while Leaf is globally disabled. -- [x] Re-enabling Leaf allows reminders to resume on subsequent logins only for players whose original seven-day period has not expired. - -## Related - -- [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -- [US-005: Administer Leaf](us-005-administer-leaf.md) -- [US-006: Configure and persist Leaf](us-006-configure-and-persist-leaf.md) diff --git a/design/user-stories/us-005-administer-leaf.md b/design/user-stories/us-005-administer-leaf.md deleted file mode 100644 index 9abcbbb..0000000 --- a/design/user-stories/us-005-administer-leaf.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -type: User Story -title: "US-005: Administer Leaf" -description: Give administrators command-based control over global and per-player Leaf behavior. -status: done ---- - -# US-005: Administer Leaf - -As a **server administrator**, I want to control Leaf globally and per player so that I can support players, prevent misuse, and suspend protection for server events. - -## Acceptance criteria - -- [x] Administrative commands require `leaf.admin`, which server operators receive by default. -- [x] `/leaf enabled ` globally enables or disables Leaf. -- [x] Global disablement immediately suppresses Leaf-managed Resistance, prefixes, and onboarding reminders without erasing player opt-in choices. -- [x] Global enablement immediately restores protection and prefixes for eligible online opted-in players. -- [x] `/leaf player status` reports the player's identity, saved opt-in choice, active-protection state, lock state, and first-join time. -- [x] `/leaf player enabled ` changes the player's saved opt-in choice and immediately reconciles their online effect and prefix. -- [x] `/leaf player locked ` controls whether that player can change their own saved choice. -- [x] A locked player receives a clear explanation when `/leaf on` or `/leaf off` is denied. -- [x] PvP-triggered opt-out remains authoritative even for a locked player. -- [x] Player targets resolve online names, previously known names, and UUIDs without confusing two players who used the same name. -- [x] `/leaf strength <1-5>` remains a deprecated compatibility alias that changes the live Resistance level. -- [x] `/leaf effect resistance <1-5>` changes the live Resistance level and immediately updates protected online players. -- [x] `/leaf effect strength <1-5>` changes the live Strength level and immediately updates protected online players. -- [x] Effect-command autocomplete suggests effect names and valid levels for the current argument position. -- [x] Administrative autocomplete is permission-aware and suggests valid subcommands, known player targets, properties, levels, and `on` or `off` values for the current argument position. -- [x] Every successful mutation reports exactly what changed, and invalid requests make no partial changes. - -## Related - -- [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -- [US-002: Relinquish protection when attacking](us-002-relinquish-protection-when-attacking.md) -- [US-006: Configure and persist Leaf](us-006-configure-and-persist-leaf.md) diff --git a/design/user-stories/us-006-configure-and-persist-leaf.md b/design/user-stories/us-006-configure-and-persist-leaf.md deleted file mode 100644 index 9bba3d5..0000000 --- a/design/user-stories/us-006-configure-and-persist-leaf.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -type: User Story -title: "US-006: Configure and persist Leaf" -description: Provide validated configuration and durable, defensive storage for Leaf behavior. -status: done ---- - -# US-006: Configure and persist Leaf - -As a **server operator**, I want Leaf settings and player state to be validated and durable so that protection remains predictable across restarts. - -## Acceptance criteria - -- [x] Configuration supports the global enabled state, Resistance and Strength levels, leaf prefix, onboarding duration, and player-facing messages. -- [x] Resistance strength defaults to level I and accepts only Minecraft Resistance levels I through V. -- [x] Strength defaults to level I and accepts only Minecraft Strength levels I through V. -- [x] Runtime changes made through `/leaf enabled` and `/leaf effect` are persisted for subsequent restarts. -- [x] UUID-keyed player state persists the latest known name, saved opt-in choice, administrative lock, and first-join timestamp. -- [x] Date-times use RFC 3339 UTC notation with a `Z` suffix. -- [x] State is saved safely so that a failed write does not replace valid persisted state with a partial document. -- [x] Invalid required configuration prevents partial plugin initialization and produces a clear server log message. -- [x] Corrupt or invalid player records are handled defensively and cannot silently grant protection or privileges. -- [x] Removing Leaf-managed Resistance or Strength does not remove a distinct corresponding effect that Leaf does not own when the API provides enough information to distinguish it. -- [x] A temporary external Resistance or Strength effect is preserved instead of overwritten, and Leaf restores its configured effect after the external effect ends. -- [x] The leaf prefix remains visible while an external effect temporarily replaces a Leaf-managed effect. -- [x] Unknown forward-compatible configuration and player-state fields are preserved where practical. - -## Related - -- [US-001: Opt into Leaf protection](us-001-opt-into-leaf-protection.md) -- [US-004: Introduce new players to Leaf](us-004-introduce-new-players-to-leaf.md) -- [US-005: Administer Leaf](us-005-administer-leaf.md) -- [US-007: Build and release Leaf](us-007-build-and-release-leaf.md) diff --git a/design/user-stories/us-007-build-and-release-leaf.md b/design/user-stories/us-007-build-and-release-leaf.md deleted file mode 100644 index 01ca9cf..0000000 --- a/design/user-stories/us-007-build-and-release-leaf.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -type: User Story -title: "US-007: Build and release Leaf" -description: Give maintainers repeatable Spigot builds, automated verification, and versioned Gitea releases. -status: done ---- - -# US-007: Build and release Leaf - -As a **plugin maintainer**, I want automated builds and releases modeled on Spigot Base so that tested, correctly versioned Leaf artifacts can be distributed consistently. - -## Acceptance criteria - -- [x] The Gradle project compiles against Spigot API `26.2-R0.1-SNAPSHOT` using a Java 17 toolchain. -- [x] Compiler lint warnings fail the build. -- [x] Automated JUnit 5 tests run as part of the Gradle check lifecycle. -- [x] Pushes and pull requests build and test Leaf in Gitea Actions. -- [x] Pull requests validate conventional commit messages. -- [x] CI stores a development Leaf JAR as a workflow artifact. -- [x] Main-branch conventional commits drive semantic versioning. -- [x] A successful release builds a versioned Leaf JAR and attaches it to the corresponding Gitea release. -- [x] Build files, Gradle wrapper, workflows, and release behavior follow `../spigot-base/` where applicable while using Leaf names and identifiers. -- [x] The initial approved user-story bundle is kept in one documentation commit. -- [x] Subsequent implementation work is organized into a separate conventional commit for each implemented user story. -- [x] No remote push is performed until the maintainer confirms that the Gitea repository and release token secret are ready. - -## Related - -- [US-006: Configure and persist Leaf](us-006-configure-and-persist-leaf.md) -- [User-story catalog](index.md)