Skip to content

Publish Grails skills through SkillsJars - #15805

Open
jamesfredley wants to merge 2 commits into
8.0.xfrom
docs/publish-grails-skillsjars
Open

Publish Grails skills through SkillsJars#15805
jamesfredley wants to merge 2 commits into
8.0.xfrom
docs/publish-grails-skillsjars

Conversation

@jamesfredley

@jamesfredley jamesfredley commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

The Problem

SkillsJars discovers and publishes agent skills from a root-level skills/**/SKILL.md layout. Grails keeps its canonical skill sources under .agents/skills, which SkillsJars does not scan by default - and that directory also contains framework-contributor-only skills that should not be published to application developers.

The Fix

Expose only the app-facing Grails skills through the SkillsJars discovery path, without duplicating any content, while keeping contributor skills internal.

Published via root-level skills/ (real directories whose SKILL.md is a symlink to the canonical source):

  • skills/grails-developer/SKILL.md -> .agents/skills/grails-developer/SKILL.md
  • skills/grails-8-upgrade/SKILL.md -> .agents/skills/grails-8-upgrade/SKILL.md

Kept internal under .agents/skills only: hibernate-developer, test-fixer, violation-fixer, and other core-contributor workflow skills.

The change also:

  • uses real skill directories with SKILL.md symlinks so SkillsJars discovers normal skill folders;
  • adds a verifySkillsJarsSources Gradle task asserting the exact published skill set, symlink targets, git mode 120000, and canonical source markers;
  • excludes only the symlink alias paths from RAT (the licensed sources remain under .agents/skills);
  • documents the canonical location, the public discovery aliases, and the publish workflow.

How to publish after merge

Submit the SkillsJars form with GitHub Org apache and Repo grails-core. SkillsJars shallow-clones the public repo, scans skills/**/SKILL.md, and publishes Maven Central artifacts under com.skillsjars:

  • com.skillsjars:apache__grails-core__grails-developer:<date>-<commit>
  • com.skillsjars:apache__grails-core__grails-8-upgrade:<date>-<commit>

