From 59c3490478c8016c31a9c319349892303dd593cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 1 Sep 2026 23:12:48 +0200 Subject: [PATCH 1/3] docs: trim agent memory and move scoped guidance to skills Claude-Session: https://claude.ai/code/session_01Q2TdmQp4honrR7pBLf85JK --- .claude/skills/release/SKILL.md | 77 +++++++++++++++++ AGENTS.md | 143 +------------------------------- spigot/CLAUDE.md | 30 +++++++ velocity/CLAUDE.md | 31 +++++++ 4 files changed, 142 insertions(+), 139 deletions(-) create mode 100644 .claude/skills/release/SKILL.md create mode 100644 spigot/CLAUDE.md create mode 100644 velocity/CLAUDE.md diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 000000000..2f000e526 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,77 @@ +--- +name: release +description: Audit the release-PR checks, verify published assets, keep the Modrinth and Hangar publish step contracts intact, and repair a connect-java release that published no assets. +--- + +# Connect Java release workflow + +Commit-prefix rules and the `release-please.yml` to `release.yml` handoff live in the root +`AGENTS.md`; this skill holds the step contracts and repair procedure. + +## Release Flow + +- `release-please.yml`'s release-PR build is a manual dispatch whose native matrix checks are + audited on the captured head before merge. Do not mirror those checks into + synthetic check runs or legacy statuses; the boundary is pinned by + `core/.../release/ReleasePleaseCheckAuditTest`. +- After creating a release, verify the release is not draft/prerelease unless + intentionally so, and verify the asset digest/availability: + +```sh +gh -R minekube/connect-java release view --json tagName,targetCommitish,isDraft,isPrerelease,assets +curl -I -L --fail https://github.com/minekube/connect-java/releases/download//connect-velocity.jar +``` + +- `release.yml`'s "Verify published release assets" step re-reads each published + release from the API (never the upload step's own output) and requires the + same positive plugin-jar allowlist as `release-repair.yml`. Pinned by + `core/.../release/ReleaseAssetVerificationTest`; keep that test's step and + upload-step names in sync when editing `release.yml`. +- `release.yml`'s "Publish to Modrinth" step publishes the same jars to the + Modrinth listing (project id `PuSyuNRf`, `minekube-connect`), one version per + platform because Modrinth runs every validator whose loaders intersect the + declared loaders against every file in a version. It uploads the runner's + build output, never the release assets, and confirms each upload by reading + the stored version back and comparing sha1 and sha512. Its event condition is + the safety property: without it every push to `main` would publish a + development build to a public listing without anything going red. Pinned by + `core/.../release/ReleaseModrinthPublishTest`; keep that test's step names in + sync when editing `release.yml`. Dispatching `release.yml` at an OLD tag + publishes that tag to Modrinth - the listing is not a backfill target. +- `release.yml`'s "Publish to Hangar" step publishes to `minekube/Connect` and + syncs `.github/hangar-description.md`. `HANGAR_API_TOKEN` needs + `create_version` and `edit_page`. Hangar's platform mapping is Paper jar to + `PAPER`, Velocity jar to `VELOCITY`, and Bungee jar to `WATERFALL` (Hangar + has no BungeeCord platform). The step reads accepted platform versions at + publish time, floors Paper from `plugin.yml` and Velocity at the existing + 3.0 compatibility boundary. The three shaded jars exceed Hangar's + Cloudflare request limit as one multipart upload, so the version uses + immutable versioned GitHub release URLs, stores their SHA-256 values in the + public version description, and verifies GitHub's asset digest plus each + Hangar download's final bytes, size, content type, and JAR magic. Pinned by + `core/.../release/ReleaseHangarPublishTest`; keep its step names in sync. + +### Repairing a release that published no assets + +- Use `release-repair.yml` (default branch, manual dispatch). It builds at the + JDK that tag's own `release.yml` pinned and uploads only missing or broken + assets. Never dispatch `release.yml` at an old tag instead: it rewrites the + live `latest` release, dragging the stable `releases/download/latest/*.jar` + URLs backwards. Boundary and guards pinned by + `core/.../release/ReleaseRepairCapabilityTest`; keep its step names in sync. +- A repair EXECUTES old, unreviewed tagged source. Never collapse the workflow's + read-only `build` / write-only `publish` job boundary or substitute + step-level token scoping; the workflow comments and + `ReleaseRepairCapabilityTest` own the exact boundary, artifact-name + allowlist, race handling, and landed-verification details. Top-level + `permissions: {}` must remain explicit. +- Asset naming is per-era: tags up to 0.7.0 published version-suffixed jars + (`connect-spigot-0.6.2.jar`), 0.7.1 onwards publish bare names. The repair + derives which from the tag's own release workflow, so a repair does not rename. +- `0.6.0` and `0.7.0` are the only zero-asset releases and are **not** + repairable. Their `bungee/build.gradle.kts` requests `bungeecord-proxy` with + transitive deps, and `net.md-5:bungeecord-{api,log,protocol,query}` at + `1.20-R0.3-SNAPSHOT` / `1.21-R0.1-SNAPSHOT` are 404 on every repository those + tags declare - only `bungeecord-proxy` itself survives upstream. `main` avoids + this with `includeTransitiveDeps = false`; back-porting that into a tag would + change what the tag builds, so it is a rewrite, not a repair. diff --git a/AGENTS.md b/AGENTS.md index 4edd68a2f..955665a1b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,8 +13,6 @@ plugin release, hub image rebuild, and production rollout as separate steps. ## Release Flow -- Stable plugin artifacts are published from GitHub releases managed by - `release-please.yml` on the `connect` branch. - Use Conventional Commit prefixes to drive releases: `fix:` for patch, `feat:` for minor, and `feat!:`/`BREAKING CHANGE:` for major. Non-release prefixes such as `chore:`, `docs:`, `ci:`, and `test:` @@ -23,76 +21,7 @@ plugin release, hub image rebuild, and production rollout as separate steps. tag/release, then dispatches `release.yml` on that tag so the JAR artifacts are uploaded. Do not manually bump versions or create release tags unless repairing automation. -- Its release-PR build is a manual dispatch whose native matrix checks are - audited on the captured head before merge. Do not mirror those checks into - synthetic check runs or legacy statuses; the boundary is pinned by - `core/.../release/ReleasePleaseCheckAuditTest`. -- The `release.yml` workflow uploads: - `connect-spigot.jar`, `connect-velocity.jar`, `connect-bungee.jar`, and - `LICENSE`. -- Pushes to `connect` still update the `latest-prerelease` release for - unreleased testing builds. -- After creating a release, verify the release is not draft/prerelease unless - intentionally so, and verify the asset digest/availability: - -```sh -gh -R minekube/connect-java release view --json tagName,targetCommitish,isDraft,isPrerelease,assets -curl -I -L --fail https://github.com/minekube/connect-java/releases/download//connect-velocity.jar -``` - -- `release.yml`'s "Verify published release assets" step re-reads each published - release from the API (never the upload step's own output) and requires the - same positive plugin-jar allowlist as `release-repair.yml`. Pinned by - `core/.../release/ReleaseAssetVerificationTest`; keep that test's step and - upload-step names in sync when editing `release.yml`. -- `release.yml`'s "Publish to Modrinth" step publishes the same jars to the - Modrinth listing (project id `PuSyuNRf`, `minekube-connect`), one version per - platform because Modrinth runs every validator whose loaders intersect the - declared loaders against every file in a version. It uploads the runner's - build output, never the release assets, and confirms each upload by reading - the stored version back and comparing sha1 and sha512. Its event condition is - the safety property: without it every push to `main` would publish a - development build to a public listing without anything going red. Pinned by - `core/.../release/ReleaseModrinthPublishTest`; keep that test's step names in - sync when editing `release.yml`. Dispatching `release.yml` at an OLD tag - publishes that tag to Modrinth - the listing is not a backfill target. -- `release.yml`'s "Publish to Hangar" step publishes to `minekube/Connect` and - syncs `.github/hangar-description.md`. `HANGAR_API_TOKEN` needs - `create_version` and `edit_page`. Hangar's platform mapping is Paper jar to - `PAPER`, Velocity jar to `VELOCITY`, and Bungee jar to `WATERFALL` (Hangar - has no BungeeCord platform). The step reads accepted platform versions at - publish time, floors Paper from `plugin.yml` and Velocity at the existing - 3.0 compatibility boundary. The three shaded jars exceed Hangar's - Cloudflare request limit as one multipart upload, so the version uses - immutable versioned GitHub release URLs, stores their SHA-256 values in the - public version description, and verifies GitHub's asset digest plus each - Hangar download's final bytes, size, content type, and JAR magic. Pinned by - `core/.../release/ReleaseHangarPublishTest`; keep its step names in sync. - -### Repairing a release that published no assets - -- Use `release-repair.yml` (default branch, manual dispatch). It builds at the - JDK that tag's own `release.yml` pinned and uploads only missing or broken - assets. Never dispatch `release.yml` at an old tag instead: it rewrites the - live `latest` release, dragging the stable `releases/download/latest/*.jar` - URLs backwards. Boundary and guards pinned by - `core/.../release/ReleaseRepairCapabilityTest`; keep its step names in sync. -- A repair EXECUTES old, unreviewed tagged source. Never collapse the workflow's - read-only `build` / write-only `publish` job boundary or substitute - step-level token scoping; the workflow comments and - `ReleaseRepairCapabilityTest` own the exact boundary, artifact-name - allowlist, race handling, and landed-verification details. Top-level - `permissions: {}` must remain explicit. -- Asset naming is per-era: tags up to 0.7.0 published version-suffixed jars - (`connect-spigot-0.6.2.jar`), 0.7.1 onwards publish bare names. The repair - derives which from the tag's own release workflow, so a repair does not rename. -- `0.6.0` and `0.7.0` are the only zero-asset releases and are **not** - repairable. Their `bungee/build.gradle.kts` requests `bungeecord-proxy` with - transitive deps, and `net.md-5:bungeecord-{api,log,protocol,query}` at - `1.20-R0.3-SNAPSHOT` / `1.21-R0.1-SNAPSHOT` are 404 on every repository those - tags declare - only `bungeecord-proxy` itself survives upstream. `main` avoids - this with `includeTransitiveDeps = false`; back-porting that into a tag would - change what the tag builds, so it is a rewrite, not a repair. +- Release-PR audit, asset verification, Modrinth/Hangar contracts, release repair: `.claude/skills/release/SKILL.md`. ## Public integration contract for login/auth plugins @@ -107,36 +36,7 @@ curl -I -L --fail https://github.com/minekube/connect-java/releases/download/ Date: Tue, 1 Sep 2026 23:41:30 +0200 Subject: [PATCH 2/3] docs: keep nested agent memory in AGENTS.md with CLAUDE.md symlinks Claude-Session: https://claude.ai/code/session_01Q2TdmQp4honrR7pBLf85JK --- spigot/AGENTS.md | 30 ++++++++++++++++++++++++++++++ spigot/CLAUDE.md | 31 +------------------------------ velocity/AGENTS.md | 31 +++++++++++++++++++++++++++++++ velocity/CLAUDE.md | 32 +------------------------------- 4 files changed, 63 insertions(+), 61 deletions(-) create mode 100644 spigot/AGENTS.md mode change 100644 => 120000 spigot/CLAUDE.md create mode 100644 velocity/AGENTS.md mode change 100644 => 120000 velocity/CLAUDE.md diff --git a/spigot/AGENTS.md b/spigot/AGENTS.md new file mode 100644 index 000000000..4bb859c46 --- /dev/null +++ b/spigot/AGENTS.md @@ -0,0 +1,30 @@ +# Spigot module agent instructions + +The root `AGENTS.md` still applies; this file adds Spigot/Paper-specific guidance. + +## Third-party platform APIs (ViaVersion & friends) + +- Connect runs against a wide range of server/plugin versions, so an API that + drifts across majors must not be bound at compile time. Resolve cross-version + accessors reflectively by name (newest first) and degrade to skipping the + workaround with a warning when no known accessor exists; see + `SpigotInjector#unwrapViaInitializer` and `SpigotInjectorViaLegacyPathTest`. +- Only plain Spigot/CraftBukkit takes Via's wrapping (legacy) injector path. On + Paper `BukkitViaInjector` registers a `ChannelInitializeListener` instead, which + Connect's local channel picks up for free - so Paper never exercises the unwrap. +- Injector failures must stay diagnosable: `ConnectPlatform.enable()` catches + `Throwable` (not `Exception`) because reflective signature drift arrives as an + `Error`, making it a logged, orderly injection failure rather than an unhandled + `Error` escaping `onEnable()`. `SpigotPlatform.enable()` still disables the plugin + on a false return, so the value is the diagnosable log line, not continued operation. + Guarded by `core/.../ConnectPlatformEnableFailureContainmentTest`. +- Compile-only platform deps live in `build-logic/.../Versions.kt` and are excluded + from the shaded jar by `provided(...)`; the `viaversion-bukkit` artifact declares + no transitive deps, so `viaversion-common` must be requested explicitly. +- Spigot NMS drift is expected when a Minecraft release renames an internal accessor: + `spigot/.../util/ClassNames.java` resolves server internals in one static initializer, + and failures are latched in the separate `NmsDiagnostics` class so they remain + available after `ClassNames` becomes erroneous. `SpigotPlatform.enable()` logs the + accessor and environment from that latch. Route new lookups through the + `NmsDiagnostics` helpers so they stay reportable; `spigot/.../util/NmsDiagnosticsTest` + guards this contract. diff --git a/spigot/CLAUDE.md b/spigot/CLAUDE.md deleted file mode 100644 index 4bb859c46..000000000 --- a/spigot/CLAUDE.md +++ /dev/null @@ -1,30 +0,0 @@ -# Spigot module agent instructions - -The root `AGENTS.md` still applies; this file adds Spigot/Paper-specific guidance. - -## Third-party platform APIs (ViaVersion & friends) - -- Connect runs against a wide range of server/plugin versions, so an API that - drifts across majors must not be bound at compile time. Resolve cross-version - accessors reflectively by name (newest first) and degrade to skipping the - workaround with a warning when no known accessor exists; see - `SpigotInjector#unwrapViaInitializer` and `SpigotInjectorViaLegacyPathTest`. -- Only plain Spigot/CraftBukkit takes Via's wrapping (legacy) injector path. On - Paper `BukkitViaInjector` registers a `ChannelInitializeListener` instead, which - Connect's local channel picks up for free - so Paper never exercises the unwrap. -- Injector failures must stay diagnosable: `ConnectPlatform.enable()` catches - `Throwable` (not `Exception`) because reflective signature drift arrives as an - `Error`, making it a logged, orderly injection failure rather than an unhandled - `Error` escaping `onEnable()`. `SpigotPlatform.enable()` still disables the plugin - on a false return, so the value is the diagnosable log line, not continued operation. - Guarded by `core/.../ConnectPlatformEnableFailureContainmentTest`. -- Compile-only platform deps live in `build-logic/.../Versions.kt` and are excluded - from the shaded jar by `provided(...)`; the `viaversion-bukkit` artifact declares - no transitive deps, so `viaversion-common` must be requested explicitly. -- Spigot NMS drift is expected when a Minecraft release renames an internal accessor: - `spigot/.../util/ClassNames.java` resolves server internals in one static initializer, - and failures are latched in the separate `NmsDiagnostics` class so they remain - available after `ClassNames` becomes erroneous. `SpigotPlatform.enable()` logs the - accessor and environment from that latch. Route new lookups through the - `NmsDiagnostics` helpers so they stay reportable; `spigot/.../util/NmsDiagnosticsTest` - guards this contract. diff --git a/spigot/CLAUDE.md b/spigot/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/spigot/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/velocity/AGENTS.md b/velocity/AGENTS.md new file mode 100644 index 000000000..d2e9bc7dd --- /dev/null +++ b/velocity/AGENTS.md @@ -0,0 +1,31 @@ +# Velocity module agent instructions + +The root `AGENTS.md` still applies; this file adds Velocity-specific guidance. + +## Defensive login re-assert (proxy) + +- Connect registers a second, late pre-login re-assert handler (see `VelocityLateEventRegistrar`). + Preserve the property + that makes it safe: it reacts **only** to Connect's own result on the event object, never + reads/links against/version-checks a third-party plugin, never overrides a deny, and no-ops + when nothing changed the decision. The existing `EARLY`/`LOWEST` handlers stay as they are - + this only adds a floor. If late-handler registration throws, `VelocityListenerRegistration` + catches `Throwable` locally, logs the failure, and continues with the pre-existing behavior; + ordinary listener registration remains unchanged. Authoritative: + `velocity/.../listener/VelocityLateEventRegistrar.java` (the two layered ordering levers and + why each exists), `VelocityLateReassertListener`, `BungeeLateReassertListener`, + `core/src/main/resources/proxy-config.yml` (`login-reassert`), `docs/login-plugin-integration.md`. +- **Do not bump velocity-api past `3.2.0-SNAPSHOT`** to reach `PostOrder.CUSTOM`: Velocity reads + the annotation while collecting a listener's methods, so an enum constant an older runtime + lacks throws `EnumConstantNotPresentException` there and kills *all* of Connect's handlers on + pre-2024-09-16 proxies, unguardably. The reflective short-`register` lookup buys the same + ordering with a catchable failure. +- Default profile scope is properties-only (skin) deliberately; restoring Connect's UUID breaks + login plugins that key their storage on the proxy UUID, so it is opt-in with its + `new-uuid-creator: MOJANG` prerequisite documented next to the option. +- `:velocity:eventOrderTest` is a separate source set running the **real** `VelocityEventManager` + and `PluginDependencyUtils` against a Velocity proxy jar pinned by sha256 (ivy repo in + `settings.gradle.kts`; PaperMC publishes no proxy artifact to Maven). Separate because that + shaded jar carries its own velocity-api and `com.velocitypowered.proxy` classes, which must not + shadow the 3.2.0 API or the stubs in `velocity/src/test`. It runs under `check`, so a + fill-data.papermc.io outage fails `./gradlew build` until the artifact resolves or is cached. diff --git a/velocity/CLAUDE.md b/velocity/CLAUDE.md deleted file mode 100644 index d2e9bc7dd..000000000 --- a/velocity/CLAUDE.md +++ /dev/null @@ -1,31 +0,0 @@ -# Velocity module agent instructions - -The root `AGENTS.md` still applies; this file adds Velocity-specific guidance. - -## Defensive login re-assert (proxy) - -- Connect registers a second, late pre-login re-assert handler (see `VelocityLateEventRegistrar`). - Preserve the property - that makes it safe: it reacts **only** to Connect's own result on the event object, never - reads/links against/version-checks a third-party plugin, never overrides a deny, and no-ops - when nothing changed the decision. The existing `EARLY`/`LOWEST` handlers stay as they are - - this only adds a floor. If late-handler registration throws, `VelocityListenerRegistration` - catches `Throwable` locally, logs the failure, and continues with the pre-existing behavior; - ordinary listener registration remains unchanged. Authoritative: - `velocity/.../listener/VelocityLateEventRegistrar.java` (the two layered ordering levers and - why each exists), `VelocityLateReassertListener`, `BungeeLateReassertListener`, - `core/src/main/resources/proxy-config.yml` (`login-reassert`), `docs/login-plugin-integration.md`. -- **Do not bump velocity-api past `3.2.0-SNAPSHOT`** to reach `PostOrder.CUSTOM`: Velocity reads - the annotation while collecting a listener's methods, so an enum constant an older runtime - lacks throws `EnumConstantNotPresentException` there and kills *all* of Connect's handlers on - pre-2024-09-16 proxies, unguardably. The reflective short-`register` lookup buys the same - ordering with a catchable failure. -- Default profile scope is properties-only (skin) deliberately; restoring Connect's UUID breaks - login plugins that key their storage on the proxy UUID, so it is opt-in with its - `new-uuid-creator: MOJANG` prerequisite documented next to the option. -- `:velocity:eventOrderTest` is a separate source set running the **real** `VelocityEventManager` - and `PluginDependencyUtils` against a Velocity proxy jar pinned by sha256 (ivy repo in - `settings.gradle.kts`; PaperMC publishes no proxy artifact to Maven). Separate because that - shaded jar carries its own velocity-api and `com.velocitypowered.proxy` classes, which must not - shadow the 3.2.0 API or the stubs in `velocity/src/test`. It runs under `check`, so a - fill-data.papermc.io outage fails `./gradlew build` until the artifact resolves or is cached. diff --git a/velocity/CLAUDE.md b/velocity/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/velocity/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file From ed5fd2859d1f27d20996eb5fa4ee32044d28b26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 1 Sep 2026 23:45:25 +0200 Subject: [PATCH 3/3] docs: make .agents/skills the primary skills dir with .claude/skills symlinks Claude-Session: https://claude.ai/code/session_01Q2TdmQp4honrR7pBLf85JK --- {.claude => .agents}/skills/release/SKILL.md | 0 .claude/skills/release | 1 + AGENTS.md | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) rename {.claude => .agents}/skills/release/SKILL.md (100%) create mode 120000 .claude/skills/release diff --git a/.claude/skills/release/SKILL.md b/.agents/skills/release/SKILL.md similarity index 100% rename from .claude/skills/release/SKILL.md rename to .agents/skills/release/SKILL.md diff --git a/.claude/skills/release b/.claude/skills/release new file mode 120000 index 000000000..14f8a38a8 --- /dev/null +++ b/.claude/skills/release @@ -0,0 +1 @@ +../../.agents/skills/release \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 955665a1b..b985ab991 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ plugin release, hub image rebuild, and production rollout as separate steps. tag/release, then dispatches `release.yml` on that tag so the JAR artifacts are uploaded. Do not manually bump versions or create release tags unless repairing automation. -- Release-PR audit, asset verification, Modrinth/Hangar contracts, release repair: `.claude/skills/release/SKILL.md`. +- Release-PR audit, asset verification, Modrinth/Hangar contracts, release repair: `.agents/skills/release/SKILL.md`. ## Public integration contract for login/auth plugins