chore(knowledge): move canonical docs to shared SoMC wiki
This commit is contained in:
@@ -1,53 +1,9 @@
|
||||
# Repository Agent Guidance
|
||||
# spigot-invisibilty agent entrypoint
|
||||
|
||||
## User-story-driven development
|
||||
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).
|
||||
|
||||
The `design/` directory is the OKF v0.1 product record for this repository. Use user stories to plan, implement, verify, and track all behavior.
|
||||
Before work, read the sibling `../somc-okf/index.md`, `../somc-okf/processes/index.md`, `../somc-okf/projects/spigot-invisibilty/index.md`, `engineering.md` in that project section, and relevant `../somc-okf/user-stories/spigot-invisibilty/` stories. Also follow the parent workspace `AGENTS.md` when present.
|
||||
|
||||
Before changing behavior:
|
||||
For standalone checkouts, start at the [project page](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/spigot-invisibilty/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.
|
||||
|
||||
1. Read `design/index.md` and every story related to the requested behavior.
|
||||
2. Draft updates to an existing story or create a new `design/user-stories/us-NNN-short-name.md` story before implementation.
|
||||
3. Define observable acceptance criteria using user or operator language.
|
||||
4. Present the relevant new or updated stories and acceptance criteria to the user for review, and wait for explicit confirmation before changing implementation code.
|
||||
5. Incorporate requested story changes before proceeding.
|
||||
6. Set story status to `in-progress` while approved implementation is incomplete.
|
||||
|
||||
While implementing:
|
||||
|
||||
1. Work in vertical slices against the documented acceptance criteria.
|
||||
2. Add tests for important behavior before implementation when practical.
|
||||
3. Keep implementation references and related-story links current.
|
||||
4. Do not mark an acceptance criterion complete until the behavior exists and has been validated.
|
||||
|
||||
Before completing or committing:
|
||||
|
||||
1. Set a completed story status to `done` only when all acceptance criteria are complete and verified.
|
||||
2. Check completed acceptance criteria and record validation evidence where appropriate.
|
||||
3. Update `design/index.md` and `design/user-stories/index.md` whenever stories are added, renamed, moved, or materially reclassified.
|
||||
4. Add a high-level entry to `design/log.md` under the verified current date.
|
||||
5. Run OKF validation along with relevant Gradle tests, lint checks, and builds.
|
||||
|
||||
## OKF conventions
|
||||
|
||||
- Every non-reserved Markdown file in `design/` must have YAML frontmatter with a non-empty `type`.
|
||||
- User stories use `type: User Story` and include `status`, `title`, and `description`.
|
||||
- Allowed story statuses are `backlog`, `in-progress`, and `done`.
|
||||
- Every user story includes an acceptance-criteria section using Markdown task-list items.
|
||||
- `design/index.md` and `design/log.md` are reserved OKF files and follow the OKF index/log structures.
|
||||
- Store user stories in `design/user-stories/` and keep their catalog current.
|
||||
- Use standard Markdown links and keep repository-local links valid when files move.
|
||||
- Preserve unknown frontmatter extensions.
|
||||
|
||||
## Java and Spigot development
|
||||
|
||||
- Use the Java version and Spigot API version declared by the Gradle build.
|
||||
- Treat compiler warnings as errors.
|
||||
- Prefer test-first development for domain rules and state transitions when practical.
|
||||
- Run `./gradlew clean check jar` before completing implementation work.
|
||||
- Keep Bukkit event handlers thin and move testable game rules into focused domain services.
|
||||
- Do not perform blocking file or network operations on the server tick thread.
|
||||
|
||||
## Timestamps
|
||||
|
||||
Always run `date +%Y-%m-%d` before adding or updating dates in stories or the design log. Use RFC 3339 UTC timestamps (`YYYY-MM-DDTHH:MM:SSZ`) when a date-time is required. Never guess dates.
|
||||
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.
|
||||
|
||||
@@ -46,11 +46,3 @@ tasks.processResources {
|
||||
expand("version" to pluginVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Exec>("validateOkf") {
|
||||
commandLine("./scripts/validate-okf.sh")
|
||||
}
|
||||
|
||||
tasks.check {
|
||||
dependsOn("validateOkf")
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
okf_version: "0.1"
|
||||
---
|
||||
|
||||
# Spigot Stealth Design
|
||||
|
||||
Spigot Stealth rewards players who spend time under invisibility effects from potions they drink, then lets unlocked players conceal their identity for a later session. [Session concealment](user-stories/us-002-rejoin-without-visible-identity.md) includes command-suggestion filtering and scrambled message and overhead names.
|
||||
|
||||
[Administration](user-stories/us-004-administer-player-stealth.md) provides private concealed-player counts at login and command-based inspection.
|
||||
|
||||
## Explore
|
||||
|
||||
- [User stories](user-stories/) - Approved requirements for progression, stealth sessions, commands, persistence, and delivery.
|
||||
- [Design log](log.md) - Material product decisions and bundle changes.
|
||||
@@ -1,39 +0,0 @@
|
||||
# Spigot Stealth Design Log
|
||||
|
||||
## 2026-09-06
|
||||
|
||||
- **Completion**: Extended US-004 with permission-gated private concealed-player counts for joining admins and arrival notices for online admins, including command hints, zero-count silence, and no duplicate notice for concealed admins. Verified all 70 tests, the JAR build, and OKF validation; not deployed or live-server tested.
|
||||
|
||||
- **Completion**: Extended US-002 with concealed-username suggestion filtering, scrambled default chat and vanilla message/advancement names, scrambled overhead tags, and display-name restoration. Verified 65 automated tests, the Gradle JAR build, and OKF validation; live-server compatibility remains untested and custom chat/scoreboard integrations are outside this slice.
|
||||
|
||||
## 2026-09-05
|
||||
|
||||
- **Fix**: Reworked US-002 server-list filtering for Purpur's native `NameAndId` samples by filtering names through Bukkit, limiting ProtocolLib to guarded count adjustment, and rate-limiting compatibility warnings; verified the complete Gradle build and OKF bundle.
|
||||
- **Completion**: Extended US-002, US-004, and US-005 with a persistent, permission-gated sleep-count policy that excludes concealed players by default, applies immediately, restores prior player state, and supports contextual administration; verified the complete Gradle build and OKF bundle.
|
||||
- **Completion**: Extended US-002 with ProtocolLib filtering of concealed sessions from multiplayer server-list counts and player samples while preserving actual online state and advertised capacity; verified the complete Gradle build and OKF bundle.
|
||||
|
||||
## 2026-09-04
|
||||
|
||||
- **Completion**: Extended US-002 so concealed players disconnect without a public quit announcement while ordinary quit messages remain unchanged; verified listener tests, the complete Gradle build, and the OKF bundle.
|
||||
- **Completion**: Extended US-003 and US-004 with contextual, prefix-filtered command completion that suppresses generic player suggestions, plus an administrative list of all known online and offline unlocked players; verified the complete Gradle build and OKF bundle.
|
||||
|
||||
## 2026-08-14
|
||||
|
||||
- **Completion**: Completed US-004 with exact known online and offline targeting, complete status, idempotent grants, confirmed resets, active concealment discovery, operator-default permissions, asynchronous persisted replies, and console audits; verified the full Gradle build.
|
||||
- **Implementation**: Began US-004 with test-first exact offline targeting, grant/reset transitions, concealed-player listing, permissions, confirmation, and audit logging.
|
||||
- **Completion**: Completed US-002 with single-use durable prepared logins, join-announcement suppression, ProtocolLib tab-only removal, scoreboard overhead-name suppression, visible physical entities, periodic observer refresh, respawn restoration, and disconnect or disable cleanup; verified the full Gradle build.
|
||||
- **Implementation**: Began US-002 with test-first prepared-login consumption, session-scoped concealment, join suppression, tab removal, and overhead-name presentation.
|
||||
- **Completion**: Completed US-003 with `/stealth progress`, live unsaved interval inclusion, configured target and remaining output, concise duration formatting, and unlocked usage guidance; verified the full Gradle build.
|
||||
- **Implementation**: Began US-003 with player-facing progress command and human-readable duration tests.
|
||||
- **Completion**: Completed US-001 with direct potion-drink effect attribution, monotonic elapsed-time accumulation, safe refresh and stop transitions, durable UUID progress, exact-once unlocking, and title plus chat presentation; verified the full Gradle build.
|
||||
- **Implementation**: Began US-001 with monotonic-clock progression tests and potion-cause event attribution.
|
||||
- **Completion**: Completed US-005 with validated eight-hour defaults and configurable messages, UUID-keyed immutable state, RFC 3339 timing metadata, safe invalid-record defaults, unknown-field preservation, atomic YAML replacement, periodic saves, and serialized off-thread persistence; verified the full Gradle build.
|
||||
- **Implementation**: Began US-005 with test-first validated settings, UUID-keyed state, and defensive asynchronous persistence.
|
||||
- **Completion**: Completed US-006 with a Java 17 Gradle build, strict compiler linting, Spigot API, JUnit 5, Mockito, plugin metadata, OKF validation, Gitea CI, conventional-commit checks, semantic releases, and versioned release assets; verified the full build and a `1.2.3` release JAR.
|
||||
- **Implementation**: Began US-006 with a test-driven Java 17, Gradle, Spigot, OKF validation, and Gitea delivery foundation modeled on Spigot Tyrant.
|
||||
- **Creation**: Established the OKF v0.1 product record for Spigot Stealth.
|
||||
- **Decision**: Only invisibility from a potion the player directly drinks contributes to the default eight-hour unlock requirement.
|
||||
- **Decision**: An unlocked player who disconnects while their qualifying potion effect remains active conceals their identity for the entirety of their next online session.
|
||||
- **Decision**: Each concealed session requires a new qualifying potion and disconnect; concealment never carries automatically into another session.
|
||||
- **Decision**: Concealed players remain physically visible but have no join announcement, tab-list entry, or overhead name tag for any player, including administrators.
|
||||
- **Decision**: Administrators can inspect and manage online or offline progression and list currently concealed players.
|
||||
@@ -1,8 +0,0 @@
|
||||
# Spigot Stealth User Stories
|
||||
|
||||
1. [US-001: Accumulate invisibility time and unlock stealth](us-001-accumulate-invisibility-and-unlock.md) - Earn stealth by spending eight hours under invisibility effects from directly consumed potions.
|
||||
2. [US-002: Rejoin without a visible identity](us-002-rejoin-without-visible-identity.md) - Turn a qualifying invisible disconnect into one concealed online session with filtered name suggestions and scrambled message and overhead names.
|
||||
3. [US-003: Check personal stealth progress](us-003-check-personal-progress.md) - View accumulated time, remaining time, and unlock status.
|
||||
4. [US-004: Inspect and manage player stealth](us-004-administer-player-stealth.md) - Inspect and modify online or offline progression, find concealed players, and receive private login count notices.
|
||||
5. [US-005: Configure and persist stealth progression](us-005-configure-and-persist-progression.md) - Keep progression durable and operator-configurable.
|
||||
6. [US-006: Build, test, and release the plugin](us-006-build-test-and-release.md) - Provide repeatable Gradle builds and Gitea delivery.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-001: Accumulate invisibility time and unlock stealth"
|
||||
description: Reward sustained use of directly consumed invisibility potions with the stealth ability.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-001: Accumulate invisibility time and unlock stealth
|
||||
|
||||
As a **player**, I want my qualifying invisibility time to accumulate so that sustained potion use eventually unlocks stealth.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] Only time spent online under an invisibility effect produced by a potion the player directly drank contributes to progression.
|
||||
- [x] Invisibility received from splash potions, lingering potions, tipped arrows, commands, plugins, or other sources does not contribute.
|
||||
- [x] Qualifying time stops when the effect ends, is removed, is replaced by a non-qualifying source, the player disconnects, or the plugin disables.
|
||||
- [x] Refreshed or overlapping qualifying effects never count elapsed time more than once.
|
||||
- [x] Qualifying time accumulates across effects, sessions, and server restarts.
|
||||
- [x] The ability unlocks when accumulated qualifying time reaches eight hours by default.
|
||||
- [x] Reaching the threshold grants the unlock exactly once without discarding excess elapsed time.
|
||||
- [x] When an online player unlocks stealth, they receive both a full-screen title and a chat message explaining the ability.
|
||||
- [x] Progress and unlock ownership are associated with the player's UUID rather than their current name.
|
||||
|
||||
## Validation
|
||||
|
||||
Automated tests verify monotonic accumulation across intervals, refresh without duplicate time, exact-once threshold crossing with excess-time preservation, direct potion-drink cause filtering, non-qualifying replacement, UUID state, and online title and chat presentation. The complete `./gradlew clean check jar` lifecycle passes.
|
||||
|
||||
## Related
|
||||
|
||||
- [Rejoin without a visible identity](us-002-rejoin-without-visible-identity.md)
|
||||
- [Check personal stealth progress](us-003-check-personal-progress.md)
|
||||
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-002: Rejoin without a visible identity"
|
||||
description: Let an unlocked player turn a qualifying invisible disconnect into one identity-concealed session.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-002: Rejoin without a visible identity
|
||||
|
||||
As an **unlocked player**, I want to disconnect while invisibility from a potion I drank is active so that my identity is concealed throughout my next session.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] An unlocked player becomes eligible for a concealed login only by disconnecting while an invisibility effect from a potion they directly drank remains active.
|
||||
- [x] A player who has not unlocked stealth cannot prepare a concealed login.
|
||||
- [x] An ordinary disconnect without an active qualifying effect clears any preparation for the next login.
|
||||
- [x] On a prepared login, no public join announcement is shown.
|
||||
- [x] When a concealed player disconnects, no public quit or disconnect announcement is shown.
|
||||
- [x] Ordinary players' quit messages remain unchanged.
|
||||
- [x] Concealment is checked before disconnect cleanup so announcement suppression is reliable.
|
||||
- [x] Throughout the concealed session, the player is absent from every other player's tab list, including administrators' tab lists.
|
||||
- [x] The multiplayer server list's online-player count excludes currently concealed players.
|
||||
- [x] Concealed players are excluded from any player-name sample shown for the server-list count, while ordinary players remain represented.
|
||||
- [x] The public count never becomes negative, and the configured maximum-player count remains unchanged.
|
||||
- [x] Server-list concealment changes only the public ping response and does not alter actual online-player state or gameplay.
|
||||
- [x] Server-list filtering remains error-free when Purpur represents player samples with native `NameAndId` values rather than Mojang `GameProfile` values.
|
||||
- [x] A server-ping compatibility failure leaves the original response usable and does not produce repeated unhandled listener exceptions.
|
||||
- [x] By default, concealed players are excluded from sleep-percentage calculations.
|
||||
- [x] When the sleep-count policy is `include`, concealed players count normally; ordinary players are never modified by either policy.
|
||||
- [x] A player's previous sleeping-ignore state is restored when concealment ends, the player disconnects or is reset, the policy changes to `include`, or the plugin disables.
|
||||
- [x] Policy changes apply immediately to currently concealed players.
|
||||
- [x] Throughout the concealed session, the overhead name is rendered as scrambled magic text, including for administrators, while the physical character stays visible.
|
||||
- [x] Concealed usernames are excluded from outgoing command suggestions, including `/msg`, `/tell`, and `/w`; manually addressing a known username remains possible.
|
||||
- [x] Default chat display names and vanilla private-message and advancement name components are scrambled without changing message content or signed chat bodies.
|
||||
- [x] Masked message name components do not expose the original identity through hover or click actions.
|
||||
- [x] Previous display names and ordinary suggestions return when concealment ends; ordinary players remain unaffected.
|
||||
- [x] The concealed player's physical character remains visible in the world and retains ordinary movement, interaction, combat, and permission behavior.
|
||||
- [x] The concealed player receives a private message explaining that stealth is active for the session.
|
||||
- [x] Concealment lasts until the player disconnects and is handled predictably across death and plugin reload or disable events.
|
||||
- [x] Disconnecting consumes the current concealed session; another concealed login requires another qualifying potion and qualifying disconnect.
|
||||
- [x] Merely owning the unlock never conceals an ordinary login or carries concealment automatically into a later session.
|
||||
- [x] Prepared-login state survives a server restart between the qualifying disconnect and the next login.
|
||||
|
||||
## Validation
|
||||
|
||||
Automated tests verify unlocked and locked disconnect transitions, ordinary-disconnect clearing, one-login consumption, concealed join and quit announcement suppression, preservation of ordinary announcements, private activation messaging, ordinary-login presentation, tab removal for existing and new observers, overhead-name suppression, active concealed-session tracking, ProtocolLib-independent native player-sample filtering, guarded public server-list count adjustment, nonnegative counts, unchanged maximum capacity, default sleep-count exclusion, immediate policy changes, restoration of prior sleeping-ignore state, and the absence of entity-hiding calls. ProtocolLib is declared as a required dependency, prepared state round trips through YAML, and `./gradlew clean check jar` passes.
|
||||
|
||||
Identity masking verification (2026-09-06): `./gradlew --offline clean check jar` passed all 65 tests and OKF validation. Tests cover semantic name slots, preserved message content, stripped identifying actions, reflected suggestion entries and chat-bound records, safe packet failure handling, immutable name snapshots, overhead formatting, and display-name restoration after repeated concealment. Presentation tests first failed to compile against the missing snapshot API, then passed after implementation. These are automated unit checks, not a live-client smoke test.
|
||||
|
||||
## Identity masking scope
|
||||
|
||||
Masking applies to active concealed sessions, not every potion effect. Outgoing username suggestions are filtered across commands; entering a known username manually still works. Default Bukkit chat uses a temporary scrambled `Anonymous` display name. Vanilla translated announcements and chat-type bound names are masked at the packet boundary without editing signed message bodies. The overhead tag uses scoreboard magic formatting on the profile name, not a profile rename; this is visual concealment, not anonymity against modified clients.
|
||||
|
||||
Custom chat formats that ignore Bukkit display names, nicknames, custom scoreboards, and third-party messaging plugins may need dedicated integration. Unsupported packet layouts retain the original packet and emit one warning per packet type rather than interrupting chat delivery. Live-server/client compatibility still requires a smoke test, particularly after Minecraft or ProtocolLib upgrades.
|
||||
|
||||
Implementation: [identity presentation](../../src/main/java/games/dmg/spigotstealth/BukkitIdentityPresentation.java), [packet listener](../../src/main/java/games/dmg/spigotstealth/ProtocolLibIdentityMaskingListener.java), and [semantic name masking](../../src/main/java/games/dmg/spigotstealth/StealthChatMasker.java).
|
||||
|
||||
## Related
|
||||
|
||||
- [Accumulate invisibility time and unlock stealth](us-001-accumulate-invisibility-and-unlock.md)
|
||||
- [Inspect and manage player stealth](us-004-administer-player-stealth.md)
|
||||
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-003: Check personal stealth progress"
|
||||
description: Show a player their accumulated qualifying invisibility time and stealth unlock status.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-003: Check personal stealth progress
|
||||
|
||||
As a **player**, I want to check my stealth progress so that I know how close I am to unlocking the ability and how to use it afterward.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] `/stealth progress` reports the player's accumulated qualifying invisibility time.
|
||||
- [x] Before unlock, the command reports the configured target and remaining duration.
|
||||
- [x] While a qualifying effect is active, the report includes elapsed time not yet written during the current tracking interval.
|
||||
- [x] After unlock, the command clearly reports that stealth is unlocked and explains how to prepare a concealed login.
|
||||
- [x] Durations are presented in a concise, human-readable form.
|
||||
- [x] Repeated command use does not change progression or concealment state.
|
||||
- [x] The command has clear usage metadata and an appropriate player permission.
|
||||
- [x] `/stealth` tab-completes `progress`, filtered by the entered prefix.
|
||||
- [x] Unsupported argument positions return no suggestions instead of Bukkit's generic player list.
|
||||
|
||||
## Validation
|
||||
|
||||
Automated tests verify concise duration formatting, live in-flight progress, configured target and remaining output, command non-mutation, unlocked usage guidance, prefix-filtered completion, and suppression of irrelevant suggestions. Command metadata and the complete `./gradlew clean check jar` lifecycle pass.
|
||||
|
||||
## Related
|
||||
|
||||
- [Accumulate invisibility time and unlock stealth](us-001-accumulate-invisibility-and-unlock.md)
|
||||
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-004: Inspect and manage player stealth"
|
||||
description: Give administrators durable controls for online and offline progression, unlocks, and active concealment.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-004: Inspect and manage player stealth
|
||||
|
||||
As a **server administrator**, I want to inspect and correct player stealth state so that I can support players whether they are online or offline.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] `/stealthadmin status <player|uuid>` reports accumulated time, unlock status, prepared-login state, and current concealment state.
|
||||
- [x] Status inspection works for online players and known offline players selected by exact current or previously recorded name or UUID.
|
||||
- [x] `/stealthadmin grant <player|uuid>` grants the unlock to an online or known offline player without altering accumulated time unnecessarily.
|
||||
- [x] Granting an already-owned unlock is safe and clearly reports that no change was needed.
|
||||
- [x] Granting the unlock sends the normal full-screen title and chat notification when the target is online.
|
||||
- [x] `/stealthadmin reset <player|uuid> confirm` clears accumulated time, unlock ownership, prepared-login state, and current concealment for an online or known offline player.
|
||||
- [x] Reset requires explicit confirmation and safely restores an online concealed player to ordinary identity presentation.
|
||||
- [x] `/stealthadmin list` lists every currently online concealed player and clearly reports when there are none.
|
||||
- [x] Concealed players remain absent from administrators' ordinary tab lists and retain scrambled overhead names; the admin command is the supported discovery mechanism.
|
||||
- [x] An administrator joining while concealed players are online receives a private count and a hint to use `/stealthadmin list`.
|
||||
- [x] When a concealed player joins, online administrators receive a private arrival notice, the updated concealed-player count, and the command hint.
|
||||
- [x] Notifications require `spigotstealth.admin`; ordinary players receive no administrative notice.
|
||||
- [x] Counts include the newly joined concealed player, and a joining concealed administrator receives only one administrative notification.
|
||||
- [x] An administrator joining when no concealed players are online receives no notification.
|
||||
- [x] Commands clearly reject unknown, ambiguous, malformed, or otherwise invalid targets without creating unintended player records.
|
||||
- [x] Administrative inspection and modification require an operator-default administrative permission.
|
||||
- [x] State-changing operations persist before success is reported and are safe under retries.
|
||||
- [x] Grant and reset actions record the administrator, target UUID, and action in the server log without blocking the server tick thread.
|
||||
- [x] `/stealthadmin` tab-completes `status`, `grant`, `reset`, and `list`, filtered by the entered prefix.
|
||||
- [x] Target positions for `status`, `grant`, and `reset` suggest known player names, and reset's final argument suggests `confirm`.
|
||||
- [x] Administrative completions require the administrative permission, and irrelevant positions return no suggestions instead of Bukkit's generic player list.
|
||||
- [x] `/stealthadmin list unlocked` lists every known online or offline player with stealth unlocked.
|
||||
- [x] The unlocked list is sorted case-insensitively, identifies nameless records by UUID, and clearly reports when it is empty.
|
||||
- [x] Existing `/stealthadmin list` behavior continues to list currently concealed online players.
|
||||
- [x] `/stealthadmin sleepcount status` reports whether concealed players are included in or excluded from sleep-percentage calculations.
|
||||
- [x] `/stealthadmin sleepcount <include|exclude>` persists the policy before reporting success, applies it immediately, and records the administrator and policy in the server log.
|
||||
- [x] The `sleepcount`, `status`, `include`, and `exclude` arguments are permission-gated and contextually tab-completed.
|
||||
|
||||
## Validation
|
||||
|
||||
Automated tests verify exact offline name and UUID resolution, ambiguous and unknown rejection without record creation, complete status output, idempotent grants and notification, complete resets with presentation cleanup, online concealed-player filtering, online and offline unlocked-player listing, contextual permission-gated completion, durable sleep-count status and policy changes, immediate policy refresh, confirmation and permission gates, persisted-before-success replies, and audit records. The complete `./gradlew clean check jar` lifecycle passes.
|
||||
|
||||
Login notice verification (2026-09-06): `./gradlew --offline clean check jar` passed all 70 tests and OKF validation. New tests first failed against the absent listener, then passed after implementation. Tests cover permission gates, zero-count silence, singular/plural counts, concealed-admin deduplication, ordinary-join silence for existing admins, and a prepared-login transition followed by an accurate administrative notice. Live-server verification remains pending.
|
||||
|
||||
Implementation: [admin join listener](../../src/main/java/games/dmg/spigotstealth/StealthAdminJoinListener.java), registered at `MONITOR` after the session listener's `HIGHEST` login transition. Notices expose only counts; names remain available through `/stealthadmin list`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Rejoin without a visible identity](us-002-rejoin-without-visible-identity.md)
|
||||
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-005: Configure and persist stealth progression"
|
||||
description: Give operators validated settings and durable, defensive storage for stealth behavior.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-005: Configure and persist stealth progression
|
||||
|
||||
As a **server operator**, I want stealth progression to be configurable and durable so that the plugin remains predictable across restarts and balance changes.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] The qualifying-time threshold is configurable and defaults to eight hours.
|
||||
- [x] Player-facing progress, unlock, prepared-login, and concealed-session messages are configurable.
|
||||
- [x] Startup validates required settings before registering partially functional listeners, commands, or tasks.
|
||||
- [x] Invalid required configuration prevents initialization and produces a clear server log message.
|
||||
- [x] UUID-keyed state stores accumulated qualifying duration, unlock ownership, active qualifying timing data, prepared-login state, and any current concealment metadata needed for safe recovery.
|
||||
- [x] Qualifying runtime intervals use a monotonic elapsed-time source so wall-clock adjustments cannot grant or remove progress.
|
||||
- [x] Durable timestamps, when required, use RFC 3339 UTC notation.
|
||||
- [x] State is saved periodically, after material state changes, and during orderly plugin disable.
|
||||
- [x] State uses atomic replacement where supported so an interrupted write does not replace valid data with a partial file.
|
||||
- [x] Corrupt, unknown, or invalid records cannot silently grant time, an unlock, a prepared login, or concealment.
|
||||
- [x] Unknown forward-compatible fields are preserved where practical.
|
||||
- [x] Persistence work does not perform blocking file operations on the server tick thread.
|
||||
- [x] The sleep-count policy persists in `state.yml` and defaults safely to `exclude` when missing or invalid.
|
||||
|
||||
## Validation
|
||||
|
||||
Verified settings defaults and rejection, packaged configuration, safe UUID-state defaults, sleep-count policy defaults and round trips, RFC 3339 round trips, unknown-field preservation, invalid-record rejection, atomic repository writes, and dedicated-thread loading and saving with automated tests and `./gradlew clean check jar`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Accumulate invisibility time and unlock stealth](us-001-accumulate-invisibility-and-unlock.md)
|
||||
- [Rejoin without a visible identity](us-002-rejoin-without-visible-identity.md)
|
||||
- [Inspect and manage player stealth](us-004-administer-player-stealth.md)
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-006: Build, test, and release the plugin"
|
||||
description: Give maintainers repeatable builds, automated verification, and versioned Gitea releases.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-006: Build, test, and release the plugin
|
||||
|
||||
As a **plugin maintainer**, I want automated builds and releases modeled on Spigot Tyrant so that tested, correctly versioned artifacts can be distributed consistently.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] The Gradle Kotlin DSL 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 and Mockito tests run as part of the Gradle check lifecycle.
|
||||
- [x] Pushes and pull requests build and test the plugin in Gitea Actions.
|
||||
- [x] Pull requests validate conventional commit messages.
|
||||
- [x] CI stores a `spigot-stealth` development JAR as a workflow artifact.
|
||||
- [x] Main-branch conventional commits drive semantic versioning.
|
||||
- [x] A successful release builds a versioned JAR and attaches it to the corresponding Gitea release.
|
||||
- [x] Build files, Gradle wrapper, workflows, and release behavior follow `../spigot-tyrant/` where applicable while using Spigot Stealth names and identifiers.
|
||||
- [x] OKF validation using `okf-base.yaml` runs locally through the repository validation script and in CI.
|
||||
|
||||
## Validation
|
||||
|
||||
Verified with `./gradlew clean check jar`, a release-version `1.2.3` JAR with matching embedded plugin metadata, and `./scripts/validate-okf.sh`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||
@@ -1,23 +0,0 @@
|
||||
okf_version: "0.1"
|
||||
|
||||
base:
|
||||
name: Spigot Stealth Design
|
||||
roots:
|
||||
- path: design
|
||||
reserved_files:
|
||||
index: index.md
|
||||
log: log.md
|
||||
|
||||
profile:
|
||||
types:
|
||||
User Story:
|
||||
required: [type, title, description, status]
|
||||
optional: []
|
||||
status_values: [backlog, in-progress, done]
|
||||
date_fields: []
|
||||
|
||||
hygiene:
|
||||
broken_links: error
|
||||
reserved_files: error
|
||||
unknown_fields: error
|
||||
split_candidates: off
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# OKF Bundle Validator v0.1
|
||||
# Usage: validate.sh <bundle-path>
|
||||
# Checks conformance with OKF v0.1 spec:
|
||||
# E1: All non-reserved .md files have YAML frontmatter
|
||||
# E2: All frontmatter has non-empty 'type' field
|
||||
# E3: Reserved files follow structure rules
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPOSITORY_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
BUNDLE="${1:-${REPOSITORY_ROOT}/design}"
|
||||
MANIFEST="${REPOSITORY_ROOT}/okf-base.yaml"
|
||||
|
||||
if command -v okflint >/dev/null 2>&1; then
|
||||
exec okflint validate --manifest "$MANIFEST" "$BUNDLE"
|
||||
fi
|
||||
|
||||
echo "Warning: okflint is unavailable; running core OKF v0.1 validation only." >&2
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
TOTAL=0
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
if [ ! -d "$BUNDLE" ]; then
|
||||
echo -e "${RED}Error: '$BUNDLE' is not a directory${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Validating OKF bundle: $BUNDLE"
|
||||
echo "---"
|
||||
|
||||
# Find all .md files
|
||||
while IFS= read -r -d '' file; do
|
||||
TOTAL=$((TOTAL + 1))
|
||||
relative="${file#$BUNDLE/}"
|
||||
basename=$(basename "$file")
|
||||
|
||||
# Skip reserved files (validate separately)
|
||||
if [[ "$basename" == "index.md" || "$basename" == "log.md" ]]; then
|
||||
# E3: Check reserved file structure
|
||||
if [[ "$basename" == "index.md" ]]; then
|
||||
# index.md should NOT have frontmatter (except bundle root may have okf_version)
|
||||
if head -1 "$file" | grep -q "^---$"; then
|
||||
# Allow only if it's bundle root and contains okf_version
|
||||
if [[ "$relative" != "index.md" ]]; then
|
||||
echo -e "${RED}E3: $relative — index.md should not have frontmatter${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ "$basename" == "log.md" ]]; then
|
||||
# log.md should have date headings in YYYY-MM-DD format
|
||||
if ! grep -qE "^## [0-9]{4}-[0-9]{2}-[0-9]{2}" "$file" 2>/dev/null; then
|
||||
if [ -s "$file" ]; then
|
||||
echo -e "${YELLOW}W: $relative — log.md has no ISO 8601 date headings${NC}"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# E1: Check for YAML frontmatter
|
||||
if ! head -1 "$file" | grep -q "^---$"; then
|
||||
echo -e "${RED}E1: $relative — no YAML frontmatter${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract frontmatter (between first --- and second ---)
|
||||
frontmatter=$(sed -n '2,/^---$/p' "$file" | sed '$d')
|
||||
|
||||
# E2: Check for non-empty type field
|
||||
type_value=$(echo "$frontmatter" | grep -E "^type:" | sed 's/^type:\s*//' | tr -d '"' | tr -d "'" | xargs)
|
||||
if [ -z "$type_value" ]; then
|
||||
echo -e "${RED}E2: $relative — missing or empty 'type' field${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Warnings for recommended fields
|
||||
if ! echo "$frontmatter" | grep -qE "^title:"; then
|
||||
echo -e "${YELLOW}W1: $relative — missing recommended 'title' field${NC}"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
if ! echo "$frontmatter" | grep -qE "^description:"; then
|
||||
echo -e "${YELLOW}W1: $relative — missing recommended 'description' field${NC}"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
|
||||
done < <(find "$BUNDLE" -name "*.md" -type f -print0 | sort -z)
|
||||
|
||||
# Summary
|
||||
echo "---"
|
||||
echo "Files scanned: $TOTAL"
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ Bundle is OKF v0.1 conformant${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ $ERRORS error(s) — bundle is NOT conformant${NC}"
|
||||
fi
|
||||
if [ $WARNINGS -gt 0 ]; then
|
||||
echo -e "${YELLOW}⚠ $WARNINGS warning(s)${NC}"
|
||||
fi
|
||||
|
||||
exit $ERRORS
|
||||
Reference in New Issue
Block a user