Testing

  • ./gradlew verifySkillsJarsSources passes; git diff --cached --check clean.
  • ./gradlew rat no longer reports the skills/** or tracked .claude/skills/** symlink aliases.

Refs #15454

Expose the canonical .agents/skills directory through a root skills symlink so SkillsJars can discover the existing skill definitions without duplicating files.

Add a verification task and publishing notes for submitting the skills from skillsjars.com.

Assisted-by: Hephaestus:openai/gpt-5.5 codex-review
Copilot AI review requested due to automatic review settings July 1, 2026 14:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds build-time verification and documentation to support publishing the repository’s agent skills via SkillsJars, using a root-level skills path intended for SkillsJars discovery.

Changes:

  • Add a verifySkillsJarsSources Gradle verification task to validate the skills publication path and presence of SKILL.md markers.
  • Exclude the skills path from Apache RAT scanning to avoid symlink/discovery-path complications.
  • Document the skills directory purpose and publishing workflow under .agents/skills.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
gradle/rat-root-config.gradle Excludes skills discovery path from RAT audit inputs.
build.gradle Adds verifySkillsJarsSources task to validate symlink target, git mode, and skill markers.
.agents/skills/README.md Documents the canonical skills location and SkillsJars publishing/verification steps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 49.4809%. Comparing base (b86be15) to head (fa7f03b).
⚠️ Report is 239 commits behind head on 8.0.x.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #15805        +/-   ##
==================================================
- Coverage     49.4820%   49.4809%   -0.0011%     
+ Complexity      16698      16697         -1     
==================================================
  Files            1947       1947                
  Lines           92474      92474                
  Branches        16152      16152                
==================================================
- Hits            45758      45757         -1     
+ Misses          39610      39609         -1     
- Partials         7106       7108         +2     

see 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Limit the SkillsJars publication path to Grails skills useful for building or upgrading end-user applications.

Keep internal Grails core development skills only under .agents/skills and expose the public skills through real skills directories containing SKILL.md symlinks to the canonical sources.

Assisted-by: Hephaestus:openai/gpt-5.5 codex-review
@jamesfredley

Copy link
Copy Markdown
Contributor Author

Updated this PR to publish only the app-facing Grails skills:

  • skills/grails-developer/SKILL.md -> .agents/skills/grails-developer/SKILL.md
  • skills/grails-8-upgrade/SKILL.md -> .agents/skills/grails-8-upgrade/SKILL.md

The internal Grails core skills remain under .agents/skills but are no longer exposed to SkillsJars. I also changed the shape from symlinked skill directories to real skill directories with SKILL.md symlinks so SkillsJars can discover/package them more reliably.

@testlens-app

testlens-app Bot commented Jul 1, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: fa7f03b
▶️ Tests: 37272 executed
⚪️ Checks: 44/44 completed


Learn more about TestLens at testlens.app.

@jamesfredley

Copy link
Copy Markdown
Contributor Author

Maintenance pass complete. Copilot generated no inline review comments, there are no review threads to resolve, Codecov/TestLens are green on the existing branch, and I reran ./gradlew.bat --no-daemon --no-parallel --max-workers=1 verifySkillsJarsSources locally. No code changes were needed.

@jdaugherty

Copy link
Copy Markdown
Contributor

I dug into this more and I don't believe we can use SkillsJars - it's acting a publisher directly to maven central. This goes against the ASF voting rules. Has there been any discussion on the mailing lists about this problem?

@jamesfredley

Copy link
Copy Markdown
Contributor Author

Follow-up on the ASF voting concern: this branch is still SkillsJars-based end to end. The changed files add verifySkillsJarsSources in build.gradle, expose skills/grails-developer and skills/grails-8-upgrade solely for SkillsJars discovery, and document com.skillsjars Maven Central coordinates in .agents/skills/README.md; the final search did not find a separate in-repo release path for these skill artifacts. Given that, we should not try to land this as-is. Proposed path: pause or close this PR, take the publish-to-Maven-Central and voting question to the dev list or Infra, and replace it with an ASF-controlled publication mechanism only if the project agrees on release and vote semantics. Until then, keeping the skills as source docs under .agents/skills avoids publishing artifacts outside the normal Apache release process.

@jamesfredley

Copy link
Copy Markdown
Contributor Author

I dug into this, and the factual premise is correct for the PR as written - but there's a compliant path that still achieves the goal, so I don't think we have to abandon skill distribution.

Why the objection holds for the current approach: the SkillsJars web service clones the repo, generates com.skillsjars:apache__grails-core__... coordinates, signs/bundles them, and uploads them to Maven Central itself. That's third-party publication of unvoted, commit-derived artifacts, outside the ASF Nexus / PMC release path. ASF release policy requires artifacts to be staged in ASF Nexus, approved by a PMC vote (>=3 binding +1, >72h), and promoted to Central through ASF infra (release policy, publishing guide). So routing through the SkillsJars publisher does conflict with the voting rules.

The compliant alternative (and I think what we actually want): build the skills jar in the grails-core build and publish it as a normal org.apache.grails:* artifact as part of the standard Apache Grails release candidate - staged, signed, and covered by the same PMC vote as every other grails-* jar. ASF policy expressly permits shipping binary jars alongside the voted source release when they're the same version and built from that release's source. That gives end users a Maven-Central-published skills artifact (closing the #15454 distribution blocker, on top of the already-merged skill content in #15798) with none of the direct-publish problem.

So the fix is to swap the distribution mechanism: package + publish the skills through the ASF release pipeline (voted) rather than via the SkillsJars external publisher. I'll rework the PR along those lines. No prior dev@ thread found on this - I'll start one so the packaging/coordinates get consensus before we wire it into the release.

@jdaugherty

Copy link
Copy Markdown
Contributor

I actually havea side branch that already publishes these as skill jars, i'm happy to push it too - but how do you consume it?

@jdaugherty

Copy link
Copy Markdown
Contributor

FYI: I pushed my rework branch (this was a start, i didn't finish it once I saw that skilljars couldn't pull other jars): https://github.com/apache/grails-core/tree/rework/publish-grails-skills

@jamesfredley jamesfredley moved this to Todo in Apache Grails Jul 24, 2026
@borinquenkid borinquenkid added this to the grails:8.0.0-RC1 milestone Jul 28, 2026

@borinquenkid borinquenkid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Overview: Exposes two app-facing skills (grails-developer, grails-8-upgrade) to the third-party SkillsJars discovery service via a new root-level skills/ directory, where each skill folder contains only a SKILL.md symlink back to the canonical .agents/skills/... source. Adds a verifySkillsJarsSources Gradle task to assert the published set and symlink integrity, and excludes the alias paths from RAT.

Code quality / correctness

  • Symlink targets (../../.agents/skills/<name>/SKILL.md) resolve correctly from skills/<name>/SKILL.md.
  • The verification task's dual-mode target read (Files.isSymbolicLink vs. plain-file fallback) sensibly handles checkouts where symlinks degrade to text files.
  • Minor fragility: expectedSkillTarget.substring(6) hardcodes the '../../' prefix length to locate the source file. Works today, but breaks silently (wrong path) if a target's depth ever changes. Resolving the path properly (e.g. Paths.get(...).resolve(target).normalize()) would be more robust than the magic-number substring.

Verification task isn't wired to check/CI

verifySkillsJarsSources is registered but not added as a dependency of check, build, or any GitHub Actions workflow — it's purely a manual, docs-invoked gate. If .agents/skills/grails-developer/SKILL.md is ever moved or renamed, the symlink breaks silently and CI stays green; nobody finds out until someone remembers to run the task by hand (or SkillsJars' scan quietly fails). Wiring this into check (or at least the RAT/CodeAnalysis job) would close that gap cheaply.

Test coverage

No automated test (e.g. Gradle TestKit) proves verifySkillsJarsSources actually fails when a symlink is broken/missing/wrong-target. Combined with the task not running in CI, the verification logic is currently unverified in both directions.

Governance note (not a code defect)

This introduces a new distribution channel: skill content sourced from apache/grails-core gets scanned and republished under com.skillsjars:apache__grails-core__* on Maven Central by a non-ASF third party. Nothing in the build itself talks to skillsjars.com (it's a manual post-merge submission per the new README), so there's no build-time supply-chain exposure — but worth an explicit maintainer/PMC nod that this publishing path is sanctioned, beyond code review (likely already implicit via #15454).

Suggested (non-blocking) actions

  1. Wire verifySkillsJarsSources into check so drift is caught automatically instead of relying on someone remembering to run it.
  2. Replace the substring(6) prefix assumption with proper path resolution.
  3. Confirm governance sign-off for the third-party publishing workflow.

Nothing here blocks the change — it's small, self-contained, and CI is green — but 1–2 are cheap hardening fixes and 3 is worth a one-line confirmation in the thread.

@jdaugherty

Copy link
Copy Markdown
Contributor

I do not think this should be in RC1. We should discuss this as I don't understand how publishing a skill jar is consumable by skill jars. From what I can tell, they have to publish. We need to discuss this one before going forward.

@borinquenkid

Copy link
Copy Markdown
Member

Following up on the ASF publishing concern — I think there's a compromise that sidesteps it entirely rather than trying to find an ASF-compliant way to route through SkillsJars.

The governance problem is coupled to SkillsJars specifically, not to distributing skills at all. Per @jdaugherty's finding on the rework branch, SkillsJars' own discovery/registry only indexes artifacts published through their scan-and-publish pipeline under com.skillsjars:* coordinates — publishing a jar ourselves under org.apache.grails:* through the normal ASF release train doesn't make it show up there. So there's no version of "publish through SkillsJars" that avoids the voting-rules conflict; the third-party publish step is the product.

Proposal: for grails-developer, stop trying to publish it at all — embed it directly into every app grails-forge (and/or the legacy create-app profiles) generates.

This repo already has the exact mechanism for this, and it ships zero new files through any third-party service:

  • grails-forge builds every generated file via Feature.apply(GeneratorContext)generatorContext.addTemplate(...) (DefaultProjectGenerator.java, Feature.java). Static verbatim files (gradle wrapper, GSP layouts, i18n props) are already added this way as classpath resources under grails-forge-core/src/main/resources/. A SKILL.md would work identically — no new distribution channel, no new build task, no RAT carve-out, nothing outside grails-core's own already-ASF-compliant release pipeline.
  • The legacy grails create-app path (grails-shell-cli + grails-profiles/*/skeleton) could get the same file for parity, since those skeletons are just static content copied into generated apps.

