This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/grails-intellij-plugin.git

commit f5a0509f69e78e29801d45c432facf347567a853
Author: James Daugherty <[email protected]>
AuthorDate: Sat Jul 18 15:23:34 2026 -0400

    Add ASF migration and improvement plans
---
 IMPROVEMENT-PLAN.md | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 MIGRATION-PLAN.md   | 363 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 747 insertions(+)

diff --git a/IMPROVEMENT-PLAN.md b/IMPROVEMENT-PLAN.md
new file mode 100644
index 0000000..107e4f3
--- /dev/null
+++ b/IMPROVEMENT-PLAN.md
@@ -0,0 +1,384 @@
+# Improvement Plan — grails-intellij-plugin
+
+Companion to `MIGRATION-PLAN.md`. That plan makes the repo ASF-compliant; this 
plan
+identifies functional gaps and sequences how to close them. Every phase below 
was
+detailed by examining the **grails-core monorepo** (surveyed 2026-07-07, 
`main` =
+`8.0.0-SNAPSHOT`); concrete class names, coordinates, and DSLs cited come from 
that
+source tree.
+
+**Two-plugin strategy** (per MIGRATION-PLAN amendment 2026-07-07): a *legacy* 
plugin
+(id `org.intellij.grails`, forked at the compliance tag, Grails 2.x–6.x, 
maintenance
+only) and a *new* plugin (fresh Apache id, Grails 7+ only). Unless marked 
otherwise,
+the phases below target the **new** plugin; the legacy plugin receives only 
Phase 1
+(platform health) on a recurring basis.
+
+> Version note: grails-core `main` is 8.0.0-SNAPSHOT (Groovy 5.0.7, Spring 
Boot 4.1,
+> Java 21, Gradle 9.6, Hibernate 7 default). Grails 7.x is the current release 
line
+> and the first under `org.apache.grails` coordinates. Where 7.x and 8.x differ
+> (artifact renames, gradle plugin ids), Phase 0 must verify against a real 
7.0.x app
+> — the facts below are authoritative for 8.x and directionally right for 7.x.
+
+## What we have today (surveyed 2026-07-07)
+
+- ~635 main sources (525 Java, 110 Kotlin) + 11 submodules; 184 test classes.
+- Strong core: full GSP language (lexer/parser/PSI, formatting, folding, 
completion,
+  HTML/Groovy/CSS/JS injection), 20+ Groovy member contributors (GORM 
criteria, named
+  queries, constraints), artefact handlers, URL-mapping references, Spring bean
+  discovery, run configurations, project view pane, GSON views support, 
templates.
+- Dual-era structure model: `Grails2Application`/`OldGrails*` 
(BuildConfig.groovy,
+  Gant) alongside `Grails3*` (Gradle-based).
+- Era artifacts: `pluginSupport/` for Searchable, Shiro, WebFlow, Resources 
(dead);
+  `testdata/` mock Grails **1.x** JARs.
+- Ultimate-only (`com.intellij.modules.ultimate` + 
javaee/persistence/jsp/spring/
+  database deps). Targets platform 2025.3, IntelliJ Platform Gradle Plugin 
2.11.0.
+
+---
+
+## Phase 0 — Baseline audit (applies to the shared codebase, pre-fork)
+
+Produce `GAPS.md` (or one GitHub issue per finding, labeled `audit`) before 
changing
+anything. Concrete checklist:
+
+**0.1 Build & platform debt**
+- [ ] `./gradlew check` — record failing/skipped tests per module.
+- [ ] `./gradlew verifyPlugin` against 2025.3 + latest EAP — inventory every
+      deprecated/internal/removed API usage (this is the legacy plugin's 
recurring
+      maintenance surface, so severity-rank it).
+- [ ] Confirm the `gen/` GSP lexer sources regenerate from their JFlex inputs; 
document
+      the regeneration command in `DEVELOPMENT.md`.
+
+**0.2 Real-app walkthrough** — generate three apps and open each with the 
plugin:
+1. Grails **7.0.x** release (via `https://start.grails.org` — forge production
+   endpoint) with the `web` application type,
+2. Grails **8.0.0-SNAPSHOT** (forge snapshot endpoint
+   `https://grailsforge-snapshot-cjmq3uyfcq-uc.a.run.app/`),
+3. Grails **6.x** (legacy baseline, for the legacy plugin's regression record).
+
+Walk this checklist per app and record works / broken / missing:
+- [ ] Project recognized as Grails; `grails-app/*` project view pane renders
+      
controllers/domain/services/taglib/views/conf/i18n/**init**/**utils**/assets
+      groups (init/ and utils/ are conventions the plugin may predate).
+- [ ] `Application.groovy` in `grails-app/init/` (extends
+      `grails.boot.config.GrailsAutoConfiguration`, runs via `GrailsApp.run`) 
is
+      recognized as the run entry point.
+- [ ] Domain class in `grails-app/domain/`: GORM static/instance member 
completion
+      (see Phase 2.3 list), constraints block completion, mapping block 
completion,
+      dynamic finder resolution, `where {}` closure typing.
+- [ ] Data Service interface with `@grails.gorm.services.Service(Book)`: 
implemented-
+      method recognition (no "cannot instantiate interface" errors, 
method-convention
+      completion).
+- [ ] Controller: action navigation, `respond`/`render`, interceptor `match()`/
+      `matchAll()` DSL, `UrlMappings.mappings` closure completion.
+- [ ] GSP: `g:` tag completion + navigation to taglib closures; `f:` namespace 
from
+      grails-fields; layout tags; `asset:` taglib (asset-pipeline).
+- [ ] `.gson` views: `json {}` / `model {}` DSL, `g`/`tmpl`/`hal`/`jsonapi` 
helpers;
+      `.gml` markup views (expected: **no support at all** — confirm).
+- [ ] `application.yml`: completion for `grails.*` keys; `runtime.groovy`/
+      `application.groovy` recognized; `spring/resources.groovy` bean DSL.
+- [ ] Run/debug: `bootRun` launch + debug attach, unit test run (Spock 2.4 
traits),
+      `integrationTest` task (from `TestPhasesGradlePlugin` source set
+      `src/integration-test/groovy`), `@Integration` test debug.
+- [ ] New-artefact actions produce Grails-7-correct file stubs (compare against
+      `grails-profiles/web/templates/artifacts/` in grails-core).
+- [ ] Version/framework detection: does the plugin light up at all when only
+      `org.apache.grails` coordinates are present? (Expected failure: 
detection almost
+      certainly keys on `org.grails` group ids — confirm exact detection code 
paths in
+      `Grails3ApplicationProvider`, `GrailsUtils`, SDK/library detection.)
+
+**0.3 Scoping counts (feed Phases 3–4)**
+- [ ] Count references from core packages into Ultimate-only APIs (spring, 
javaee,
+      persistence, database plugins) — data for the Community-edition split.
+- [ ] Map which classes are reachable only from `OldGrails*`/Gant/BuildConfig 
paths —
+      the safe-deletion list for the new plugin's pruning step.
+- [ ] List every stub index / file-based index and its version key (needed for 
the
+      package-rename impact assessment in the new plugin).
+
+---
+
+## Phase 1 — Platform health (both plugins; recurring for legacy)
+
+- [ ] Fix all `verifyPlugin` **error**-level findings (removed/internal APIs).
+- [ ] Provide new-UI (20×20) icon variants for the gutter/toolwindow/artefact 
icons;
+      audit `GrailsIconProvider` and `GroovyMvcIcons` (generated) coverage.
+- [ ] Verify dumb-mode safety of the heavier contributors (member contributors 
and
+      reference providers should be index-tolerant).
+- [ ] Encode the supported-platform window in `pluginVerification.ides` 
(proposal:
+      latest two IDE majors for the new plugin; "current major only, best 
effort" for
+      legacy).
+- [ ] Legacy-specific: define the recurring platform-bump procedure (bump
+      `sinceBuild`, run verifier, fix errors, release `<branch>.x.y`) and put 
it in
+      the legacy repo's `DEVELOPMENT.md`.
+
+---
+
+## Phase 2 — Grails 7+/8 support (new plugin; the headline work)
+
+### 2.1 Framework detection & versioning
+
+Detection must work from Gradle dependency data (the plugin already has a
+`GrailsProjectResolverExtension`); the authoritative signals from grails-core:
+
+- [ ] **Gradle plugin ids**: `org.apache.grails.gradle.grails-app`, `-web`,
+      `-plugin`, `-gsp`, `-gson`, `-markup`, `-profile` (Apache era). Legacy 
signal
+      for ≤6: `org.grails.grails-*` ids.
+- [ ] **BOM**: `platform("org.apache.grails:grails-bom:<v>")` (variants:
+      `grails-bom-hibernate5/-hibernate7/-micronaut`). BOM version = Grails 
version.
+- [ ] **Coordinates**: any `org.apache.grails[.*]:grails-*` dependency ⇒ 
Apache era.
+      Sub-groups to recognize: `org.apache.grails.data` (GORM), `.views`, 
`.web`,
+      `.testing`, `.i18n`, `.gradle`, `.profiles`. `RENAME.md` in grails-core 
is the
+      full old→new artifact map — encode it as a table in one detection class
+      (e.g. `grails-plugin-controllers` → `grails-controllers`,
+      `grails-datastore-gorm` → `grails-datamapping-core`).
+- [ ] **Corroborating signals** (don't hard-require): Groovy group
+      `org.apache.groovy` (4/5) vs `org.codehaus.groovy` (≤3); Spring Boot 3 
(Grails
+      6/7) vs 4.1+ (Grails 8); Java baseline 21 (8.x).
+- [ ] Replace/extend `org.jetbrains.plugins.grails.util.Version` handling so 
7.x/8.x
+      version strings parse and feature-gate correctly (e.g. `.gml` support 
only when
+      `grails-views-markup` present).
+
+### 2.2 Project structure model
+
+- [ ] Single `GrailsApplication` implementation family for Gradle-era apps 
(7+);
+      recognize 
`grails-app/{controllers,domain,services,taglib,views,conf,i18n,
+      init,utils,assets}` plus `src/main/groovy`, `src/test/groovy`,
+      `src/integration-test/groovy` (registered by `TestPhasesGradlePlugin`) 
and
+      `src/functional-test/groovy` (functional/Geb phase) source sets.
+- [ ] `grails-app/conf`: `application.yml` (primary), `application.groovy`,
+      `runtime.groovy`, `logback-spring.xml`, `spring/resources.groovy` (bean 
DSL —
+      keep existing support, verify against Boot 3/4 world).
+- [ ] Plugin projects: `*GrailsPlugin` class in `grails-app/init` (or 
`src/main/
+      groovy`), lifecycle hooks `doWithSpring`/`doWithDynamicMethods`/
+      `doWithApplicationContext`/`onChange`/`onConfigChange`/`onShutdown` — 
verify
+      completion inside these closures.
+
+### 2.3 Artefact handlers & GORM code insight
+
+Artefact type list to support (from grails-core artefact handlers):
+Controller, Domain, Service, TagLib, UrlMappings (fixed name, lives in
+`controllers/`), BootStrap (fixed name, `init/`), Codec, Interceptor, 
Application
+(`init/`), plus **Data Service** (new):
+
+- [ ] **Data Services**: `@grails.gorm.services.Service(Domain)` on interfaces 
/
+      abstract classes in `grails-app/services/`. IDE must: suppress
+      "unimplemented method" errors (AST-generated impl), complete/resolve
+      convention methods (`get`, `list`, `count`, `save`, `delete`, 
`find*By*`),
+      and gutter-mark the generated implementation.
+- [ ] **GormEntity member injection** — align the existing 
NonCodeMemberProcessor
+      lists with GORM 9's `org.grails.datastore.gorm.GormEntity` trait. 
Instance:
+      `save/insert/merge/delete/refresh/attach/discard/lock/ident/isDirty/
+      getDirtyPropertyNames/addTo*/removeFrom*/instanceOf`. Static: 
`get/read/load/
+      
proxy/getAll/list/first/last/count/exists/saveAll/deleteAll/where/whereLazy/
+      
whereAny/createCriteria/withCriteria/findWhere/findAllWhere/findOrCreateWhere/
+      
findOrSaveWhere/executeQuery/executeUpdate/withTransaction/withNewTransaction/
+      withSession/withNewSession/withStatelessSession` + dynamic finders.
+- [ ] **Dynamic finder grammar** (`org.grails.datastore.gorm.finders.*`): 
prefixes
+      `findBy/findAllBy/countBy/listOrderBy/findOrCreateBy/findOrSaveBy`; 
comparators
+      
`Equal/NotEqual/InList/NotInList/Between/InRange/Like/Ilike/Rlike/GreaterThan
+      [Equals]/LessThan[Equals]/IsNull/IsNotNull/IsEmpty/IsNotEmpty`; 
`And`/`Or`
+      combinators; trailing Map args 
(`max/offset/sort/order/ignoreCase/fetch/cache/
+      lock`).
+- [ ] **where/DetachedCriteria**: type the `where {}` closure delegate as
+      `grails.gorm.DetachedCriteria<T>` with property-operator DSL (`==`, 
`!=`, `<`,
+      `in`, `like`, …) and `and/or/not/order/join/projections` blocks.
+- [ ] **Constraints block**: built-in list from
+      `org.grails.datastore.gorm.validation.constraints.*` — nullable, blank, 
size,
+      minSize, maxSize, min, max, range, inList, matches, email, url, 
creditCard,
+      validator, unique, notEqual, scale. (Diff against the plugin's current
+      hardcoded list — it dates from Grails 2.)
+- [ ] **mapping block**: Hibernate mapping DSL from
+      `org.grails.orm.hibernate.cfg.Mapping` (table, id/generator, version, 
cache,
+      sort, batchSize, dynamicInsert/Update, per-property column/sqlType/lazy/
+      formula/index).
+- [ ] **jakarta, not javax**: domain/JPA integration (the `hibernate/` 
submodule and
+      persistence facet code) must use `jakarta.persistence.*`.
+
+### 2.4 Web layer: GSP, taglibs, views, URL mappings, interceptors
+
+- [ ] **Taglib registry refresh** — the `g:` namespace tag set should be 
sourced from
+      the app classpath (taglib classes with `@TagLib`/namespace field) rather 
than a
+      static list; current core taglibs in grails-core:
+      `ApplicationTagLib` (link, createLink, resource, img, set, cookie, 
header,
+      external, join, meta, applyCodec, flashMessages), `FormTagLib` (form,
+      textField, passwordField, hiddenField, checkBox, textArea, datePicker,
+      submitButton, uploadForm, …), `ValidationTagLib` (message, hasErrors,
+      eachError, fieldError, fieldValue), `FormatTagLib` (formatDate, 
formatNumber,
+      formatBoolean, encodeAs), `UrlMappingTagLib` (include, paginate),
+      `JavascriptTagLib`, `RenderTagLib` (render, renderException), layout tags
+      
(layoutBody/layoutHead/layoutTitle/pageProperty/ifPageProperty/applyLayout —
+      Sitemesh2 still, packaged as `org.apache.grails:grails-layout`).
+- [ ] **`f:` namespace** (grails-fields 
`grails.plugin.formfields.FormFieldsTagLib`):
+      `with`, `all`, `field`, `table`, `display` — grails-fields is now in the
+      monorepo, so treat it as first-party.
+- [ ] **`.gson` JSON views**: verify against current `grails-views-gson` —
+      script base `grails.plugin.json.view.JsonViewWritableScript`, `json {}` /
+      `json(list)` entry points, implicit `model {}`, helpers `g`
+      (`GrailsJsonViewHelper`), `hal`, `jsonapi`, `tmpl` (`TemplateRenderer`);
+      `_partial` template resolution and `/object/_object.gson` convention.
+- [ ] **`.gml` markup views — NEW file type**: register extension, inject 
Groovy
+      `MarkupTemplateEngine` DSL, `g` helper (`GrailsViewHelper`). Gate on
+      `grails-views-markup` on classpath.
+- [ ] **UrlMappings DSL**: completion inside `static mappings = {}` — 
pattern-string
+      entries, `constraints {}`, `group`, `resources`/nested resources, 
response-code
+      mappings (`"404"(...)`) , attrs 
`controller/action/view/method/namespace`.
+      Cross-reference: mapping ↔ controller action ↔ view.
+- [ ] **Interceptors**: `grails.artefact.Interceptor` trait members 
(`match(Map)`,
+      `matchAll()`, matcher chain `.excludes/.except`, lifecycle
+      `before/after/afterView`) — completion + "matched controllers" 
navigation.
+- [ ] **Codecs**: keep `encodeAs*` dynamic-method support; built-ins HTML, 
HTML4,
+      JavaScript, HTMLJS, URL, Raw (`org.grails.plugins.codecs`).
+
+### 2.5 Config files
+
+- [ ] `application.yml`/`application.groovy` key completion sourced from
+      `spring-configuration-metadata.json` on the app classpath (Boot 
metadata) plus
+      the Grails namespace defaults seen in the profile skeletons
+      (`grails.codegen.defaultPackage`, `grails.profile`, `grails.views.gsp.*`,
+      `info.app.*` with `@…@` Gradle interpolation).
+- [ ] Retire `BuildConfig.groovy`/`Config.groovy` analysis from the new plugin
+      (moves to legacy only).
+
+### 2.6 Run, test, and command execution
+
+- [ ] Run config delegates to Gradle `bootRun` (Boot 3/4 `BootRun` task; 
dev-mode PID
+      file `run-app.pid`); debug via standard Gradle/JVM attach.
+- [ ] Test integration: unit-test traits from `grails-testing-support-*`
+      (`grails.testing.services.ServiceUnitTest`, 
`grails.testing.web.controllers.
+      ControllerUnitTest`, `grails.testing.gorm.DomainUnitTest`, 
`grails.testing.web.
+      taglib.TagLibUnitTest`, 
`grails.testing.web.interceptor.InterceptorUnitTest`,
+      `grails.testing.web.UrlMappingsUnitTest`), 
`@grails.testing.mixin.integration.
+      Integration` for integration tests, Geb 
(`org.apache.groovy.geb:geb-spock`)
+      for functional. Spock 2.4. Run line markers on all of these; route
+      `integrationTest` through the Gradle task, not a JUnit config.
+- [ ] Command layer: replace `Grails3CommandProvider` sources with (a) 
profile-yml
+      commands (`create-controller`, `create-service`, `create-taglib`,
+      `create-interceptor`, `create-domain-class`, `create-integration-test` 
from
+      `grails-profiles/*/commands/`) and (b) forge-cli commands
+      (`org.grails.forge.cli.command.Create*Command`, incl. `create-job`,
+      `create-scaffold-controller` era equivalents). Scaffolding: 
`@grails.plugin.
+      scaffolding.annotation.Scaffold` recognition + `generate-*` commands.
+- [ ] New-project wizard: call the forge HTTP API (`https://start.grails.org`; 
API
+      `/application-types` for types/features/gorm-impls/JDKs, POST `/create` 
for
+      generation) with an offline fallback via 
`org.grails.forge:grails-forge-cli`.
+      This replaces the existing JetBrains-era forge module builder endpoint.
+
+### 2.7 Test fixtures
+
+- [ ] Replace mock Grails 1.x JARs in `testdata/` with fixtures from a pinned
+      Grails 7/8 release (align with MIGRATION-PLAN Phase 3's source-release
+      decision; prefer test-time fetch of real `org.apache.grails` artifacts).
+- [ ] Add whole-project test fixtures for the 7.x layout (web app + plugin 
project +
+      data-service usage) so modern support has regression coverage — today's 
suite
+      guards the legacy era.
+
+---
+
+## Phase 3 — Creating the new plugin (fork + prune + rename)
+
+Per the two-plugin strategy, this is no longer a "should we remove?" debate — 
it's
+the construction step of the new plugin, immediately after the compliance fork:
+
+- [ ] New plugin id (proposal: `org.apache.grails.intellij`) + fresh 
Marketplace
+      listing under the Apache vendor; declare mutual exclusivity with
+      `org.intellij.grails` (both directions).
+- [ ] Package rename `org.jetbrains.plugins.grails.*` / `org.jetbrains.plugins.
+      groovy.mvc.*` → `org.apache.grails.intellij.*`: mechanical, but must 
update
+      every FQN in `plugin.xml`/module XMLs, bump all stub-index version keys
+      (inventory from Phase 0.3), and accept fresh persisted settings (new 
plugin =
+      no migration burden).
+- [ ] Delete, using the Phase 0.3 reachability map:
+      - `Grails2Application`, `OldGrails*` (application, command provider, node
+        provider), module-based structure sync paths in
+        `MvcModuleStructureSynchronizer` (keep what Gradle-era import still 
uses),
+      - BuildConfig.groovy/Config.groovy analysis, Gant script support,
+        `grails-compiler-patch` + `jps-plugin` if they only serve the 
pre-Gradle
+        compile pipeline (verify in Phase 0),
+      - `pluginSupport/`: searchable, shiro, webflow, resources (keep 
assetPipeline,
+        spock, buildTestData only if still ecosystem-relevant — asset-pipeline 
yes,
+        spock yes),
+      - Grails 1.x/2.x testdata and the tests that exercise deleted paths.
+- [ ] Re-run the full test suite + `verifyPlugin`; the survivor test set 
defines the
+      new plugin's guarded behavior.
+- [ ] Legacy repo: no code changes beyond the Phase 1 maintenance policy; add a
+      README banner and Marketplace description pointing Grails 7+ users to 
the new
+      plugin.
+
+---
+
+## Phase 4 — Audience: Community-edition support (new plugin)
+
+`com.intellij.modules.ultimate` + javaee/jsp/spring/database deps make the 
plugin
+invisible to IntelliJ Community users. Using the Phase 0.3 reference counts:
+
+- [ ] Restructure `plugin.xml` so the core loads on Community: GSP language + 
editing,
+      artefact recognition/navigation, GORM completion, run configs, project 
view,
+      forge wizard.
+- [ ] Move to `<depends optional="true" config-file=…>` modules: Spring bean
+      integration (`com.intellij.spring`), JPA/persistence + the `hibernate/`
+      submodule (`com.intellij.persistence`, `com.intellij.javaee.jpa`), 
database
+      integration (`com.intellij.database`), JSP-adjacent GSP features
+      (`com.intellij.jsp`), JS/CSS injection (already optional).
+- [ ] The existing submodule layout (`hibernate/`, `langInjection/`, `i18n/`, 
…) is
+      most of the needed seam — the work is breaking compile-time references 
from
+      core packages into Ultimate-only APIs (count known from Phase 0.3).
+- [ ] Decide the Marketplace story: one artifact with optional deps 
(recommended)
+      vs. core+addon pair. Default to one artifact unless the audit shows an
+      irreducible Ultimate core.
+
+---
+
+## Phase 5 — Feature roadmap (new plugin, post-stabilization)
+
+File as issues, prioritize by community feedback. Grounded candidates:
+
+- [ ] Controller action ↔ GSP view ↔ URL-mapping line markers (the mapping DSL 
data
+      from 2.4 makes the triad resolvable).
+- [ ] Data Service gutter: `@Service(Book)` interface method → generated 
implementer
+      semantics (`org.grails.datastore.gorm.services.ServiceImplementer` 
variants).
+- [ ] Inspections: unknown `application.yml` key (vs. Boot metadata), 
constraint on
+      nonexistent property, dynamic finder referencing nonexistent property, 
taglib
+      call with unknown attribute, `@Integration` test in unit source set.
+- [ ] Quick-doc for GORM injected members and `g:`/`f:` tags (tag descriptions 
can be
+      generated from the taglib source Javadoc in grails-core).
+- [ ] Live/file template refresh from `grails-profiles/*/templates/artifacts/`
+      (authoritative artifact stubs), including Data Service and Interceptor
+      templates; `.gml` markup-view support (2.4).
+- [ ] Refactoring: artefact rename cascades (controller rename → views 
directory +
+      URL mappings + tests).
+- [ ] Micronaut-BOM awareness (`grails-bom-micronaut`, `micronaut-context` 
config
+      keys) — low priority until user demand shows.
+
+---
+
+## Phase 6 — Community & sustainability (both repos)
+
+- [ ] Issue templates/labels mirroring grails-core `.github` conventions; 
triage the
+      inherited Marketplace/YouTrack feedback into GitHub issues split across 
the two
+      repos (legacy bugs vs. new-plugin features).
+- [ ] `DEVELOPMENT.md` per repo: `runIde` sandbox, test layout, GSP lexer
+      regeneration (`gen/` provenance), platform-bump procedure (legacy), 
release
+      procedure pointer to MIGRATION-PLAN Phase 6.
+- [ ] Marketplace listings: legacy = renamed "Grails (legacy, Grails 2–6)" with
+      pointer; new = Apache branding, fresh screenshots against a Grails 7 app.
+- [ ] ≥2 committers named as maintainers per plugin line; legacy sunset 
criteria
+      reviewed yearly (cap `untilBuild` + archive when Grails 6 usage 
justifies).
+
+---
+
+## Sequencing & dependencies
+
+```
+MIGRATION-PLAN Phases 1–4 (compliance, this repo)
+        │
+        ├── Phase 0 audit (read-only, runs in parallel) ──► GAPS.md
+        │
+first compliant release (feature-frozen re-release of the JetBrains plugin)
+        │
+        ├── fork ──► legacy repo (keeps org.intellij.grails) ──► Phase 1 
recurring
+        │
+        └── this repo = new plugin
+                Phase 3 (prune + rename + new id)
+                Phase 1 (platform health)
+                Phase 2 (Grails 7+/8 support)   ◄── grails-core facts above
+                Phase 4 (Community edition)
+                Phase 5 (features) / Phase 6 (community)
+```
diff --git a/MIGRATION-PLAN.md b/MIGRATION-PLAN.md
new file mode 100644
index 0000000..797eab2
--- /dev/null
+++ b/MIGRATION-PLAN.md
@@ -0,0 +1,363 @@
+# ASF Migration Plan — grails-intellij-plugin
+
+Bring `apache/grails-intellij-plugin` (imported from 
`JetBrains/intellij-obsolete-plugins`)
+into compliance with ASF policy and the grails-core conventions, and stand up 
GitHub
+Actions for building and publishing.
+
+Unlike prior plugin migrations, this repo **stays standalone** — it cannot 
merge into the
+grails-core monorepo because it builds against the IntelliJ Platform Gradle 
Plugin
+(`org.jetbrains.intellij.platform` 2.11.0), not the Grails BOM/shared-gradle 
world. We
+therefore *port* the grails-core compliance/CI conventions here rather than 
rewiring onto
+shared config. The `mono-repo-integration` skill's principles still apply 
where relevant
+(license headers, RAT excludes for templates/test data, CI gating publish on 
tests, no
+blind deletion of imported infra).
+
+## Current state (surveyed 2026-07-06)
+
+- **Build**: Gradle Kotlin DSL, root plugin + 11 subprojects (`copyright`, 
`coverage`,
+  `gradle-tooling`, `grails-compiler-patch`, `grails-rt`, `hibernate`, `i18n`,
+  `jps-plugin`, `langInjection`, `maven`, `testFramework`). IntelliJ Platform 
Gradle
+  Plugin 2.11.0, Kotlin 2.3.0, platform 2025.3.1 (`sinceBuild=253`), Java 21 
(8/11 for the
+  runtime/JPS legacy modules). `pluginVersion=253.0.0`.
+- **Identity**: plugin id `org.intellij.grails`, vendor **JetBrains**, packages
+  `org.jetbrains.plugins.grails`.
+- **Licensing**: `LICENSE.txt` = Apache-2.0 full text. **No NOTICE.** UPDATED
+  (2026-07-18): the ~909 source files that carried the abbreviated JetBrains 
one-line
+  header (`Copyright 2000-20xx JetBrains s.r.o. and contributors. Use of this 
source
+  code is governed by the Apache 2.0 license.`) now carry the **full standard 
Apache 2.0
+  license header** (the license Appendix boilerplate) with the JetBrains 
copyright line
+  preserved — *not* the ASF "Licensed to the Apache Software Foundation" 
header. ~44
+  files still have no header (testdata fixtures, `.ft`/`.gsp` templates, 
properties,
+  XML). `gen/` is generated code; `testdata/` contains mock Grails 1.x JARs 
(test
+  fixtures, not shipped).
+- **CI/publishing**: none. No `.github/`, no `.asf.yaml`, no publish or 
signing config.
+
+## Open decisions (resolve before/while executing)
+
+1. **Header strategy for JetBrains-copyright files.** RESOLVED (2026-07-18): 
**no
+   software grant / IP clearance is recorded yet**, so the licensing terms in 
the
+   per-file headers must not be changed — the files stay attributed to 
JetBrains and
+   must NOT receive the ASF "Licensed to the Apache Software Foundation" 
header until a
+   grant is provided. What we did instead: expanded every abbreviated 
JetBrains header
+   to the full standard Apache 2.0 license header (Appendix boilerplate) while 
keeping
+   the `Copyright 2000-20xx JetBrains s.r.o. and contributors.` line. This 
satisfies
+   RAT's stock Apache-2.0 detection with no custom matcher. The wholesale 
ASF-header
+   replacement (former "option A") moves to Phase 9 (post-grant follow-up) and 
is
+   blocked on the software grant being recorded with the ASF Secretary.
+2. **Plugin id & Marketplace listing.** RESOLVED (2026-07-06): JetBrains has 
already
+   handed the Marketplace listing over. The plugin id `org.intellij.grails` is 
permanent —
+   Marketplace ids cannot be changed on an existing listing, and the IDE 
matches updates
+   by id, so a new id would orphan the entire install base and break 
`<depends>` in
+   third-party plugins. Keep the id forever; update the listing's name/vendor 
to Apache.
+   AMENDED (2026-07-07): under the two-plugin strategy below, the id and 
existing listing
+   stay with the **legacy** plugin line; the new Grails 7+ plugin gets a fresh 
Apache id.
+   The Java packages (`org.jetbrains.plugins.grails.*`) CAN be renamed 
(nothing external
+   keys on them), but it means renaming every class reference in 
plugin.xml/module XMLs,
+   resets FQN-keyed persisted state for users, and forces stub-index version 
bumps — and
+   ASF policy does not require it (cf. Apache Groovy keeping 
`org.codehaus.groovy`). If
+   wanted, do it as a dedicated follow-up effort after this migration, never 
mixed into
+   the compliance commits.
+3. **Develocity / TestLens**: grails-core CI wires `DEVELOCITY_ACCESS_KEY` and 
TestLens.
+   Decide whether this repo joins the same Develocity instance; if not, drop 
those steps.
+4. **Snapshot distribution**: attach the plugin ZIP as a workflow artifact on 
every main
+   build (simplest, recommended), and/or publish to a Marketplace 
`eap`/`snapshot`
+   channel.
+5. **Versioning**: keep the `<platform-branch>.x.y` scheme (`253.0.0`) or 
align with a
+   Grails-style scheme. Marketplace convention favors the platform-branch 
scheme — keep it.
+
+---
+
+## Two-plugin strategy (added 2026-07-07)
+
+Decision: maintain **two plugins** — a *legacy* plugin forked from this 
codebase that
+keeps JetBrains-platform compatibility for older Grails versions (2.x–6.x), 
and a *new*
+plugin that supports only Grails 7+ (Apache-era coordinates, pruned codebase). 
This is
+feasible; the constraints and the plan:
+
+- **Fork point**: fork *after* Phases 1–4 land in this repo (metadata, 
headers, RAT,
+  identity), so both repos inherit the compliance work and neither redoes 
header
+  expansion. Note: the post-grant ASF header replacement (Phase 9) will have 
to run in
+  **both** repos if the fork happens before the grant lands — keep the 
replacement
+  script shared. Feature divergence starts only after the first compliant 
release.
+- **Repos**: this repo (`apache/grails-intellij-plugin`) becomes the home of 
the **new**
+  plugin; request a second repo from INFRA (suggested: 
`grails-intellij-plugin-legacy`)
+  created as a fork at the compliance tag. Both carry the same `.asf.yaml`/CI 
shape;
+  the legacy repo's workflows are identical minus the feature-oriented jobs.
+- **Marketplace identity**:
+  - *Legacy* keeps plugin id `org.intellij.grails` and the transferred listing 
— the
+    existing install base auto-updates into it and nobody's Grails 2–6 support 
silently
+    disappears. Listing description gains a prominent pointer to the new 
plugin.
+  - *New* plugin registers a fresh id (suggested: 
`org.apache.grails.intellij`) and a
+    new listing under the Apache vendor. Because it has no persisted-state or 
listing
+    history, it is free to rename packages `org.jetbrains.plugins.grails` →
+    `org.apache.grails.intellij.*` from day one — this retires the 
package-rename
+    question deferred in decision 2 (it applies to the new plugin only; legacy 
never
+    renames).
+- **Mutual exclusivity**: both plugins register the GSP language, file type, 
run
+  configuration type, and stub indices — the platform cannot load both. 
Declare the
+  conflict (plugin.xml `<incompatible-with>` on each other's id; verify the 
mechanism
+  during implementation — if id-level incompatibility isn't honored on the 
target
+  platform, add a startup check that notifies and disables) and document 
"install one,
+  not both" in both listings. If dual-install demand materializes, the escape 
hatch is
+  extracting a shared GSP-core plugin both depend on — out of scope for now.
+- **Support policy** (propose to PMC alongside the fork):
+  - Legacy: **maintenance-only**. Platform-compat fixes (`sinceBuild` bumps, 
Plugin
+    Verifier errors), critical bugs. No new features. `verifyPlugin` CI 
against each new
+    IDE major is the tripwire. Versioning continues the 
`<platform-branch>.x.y` scheme.
+    Sunset criteria (e.g. when Grails 6 usage drops, cap `untilBuild` and 
archive) to be
+    revisited yearly.
+  - New: all feature work (see `IMPROVEMENT-PLAN.md`; its Phase 3 "legacy 
pruning"
+    becomes the *creation step* of the new plugin — prune Grails 1/2 support, 
dead
+    plugin integrations, and `OldGrails*` structure model immediately after 
the fork).
+- **Cost acknowledged**: double release engineering (two votes, two Marketplace
+  publishes) and platform bumps twice a year on the legacy line. Mitigated by 
the
+  maintenance-only policy and shared workflow templates.
+
+## Phase 1 — ASF repo metadata
+
+One commit: "ASF Compliance - repository metadata".
+
+- **`NOTICE`** (new, required by policy). Model on grails-core's:
+
+  ```
+  Apache Grails IntelliJ Plugin
+  Copyright 2025-2026 The Apache Software Foundation
+
+  This product includes software developed at
+  The Apache Software Foundation (http://www.apache.org/).
+
+  This product includes software originally developed by JetBrains s.r.o.
+  and contributors (https://www.jetbrains.com/).
+  Copyright 2000-2026 JetBrains s.r.o. and contributors.
+  Licensed under the Apache License, Version 2.0.
+  ```
+
+  Do **not** say "donated to the ASF" until the software grant is recorded; 
add that
+  wording (confirmed against the grant text) in Phase 9.
+- **`HEADER`** (new): copy grails-core's root `HEADER` file verbatim — the 
canonical ASF
+  header text ("Licensed to the Apache Software Foundation (ASF) under one or 
more
+  contributor license agreements…"). Pre-grant it applies to **new files only**
+  (decision 1); post-grant it becomes the single source of truth for the 
wholesale
+  replacement in Phase 9.
+- **`LICENSE`**: rename `LICENSE.txt` → `LICENSE` (grails-core convention). 
Create a
+  `licenses/` directory only if/when non-Apache third-party assets are 
identified (none
+  found so far — icons are JetBrains-authored and covered by the grant; 
testdata JARs are
+  Apache-2.0 Grails/Spring artifacts and are excluded from the source distro 
anyway —
+  see Phase 3 decision on testdata JARs).
+- **`.asf.yaml`**: adapt grails-core's — `description`, `homepage:
+  https://grails.apache.org/`, notifications to `[email protected]` /
+  `[email protected]`, default-branch protection ruleset
+  (restrict deletion/force-push), and a `release` deployment environment with 
the same
+  required reviewers (jdaugherty, matrei, jamesfredley, sbglasius) to gate the 
release
+  workflow.
+- **`README.md`** (new): what the plugin is, supported IntelliJ versions, build
+  instructions, ASF links (no release history section, per grails-core docs 
convention).
+- Optional: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` pointers to the main 
project.
+
+## Phase 2 — License headers (pre-grant form)
+
+One commit: "ASF Compliance - license headers". **Constraint (decision 1): 
until a
+software grant is recorded, headers keep JetBrains attribution and standard 
Apache 2.0
+terms — no ASF licensing language on JetBrains-authored files.**
+
+- DONE (2026-07-18, uncommitted): mechanically expanded every abbreviated 
JetBrains
+  header (the `//`, `/* ... */`, `<!-- -->`, and `#` one-line forms, any year 
range,
+  ~909 files) to the full standard Apache 2.0 header (Appendix boilerplate:
+  "Licensed under the Apache License, Version 2.0 …") with the original
+  `Copyright 2000-20xx JetBrains s.r.o. and contributors.` line and year range
+  preserved. Files that already carried the full old-style (2007/2008) 
JetBrains Apache
+  header were left untouched; a duplicated embedded header in `GspUtil.java` 
was
+  de-duplicated. The expansion script lives in the session scratchpad — commit 
a copy to
+  `etc/bin/expand-jetbrains-headers.py` for auditability.
+- DONE (2026-07-18, uncommitted): headered the previously-unheadered 
JetBrains-authored
+  files that RAT flags — grails-rt runtime sources/scripts, 
`GrailsStandardDSL.gdsl`,
+  the non-generated `META-INF/*.xml` descriptors, `liveTemplates/gsp.xml`, 
`grails.tld`,
+  bundle/log4j/gradle `.properties`, and the JPS services file — all with the 
same
+  JetBrains-attributed Apache 2.0 header. Files that are RAT-excluded (testdata
+  fixtures, templates, forms, description HTML, `MANIFEST.MF`) were left alone.
+  Genuinely new ASF-authored files get the canonical ASF header.
+- **New files** authored after the import (by ICLA-covered contributors) use 
the
+  canonical ASF header from the root `HEADER` file — no grant needed for new
+  contributions.
+- Do **not** header: `gen/**` (generated), `testdata/**` fixtures whose 
content *is* the
+  test input (adding headers can break parser/position-sensitive tests — same 
reason
+  grails-core excludes test resources), file templates under 
`resources/fileTemplates/**`
+  (they materialize inside end-user projects — grails-core's "templates people 
are
+  expected to use" rule), `.form` GUI-builder files (machine-edited XML), 
inspection/
+  intention description HTML (grails-core excludes `**/*.html`).
+- Verify with a grep audit: every non-excluded source file contains either the 
full
+  Apache 2.0 header (JetBrains-attributed) or the ASF header (new files).
+
+## Phase 3 — RAT verification in the build
+
+One commit: "ASF Compliance - RAT license audit".
+
+Because Phase 2 expanded the headers to the standard Apache 2.0 boilerplate, 
RAT's
+stock license matcher recognizes them — **no custom JetBrains matcher is 
needed**.
+
+DONE (2026-07-18, uncommitted): `org.nosphere.apache.rat` 0.8.1 added to the 
root
+`plugins {}` block with a kts `tasks.rat { excludes... }` config 
(grails-core's shape,
+translated; every exclude justified in a comment; `outputs.upToDateWhen { 
false }`).
+`./gradlew rat` passes: 927 files audited, 0 unknown licenses. Notable 
repo-specific
+exclude: `.intellijPlatform/**` (IntelliJ Platform Gradle Plugin's local cache 
of
+generated Ivy descriptors). Remaining details below for reference.
+
+- Add the `org.nosphere.apache.rat` plugin (grails-core pins **0.8.1** via
+  `apacheRatVersion` on the buildSrc classpath; this repo has no buildSrc, so 
declare
+  `id("org.nosphere.apache.rat") version "0.8.1"` in the root `plugins {}` 
block) and port
+  grails-core's `gradle/rat-root-config.gradle` shape (this repo is Kotlin 
DSL; either
+  keep a Groovy `gradle/rat-root-config.gradle` applied via `apply(from = 
...)` — closest
+  to grails-core — or translate to kts).
+- Excludes tailored to this repo (mirroring grails-core's categories):
+  - generated: `gen/**`
+  - test fixtures: `testdata/**` (includes the mock Grails JARs; binary anyway)
+  - templates shipped into user projects: `resources/fileTemplates/**`,
+    `resources/liveTemplates/**` (verify), `**/*.ft`, template `.gsp`
+  - images: `**/*.png`, `**/*.svg`, `**/*.ico`
+  - html/json descriptions: `**/*.html`, 
`**/spring-configuration-metadata.json`-style
+    JSON (JSON can't carry headers)
+  - infra: `.asf.yaml`, `.github/**`, `**/.gitignore`, `gradlew*`, 
`**/wrapper/**`,
+    `.idea/**`, `**/*.iml`, `out/**`, `build/**` + per-subproject build dirs 
(use the
+    same `rootProject.subprojects.collect{...}` trick), `**/*.form` if not 
headered
+  - `MIGRATION-PLAN.md` (this file) until deleted
+- Set `outputs.upToDateWhen { false }` (never cache audits), like grails-core.
+- Iterate `./gradlew rat` until clean; every exclude must carry a 
justification comment
+  (grails-core style) — no blanket excludes to make it pass.
+- Decision to record: whether the `testdata/**/*.jar` binaries may remain in 
the
+  **source release** at all. ASF source releases must not contain compiled 
code without
+  justification; likely fine as clearly-labeled test fixtures, but flag it for 
the PMC
+  and document in the RAT config comment. Alternative: fetch them at test time.
+
+## Phase 4 — Identity & conventions cleanup
+
+One commit: "ASF Compliance - plugin identity".
+
+- `plugin.xml`: `vendor` → Apache Software Foundation (Grails), vendor URL
+  `https://grails.apache.org/`, plugin description/links updated; keep plugin 
id per
+  decision 2.
+- Gradle `group` coordinates: `org.intellij.grails` → 
`org.apache.grails.intellij` (or
+  similar) for the module groups — these are internal (nothing publishes to 
Maven), so
+  this is low-risk; record the rename in the repo README. Package renames
+  (`org.jetbrains.plugins.grails` → `org.apache...`) are **out of scope** for 
this
+  migration (huge churn, breaks settings/serialized state and plugin.xml 
extension
+  points); revisit later if the PMC wants it.
+- Apply the grails-core best-practice conventions that translate: 4-space 
indent, no
+  wildcard imports, Apache header on every **new** file. CodeNarc does not 
apply (Java/
+  Kotlin codebase); consider ktlint/detekt later, not in this migration.
+
+## Phase 5 — GitHub Actions: build & verification CI
+
+One commit: "ASF Compliance - CI workflows". Follow grails-core's patterns: 
all actions
+**pinned to full commit SHAs**, `concurrency` group per workflow+ref, publish 
gated on
+test jobs via `needs:` **and** `if:` result guards.
+
+- **`.github/workflows/gradle.yml`** — on push/PR to `main` (+ 
`workflow_dispatch`):
+  - `build`: checkout (`actions/checkout` @ pinned v6.0.2), 
`actions/setup-java`
+    (liberica, 21), `gradle/actions/setup-gradle` @ pinned v6.1.0 with
+    `cache-provider: basic` (the MIT-licensed provider — grails-core 
deliberately avoids
+    the proprietary default) and Develocity key per decision 3; run
+    `./gradlew check buildPlugin`.
+  - `verify`: `./gradlew verifyPlugin` (IntelliJ Plugin Verifier against the
+    `sinceBuild`/target IDE matrix — configure `pluginVerification.ides` in 
the build to
+    cover 2025.3 and latest EAP; this is the IntelliJ-world analog of 
grails-core's
+    functional matrix and the skill's "reproduce every test axis" rule).
+  - `snapshot` (main-branch pushes only): upload the `buildPlugin` ZIP as a 
workflow
+    artifact; optionally `publishPlugin` to a Marketplace snapshot channel 
(decision 4).
+    `needs: [build, verify]` **and** `if:` guards on both results — publishing 
must be
+    blocked when tests fail.
+- **`.github/workflows/rat.yml`** — copy grails-core's nearly verbatim (adapt 
branch
+  filter to `main`): run `./gradlew rat`, upload the HTML report artifact, 
inject the
+  report into the job summary.
+- Later/optional, mirroring grails-core: `codeql.yml`, dependabot config.
+
+## Phase 6 — GitHub Actions: ASF release & Marketplace publishing
+
+One commit: "ASF Compliance - release workflow". Modeled on grails-core's 
`release.yml`
+and the `apache/grails-github-actions` composite actions, adapted because the 
deliverable
+is a plugin ZIP, not Maven artifacts (no Nexus staging at all):
+
+ASF policy framing: the **official release is the signed source archive**, 
voted on by
+the PMC and published to `dist.apache.org`; the plugin ZIP on JetBrains 
Marketplace is a
+convenience binary.
+
+- Trigger: GitHub `release` event (tag `vX.Y.Z`), same as grails-core.
+- Jobs:
+  1. `publish` — build: `apache/grails-github-actions/pre-release@asf` (sets 
release
+     version), `./gradlew buildPlugin`, JetBrains `signPlugin` (Marketplace 
code-signing
+     chain: `CERTIFICATE_CHAIN`, `PRIVATE_KEY`, `PRIVATE_KEY_PASSWORD` 
secrets), attach
+     ZIP to the GitHub release (`softprops/action-gh-release` @ pinned SHA).
+  2. `source` — produce the source distribution (git-archive based zip 
excluding
+     `.github` etc.), generate `sha512`, GPG-sign (`.asc`) with the release 
manager's key
+     from the Grails `KEYS` file.
+  3. `upload` — `needs: [publish, source]`; svn-commit source + convenience 
artifacts to
+     `dist.apache.org/repos/dist/dev/grails/` for the vote.
+  4. `release` — `environment: release` (required reviewers from `.asf.yaml`; 
this is the
+     post-vote gate): move dev → `dist/release`, then `./gradlew 
publishPlugin` to
+     JetBrains Marketplace (`PUBLISH_TOKEN` / 
`ORG_GRADLE_PROJECT_...PublishingToken`
+     secret — the listing is already transferred, so this just needs a token 
minted
+     under the ASF-controlled Marketplace account and stored as a repo secret).
+  5. `close` — `apache/grails-github-actions/post-release@asf` (bump to next 
snapshot,
+     housekeeping).
+- Secrets to provision (INFRA/PMC): GPG signing key material, 
`GRAILS_GHTOKEN`-style
+  token if needed, JetBrains Marketplace token + code-signing certs, 
Develocity key
+  (optional).
+- Also add `release-abort`/`release-notes` style helpers later if useful; not 
required
+  for the first release.
+
+## Phase 7 — Artifact compliance
+
+Part of Phase 5/6 commits or separate:
+
+- Ensure `LICENSE` and `NOTICE` are packaged **inside the plugin ZIP / jar 
META-INF**
+  (grails-core gets this from its shared build-logic plugins; here add a small 
Gradle
+  convention — `processResources`/`buildPlugin` copy spec — in the root build).
+- Optional stretch goals, ported from grails-core if the PMC wants parity: 
CycloneDX
+  SBOM (`SbomPlugin.groovy`-style `LICENSE_MAPPING` for transitives), 
reproducible-build
+  config (stable timestamps/ordering in the ZIP), `verify.sh`-style release 
verification
+  scripts. Not blockers for the first compliant release.
+
+## Phase 8 — Verify
+
+- [ ] `./gradlew rat` passes locally and in CI; every exclude justified in a 
comment.
+- [ ] Header audit grep: no non-excluded source file lacks the full Apache 2.0 
header
+      (JetBrains-attributed on imported files, ASF on new files).
+- [ ] No imported file carries ASF licensing language (grep for "Licensed to 
the Apache
+      Software Foundation" must only hit files authored post-import) — guard 
until the
+      software grant is recorded.
+- [ ] `NOTICE` + `LICENSE` present at root **and** inside the built plugin ZIP.
+- [ ] `.asf.yaml` valid (notifications, branch protection, release 
environment).
+- [ ] `./gradlew check buildPlugin verifyPlugin` green in CI on a PR.
+- [ ] Snapshot publish job gated on build+verify via `needs:` and `if:` result 
guards.
+- [ ] All workflow actions pinned to commit SHAs.
+- [ ] Dry-run the release workflow to a test tag (skip the `release` 
environment job).
+- [ ] PMC sign-off on: pre-grant header posture (JetBrains-attributed Apache 
2.0),
+      plugin id/Marketplace transfer, testdata JARs in the source release.
+
+## Phase 9 — Post-grant follow-up (BLOCKED until software grant recorded)
+
+Do **not** start any of this until the JetBrains software grant is recorded 
with the
+ASF Secretary and IP clearance is filed (incubator IP-clearance form, even for 
a
+non-incubating PMC receiving a codebase).
+
+- Wholesale replacement of the JetBrains-attributed Apache 2.0 headers with the
+  canonical ASF header from `HEADER` (script in `etc/bin/`, one mechanical 
commit —
+  the former "option A"). Run in the legacy repo too if the fork has happened.
+- Move the JetBrains copyright into `NOTICE` and add the "donated to the ASF" 
wording,
+  confirmed against the grant text.
+- Update the Phase 8 grep guard: the ASF header becomes required on all 
non-excluded
+  files; the JetBrains-attributed form should no longer appear.
+- Revisit RAT config: no changes expected (both header forms are Apache-2.0 to 
RAT),
+  but re-run the audit after the replacement.
+
+## Suggested commit sequence
+
+1. `ASF Compliance - license headers` (expand JetBrains headers to full Apache 
2.0 —
+   done in working tree + commit the `etc/bin` expansion script)
+2. `ASF Compliance - repository metadata` (NOTICE, LICENSE rename, .asf.yaml, 
README,
+   HEADER)
+3. `ASF Compliance - RAT license audit` (rat plugin + config, passing)
+4. `ASF Compliance - plugin identity` (vendor, groups)
+5. `ASF Compliance - CI workflows` (gradle.yml, rat.yml)
+6. `ASF Compliance - release workflow` (release.yml, packaging LICENSE/NOTICE)
+7. *(post-grant, Phase 9)* `ASF Compliance - ASF license headers` (replacement 
+ NOTICE
+   update)


Reply via email to