docs(design): add Spigot Stealth user stories
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
# Repository Agent Guidance
|
||||||
|
|
||||||
|
## User-story-driven development
|
||||||
|
|
||||||
|
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 changing behavior:
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Spigot Stealth Design Log
|
||||||
|
|
||||||
|
## 2026-08-14
|
||||||
|
|
||||||
|
- **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.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# 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.
|
||||||
|
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 and find concealed players.
|
||||||
|
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.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
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: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] Only time spent online under an invisibility effect produced by a potion the player directly drank contributes to progression.
|
||||||
|
- [ ] Invisibility received from splash potions, lingering potions, tipped arrows, commands, plugins, or other sources does not contribute.
|
||||||
|
- [ ] Qualifying time stops when the effect ends, is removed, is replaced by a non-qualifying source, the player disconnects, or the plugin disables.
|
||||||
|
- [ ] Refreshed or overlapping qualifying effects never count elapsed time more than once.
|
||||||
|
- [ ] Qualifying time accumulates across effects, sessions, and server restarts.
|
||||||
|
- [ ] The ability unlocks when accumulated qualifying time reaches eight hours by default.
|
||||||
|
- [ ] Reaching the threshold grants the unlock exactly once without discarding excess elapsed time.
|
||||||
|
- [ ] When an online player unlocks stealth, they receive both a full-screen title and a chat message explaining the ability.
|
||||||
|
- [ ] Progress and unlock ownership are associated with the player's UUID rather than their current name.
|
||||||
|
|
||||||
|
## 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)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
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: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] An unlocked player becomes eligible for a concealed login only by disconnecting while an invisibility effect from a potion they directly drank remains active.
|
||||||
|
- [ ] A player who has not unlocked stealth cannot prepare a concealed login.
|
||||||
|
- [ ] An ordinary disconnect without an active qualifying effect clears any preparation for the next login.
|
||||||
|
- [ ] On a prepared login, no public join announcement is shown.
|
||||||
|
- [ ] Throughout the concealed session, the player is absent from every other player's tab list, including administrators' tab lists.
|
||||||
|
- [ ] Throughout the concealed session, no overhead name tag identifies the player to any other player, including administrators.
|
||||||
|
- [ ] The concealed player's physical character remains visible in the world and retains ordinary movement, interaction, combat, and permission behavior.
|
||||||
|
- [ ] The concealed player receives a private message explaining that stealth is active for the session.
|
||||||
|
- [ ] Concealment lasts until the player disconnects and is handled predictably across death and plugin reload or disable events.
|
||||||
|
- [ ] Disconnecting consumes the current concealed session; another concealed login requires another qualifying potion and qualifying disconnect.
|
||||||
|
- [ ] Merely owning the unlock never conceals an ordinary login or carries concealment automatically into a later session.
|
||||||
|
- [ ] Prepared-login state survives a server restart between the qualifying disconnect and the next login.
|
||||||
|
|
||||||
|
## 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)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
type: User Story
|
||||||
|
title: "US-003: Check personal stealth progress"
|
||||||
|
description: Show a player their accumulated qualifying invisibility time and stealth unlock status.
|
||||||
|
status: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] `/stealth progress` reports the player's accumulated qualifying invisibility time.
|
||||||
|
- [ ] Before unlock, the command reports the configured target and remaining duration.
|
||||||
|
- [ ] While a qualifying effect is active, the report includes elapsed time not yet written during the current tracking interval.
|
||||||
|
- [ ] After unlock, the command clearly reports that stealth is unlocked and explains how to prepare a concealed login.
|
||||||
|
- [ ] Durations are presented in a concise, human-readable form.
|
||||||
|
- [ ] Repeated command use does not change progression or concealment state.
|
||||||
|
- [ ] The command has clear usage metadata and an appropriate player permission.
|
||||||
|
|
||||||
|
## 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)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
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: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] `/stealthadmin status <player|uuid>` reports accumulated time, unlock status, prepared-login state, and current concealment state.
|
||||||
|
- [ ] Status inspection works for online players and known offline players selected by exact current or previously recorded name or UUID.
|
||||||
|
- [ ] `/stealthadmin grant <player|uuid>` grants the unlock to an online or known offline player without altering accumulated time unnecessarily.
|
||||||
|
- [ ] Granting an already-owned unlock is safe and clearly reports that no change was needed.
|
||||||
|
- [ ] Granting the unlock sends the normal full-screen title and chat notification when the target is online.
|
||||||
|
- [ ] `/stealthadmin reset <player|uuid> confirm` clears accumulated time, unlock ownership, prepared-login state, and current concealment for an online or known offline player.
|
||||||
|
- [ ] Reset requires explicit confirmation and safely restores an online concealed player to ordinary identity presentation.
|
||||||
|
- [ ] `/stealthadmin list` lists every currently online concealed player and clearly reports when there are none.
|
||||||
|
- [ ] Concealed players remain absent from administrators' ordinary tab lists and retain hidden overhead name tags; the admin command is the supported discovery mechanism.
|
||||||
|
- [ ] Commands clearly reject unknown, ambiguous, malformed, or otherwise invalid targets without creating unintended player records.
|
||||||
|
- [ ] Administrative inspection and modification require an operator-default administrative permission.
|
||||||
|
- [ ] State-changing operations persist before success is reported and are safe under retries.
|
||||||
|
- [ ] Grant and reset actions record the administrator, target UUID, and action in the server log without blocking the server tick thread.
|
||||||
|
|
||||||
|
## 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)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
type: User Story
|
||||||
|
title: "US-005: Configure and persist stealth progression"
|
||||||
|
description: Give operators validated settings and durable, defensive storage for stealth behavior.
|
||||||
|
status: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] The qualifying-time threshold is configurable and defaults to eight hours.
|
||||||
|
- [ ] Player-facing progress, unlock, prepared-login, and concealed-session messages are configurable.
|
||||||
|
- [ ] Startup validates required settings before registering partially functional listeners, commands, or tasks.
|
||||||
|
- [ ] Invalid required configuration prevents initialization and produces a clear server log message.
|
||||||
|
- [ ] 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.
|
||||||
|
- [ ] Qualifying runtime intervals use a monotonic elapsed-time source so wall-clock adjustments cannot grant or remove progress.
|
||||||
|
- [ ] Durable timestamps, when required, use RFC 3339 UTC notation.
|
||||||
|
- [ ] State is saved periodically, after material state changes, and during orderly plugin disable.
|
||||||
|
- [ ] State uses atomic replacement where supported so an interrupted write does not replace valid data with a partial file.
|
||||||
|
- [ ] Corrupt, unknown, or invalid records cannot silently grant time, an unlock, a prepared login, or concealment.
|
||||||
|
- [ ] Unknown forward-compatible fields are preserved where practical.
|
||||||
|
- [ ] Persistence work does not perform blocking file operations on the server tick thread.
|
||||||
|
|
||||||
|
## 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)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
type: User Story
|
||||||
|
title: "US-006: Build, test, and release the plugin"
|
||||||
|
description: Give maintainers repeatable builds, automated verification, and versioned Gitea releases.
|
||||||
|
status: backlog
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- [ ] The Gradle Kotlin DSL project compiles against Spigot API `26.2-R0.1-SNAPSHOT` using a Java 17 toolchain.
|
||||||
|
- [ ] Compiler lint warnings fail the build.
|
||||||
|
- [ ] Automated JUnit 5 and Mockito tests run as part of the Gradle check lifecycle.
|
||||||
|
- [ ] Pushes and pull requests build and test the plugin in Gitea Actions.
|
||||||
|
- [ ] Pull requests validate conventional commit messages.
|
||||||
|
- [ ] CI stores a `spigot-stealth` development JAR as a workflow artifact.
|
||||||
|
- [ ] Main-branch conventional commits drive semantic versioning.
|
||||||
|
- [ ] A successful release builds a versioned JAR and attaches it to the corresponding Gitea release.
|
||||||
|
- [ ] Build files, Gradle wrapper, workflows, and release behavior follow `../spigot-tyrant/` where applicable while using Spigot Stealth names and identifiers.
|
||||||
|
- [ ] OKF validation using `okf-base.yaml` runs locally through the repository validation script and in CI.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Configure and persist stealth progression](us-005-configure-and-persist-progression.md)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/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