Why this is actually a better fit for grails-developer specifically: its whole purpose is guiding ongoing development of a Grails app — the exact moment create-app runs. Shipping it in the generated project puts it in front of 100% of new Grails developers automatically, with no dependency, no Maven coordinate, no separate install step.

Where this doesn't help — and I don't think we should pretend it does: grails-8-upgrade targets people upgrading an existing pre-8 app. A freshly generated app is already on 8, so embedding it at generation time doesn't reach that audience at all. I'd suggest we don't block on solving that one here — either leave it as source docs under .agents/skills (discoverable via the README this PR already added) until we have a real answer, or treat it as a separate follow-up once we know what channel we actually want for "skill for an existing project" distribution (which is the same question a compliant SkillsJars-alternative would have to answer anyway).

Concretely, I'd propose:

  1. Close/pause this PR as-is (per @jamesfredley's earlier comment) rather than continue reworking the SkillsJars integration.
  2. Open a new, small PR that adds a grails-developer feature/template to grails-forge (and optionally the legacy profiles) so it's seeded into every generated app.
  3. Leave grails-8-upgrade distribution as an open question, not a blocker on Create a "Grails 8 Upgrade" skill file that we offer end users #15454 — it was already satisfied by the source doc landing in Add Grails 8 upgrade skill #15798.

Happy to put together the grails-forge PR if folks are on board with this direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants