This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push:
new 1797ede2a CAY-2978 AI skill: "cayenne-model-naming"
1797ede2a is described below
commit 1797ede2a4595816df0ce4423cef4a55a1ce07a6
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sat Jul 18 12:19:15 2026 -0400
CAY-2978 AI skill: "cayenne-model-naming"
---
RELEASE-NOTES.txt | 1 +
ai-plugin/README.md | 4 +
ai-plugin/references/model-naming-conventions.md | 174 +++++++++++++++++++++
ai-plugin/references/model-naming-rename-safety.md | 100 ++++++++++++
ai-plugin/skills/cayenne-db-import/SKILL.md | 1 +
ai-plugin/skills/cayenne-model-naming/SKILL.md | 148 ++++++++++++++++++
ai-plugin/skills/cayenne-modeling/SKILL.md | 1 +
7 files changed, 429 insertions(+)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 20e6ea637..72a68945d 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -25,6 +25,7 @@ CAY-2971 Remove extra spaces within SQL parenthesis
CAY-2972 Fewer parentheses in generated SQL
CAY-2974 CayenneSqlException with a reference to translated query
CAY-2975 Mnemonic table aliases in generated SQL
+CAY-2978 AI skill: "cayenne-model-naming"
Bug Fixes:
diff --git a/ai-plugin/README.md b/ai-plugin/README.md
index b49a16074..b257bddbc 100644
--- a/ai-plugin/README.md
+++ b/ai-plugin/README.md
@@ -24,6 +24,7 @@ This plugin teaches Claude how to:
- Edit Cayenne DataMap (`*.map.xml`) and project descriptor (`cayenne-*.xml`)
files a-la-carte (add entities, relationships, queries, embeddables).
- Reverse-engineer a database schema into a DataMap by driving CayenneModeler.
+- Polish the reverse-engineered Object-layer names (entities, attributes,
relationships) into idiomatic Java, fixing the few cases the deterministic name
generator can't.
- Regenerate Java entity classes from a DataMap.
- Bootstrap `CayenneRuntime` in a Java application and write `ObjectSelect` /
`SQLSelect` queries.
@@ -59,6 +60,7 @@ ai-plugin/
├── skills/ # auto-triggering workflows
│ ├── cayenne-modeling/ # edit *.map.xml and cayenne-*.xml
│ ├── cayenne-db-import/ # import a DB schema (Modeler GUI)
+│ ├── cayenne-model-naming/ # polish Obj-layer names after import / on
request
│ ├── cayenne-cgen/ # regenerate Java classes via MCP
│ ├── cayenne-modeler/ # open the GUI on a project
│ ├── cayenne-runtime/ # bootstrap CayenneRuntime in an app
@@ -68,6 +70,8 @@ ai-plugin/
├── datamap-schema.md
├── project-descriptor-schema.md
├── dbimport-config.md
+ ├── model-naming-conventions.md
+ ├── model-naming-rename-safety.md
├── cgen-config.md
├── runtime-api.md
├── query-api.md
diff --git a/ai-plugin/references/model-naming-conventions.md
b/ai-plugin/references/model-naming-conventions.md
new file mode 100644
index 000000000..566252c6d
--- /dev/null
+++ b/ai-plugin/references/model-naming-conventions.md
@@ -0,0 +1,174 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+# Obj-layer naming conventions — what to fix, what to leave alone
+
+Reference for the `cayenne-model-naming` skill. The governing rule: **the
Modeler already
+generates good names for the common case. This is a polish pass, not a
rewrite.** Read the
+"deterministic baseline" first so you can recognize a name that is already
correct and skip it.
+
+## The deterministic baseline (leave these names alone)
+
+Reverse engineering (`dbimport_run`, the Modeler's DB Import) generates the
Obj-layer names before
+you ever see the model, then de-duplicates any collisions. It already applies
these principles — and
+you must preserve them:
+
+1. **Obj names stay as close to the DB names as possible** — the object name
is a transliteration
+ of the table/column, not a re-invention.
+2. **Java identifier / class conventions** — classes PascalCase, properties
camelCase.
+3. **snake_case → camelCase / PascalCase** — split on `_`, drop the
underscores, camel-join.
+4. **Relationship names from entity name + cardinality** — see below.
+
+Concretely, the generator produces:
+
+| DB element | Rule | Result |
+|---|---|---|
+| `db-entity` name | stem, split on `_`, capitalize each token |
`ARTIST_GROUP` → `ArtistGroup` |
+| `db-attribute` name | split on `_`, camelCase | `FIRST_NAME` → `firstName` |
+| to-one relationship | FK column minus trailing `_ID`/`ID`; else target
entity name | `MANAGER_ID` → `manager` |
+| to-many relationship | English plural of the target entity name | `PAINTING`
→ `paintings` |
+| name collision within an entity | append a numeric suffix | `team`, `team1`,
`team2` … |
+
+Generation also collapses all-upper tokens to lowercase and preserves
already-mixed
+case. So `GameType`, `gameType`, `firstName`, `ArtistGroup`, `paintings`,
`manager` are **all
+already correct** — do not re-case, re-spell, re-pluralize, or "prettify"
them. If a name is a
+clean camelCase/PascalCase transliteration of its DB element with the right
cardinality, it is
+done. Touch nothing.
+
+## Where the deterministic algorithm falls short — the AI job
+
+The cases below are the ones we've identified where the generator can't do
better and your judgment
+adds real value. They are **illustrative, not exhaustive** — the generator is
a deterministic
+transliteration, so any place where a *human* reading the DB name would
produce a clearly better
+Java name than a mechanical `_`-split is fair game (§5). Focus your attention
on these gaps; don't
+touch names the baseline already got right.
+
+### 1. Run-together DB names with no separators
+
+Word-splitting happens **only on `_`**. A single-token, uniform-case DB name
has no boundary to
+split on, so a multi-word concept collapses into one lowercased chunk. This is
the classic case:
+
+| DB name | Generator output | Correct |
+|---|---|---|
+| `gametype` / `GAMETYPE` | `Gametype` | `GameType` |
+| `dateofbirth` | `Dateofbirth` | `dateOfBirth` |
+| `ordernumber` | `Ordernumber` | `orderNumber` |
+| `custaddr` | `Custaddr` | `CustomerAddress` (with expansion — see §3) |
+
+Split on the **real** word boundary, using domain knowledge, and re-apply the
Java convention
+(PascalCase for entities, camelCase for attributes and relationships). Only
split where you are confident a boundary
+exists — never invent one (`status` is not `sta` + `tus`; `metadata` is one
word). Names that are
+**already** mixed-case (`GameType`, `gameType`) were handled by the generator
— leave them.
+
+### 2. More than one relationship between the same two tables
+
+When two FKs point at the same target table (or two relationships otherwise
share a target),
+generation can't invent a role, so it disambiguates with numbers. But **the two
+directions are not equally affected** — the to-one and to-many sides are named
by different rules:
+
+- **to-many side always collides.** To-many naming ignores the FK column
entirely and always uses
+ the pluralized target entity name, so two relationships to the same target
both become e.g.
+ `games` / `games1` (or `people` / `people1`) no matter how well the FKs are
named. This is the
+ common case and the main reason this rule exists. Name each collection by
the **logically opposite**
+ role instead: on `Team`, the two reverse collections of `Game` become
`homeGames` / `awayGames`.
+
+- **to-one side usually does NOT collide.** To-one naming is FK-column-based —
it strips a trailing
+ `_ID`/`ID`, so distinct `*_ID` FKs already yield distinct, good names
(`HOME_TEAM_ID` → `homeTeam`,
+ `AWAY_TEAM_ID` → `awayTeam`). Leave those alone. It **only** collides in the
fallback path: when a
+ FK column does *not* end in `ID`/`_ID` (or is null / has no joins), the
generator drops to the
+ target entity name, so two such FKs both become e.g. `employee` /
`employee1`. There, derive the
+ role from the FK column yourself even though it lacks the `_ID` suffix
(`MANAGER` → `manager`,
+ `SUPERVISOR` → `supervisor`).
+
+So in the typical "two well-named `*_ID` FKs" model you'll rename **only the
to-many collections**
+(`games`/`games1`), and the to-one ends are already fine. When you do rename
both ends, give them
+matching opposite-role names so the pair is legible from either side
(`homeTeam` ↔ `homeGames`,
+`awayTeam` ↔ `awayGames`).
+
+### 3. Genuinely cryptic abbreviations (secondary, be conservative)
+
+Expand an abbreviation only when the win is clear **and** you apply it
consistently across the whole
+model: `qty` → `quantity`, `amt` → `amount`, `dob` → `dateOfBirth`. An
unfamiliar or ambiguous
+abbreviation stays as-is (case-normalized) rather than becoming a wrong guess.
If the model is full
+of domain-specific abbreviations, ask the user for a glossary instead of
guessing element by element.
+
+### 4. A common entity prefix leaking into relationship names
+
+Many schemas tag every table with the same prefix (`AA_CUSTOMER`, `AA_ORDER`,
or `os_t1`, `os_t2`).
+The reverse-engineering **"Strip from Table Names"** (`stripFromTableNames`)
setting handles this
+cleanly *when it's used*: entity names come through stripped (`AA_CUSTOMER` →
`Customer`) and there's
+no problem — leave that model alone.
+
+The case that needs you is when the prefix is **kept on the entity names**
(stripping was not
+configured — often intentional, treating the prefix as a class-name
namespace). Then the prefix
+**leaks into the relationship names**, which you almost never want:
+
+- to-many names are the pluralized target entity name; with the prefix kept,
the target's prefixed
+ name flows straight in → `aaOrders`, `aaCustomers`.
+- to-one names built from a prefixed FK column (`AA_CUSTOMER_ID`) carry it too
→ `aaCustomer`.
+
+A relationship name is a **role/property** on a class
(`order.getAaCustomer()`), and the shared
+prefix is pure noise there. **Strip the common prefix from the relationship
names** — `aaOrders` →
+`orders`, `aaCustomer` → `customer` — and mirror the change onto the paired
DbRelationship.
+
+**Leave the ObjEntity names as they are.** The prefix on the class names is
the user's choice (if
+they'd wanted it gone from entities they would have set
`stripFromTableNames`); renaming entities is
+a bigger, class-regenerating change. This case cleans relationship names only.
+
+### 5. Other cases — use judgment
+
+The three cases above don't exhaust the ways a purely mechanical
transliteration can miss. Whenever
+you spot a name where a human reading the underlying DB name would obviously
do better, and the fix
+is defensible (not a guess), apply the same conservative treatment. Some more
examples:
+
+- **Reserved words / illegal identifiers** the generator passed through — a
column literally named
+ `class`, `package`, `default`, or one starting with a digit needs a legal
Java name.
+- **Lost acronym casing** — `HTTPURL` → `Gametype`-style collapse loses the
acronym; `httpUrl` /
+ `url` may read better than `httpurl`.
+- **Plural table → singular entity** — a `CUSTOMERS` table yields `Customers`;
an entity is a single
+ row, so `Customer` is usually the intent (be careful: only when clearly a
pluralized table name,
+ and check for a resulting collision).
+- **Redundant entity-name prefix on an attribute** — `Artist.artistName` →
`name` — only when it's
+ clearly noise and doesn't collide.
+
+The bar is the same throughout: a clear, defensible improvement over the
mechanical output, applied
+consistently. When in doubt, leave the baseline name and surface the question
to the user rather than
+guessing.
+
+## Target forms (for the names you actually change)
+
+- **ObjEntity `name`** — PascalCase, singular preferred; keep it equal to the
`className` simple name.
+- **ObjAttribute `name`** — camelCase; strip type/Hungarian prefixes
(`strName` → `name`, `n_count`
+ → `count`) only when unambiguous.
+- **ObjRelationship, to-one** — singular camelCase role.
+- **ObjRelationship, to-many** — plural camelCase.
+- **DbRelationship `name`** — mirror the paired ObjRelationship name (see
below).
+
+## DbRelationship names
+
+A DbRelationship name is **arbitrary** — there is no DB metadata behind it
(unlike a DbEntity/
+DbAttribute, which mirror a real table/column). The working convention is that
a DbRelationship's
+name matches the ObjRelationship built on it, **per direction**. So when you
rename a to-one
+ObjRelationship to `homeTeam`, rename its backing single-hop DbRelationship to
`homeTeam` as well,
+and the reverse-direction pair (`homeGames`) likewise.
+
+Flattened (many-to-many) ObjRelationships traverse more than one DbRelationship
+(`db-relationship-path="artistGroupArray.toArtist"`), so there is no 1:1 name
to mirror — just keep
+each individual DbRelationship name sane on its own and fix any that are
numbered collisions.
+
+See `model-naming-rename-safety.md` for exactly what to update when you rename
any of these.
diff --git a/ai-plugin/references/model-naming-rename-safety.md
b/ai-plugin/references/model-naming-rename-safety.md
new file mode 100644
index 000000000..3d84d1e45
--- /dev/null
+++ b/ai-plugin/references/model-naming-rename-safety.md
@@ -0,0 +1,100 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+# Rename safety — cross-reference checklist
+
+Reference for the `cayenne-model-naming` skill. A model name is referenced
from several other places
+in the same DataMap (and from user Java code). Renaming an element without
updating its references
+leaves a dangling reference that fails Cayenne validation at load time, or
silently changes behavior.
+For every rename, walk the checklist for that element type. Element shapes are
in `datamap-schema.md`.
+
+## Golden rules
+
+- **Rename the element, don't repoint it.** You change a `name`; you never
touch the `db-attribute-path`
+ or `db-relationship-path` *targets* to point somewhere else. Those paths
only change when the thing
+ they point *to* was itself renamed (DbAttribute/DbRelationship), and then
only the matching segment.
+- **Names are unique within their scope.** Within an ObjEntity, attributes and
relationships share one
+ namespace; within a DbEntity, likewise; ObjEntity names are unique within
the DataMap; DbRelationship
+ names are unique within their source DbEntity. Never introduce a collision —
collisions are exactly
+ what produced the numbered names (`team1`) you're removing.
+- **Batch, then re-validate.** Apply all renames, then re-walk every checklist
below to confirm no
+ reference dangles.
+- **Name before you generate.** Renames must happen *before* `cayenne-cgen`,
so classes are generated
+ with the final names. Renaming after generation orphans the old `.java`
files.
+
+## Rename an ObjEntity `name` (`X` → `Y`)
+
+| Update | Where |
+|---|---|
+| `className` simple name | Same `<obj-entity>`. Keep it equal to the new
`name` → the generated class is `Y`. Old `X.java` / `_X.java` become orphaned;
regenerate and delete the stale pair. |
+| `source="X"` / `target="X"` | Every `<obj-relationship>` in the DataMap — on
**any** entity, not just this one. |
+| `root-name="X"` | Every `<query>` with `root="obj-entity"`. |
+| entity name in EJBQL | `<ejbql>` bodies (`select a from X a` → `... from Y
a`). |
+| `result-entity="X"` | `<query type="ProcedureQuery">`. |
+| Java | `ObjectSelect.query(X.class)` tracks `className`, so updating
`className` + regenerating covers it. Also fix string-based entity lookups
(`context.newObject("X")`, `objectSelect("X")`) and EJBQL strings in code. |
+
+The `dbEntityName` does **not** change — the DbEntity keeps its DB-derived
name.
+
+## Rename an ObjAttribute `name`
+
+| Update | Where |
+|---|---|
+| query qualifiers / orderings | `<qualifier>` and `<ordering>` bodies that
reference the old property name. |
+| EJBQL | `<ejbql>` paths (`a.oldName` → `a.newName`). |
+| Java | Generated getter/setter changes; fix `Expression`/`Property` paths
and `ObjectSelect` column refs in user code. |
+
+`db-attribute-path` is **unaffected** — it names the DB column, which didn't
change. Uniqueness is
+within the owning ObjEntity (attrs + rels).
+
+## Rename an ObjRelationship `name`
+
+| Update | Where |
+|---|---|
+| prefetches | `<prefetch>` bodies and any dotted path that traverses this
relationship. |
+| qualifiers / orderings / EJBQL | Expression paths that step through the old
relationship name. |
+| Java | Generated getter/setter changes; fix prefetch/expression paths in
user code. |
+
+`db-relationship-path` is **unaffected** — it names DbRelationships, not this
ObjRelationship's own
+name. Uniqueness is within the owning ObjEntity.
+
+## Rename a DbRelationship `name` (`a` → `b`)
+
+| Update | Where |
+|---|---|
+| `db-relationship-path` **segments** | Every `<obj-relationship>` whose
`db-relationship-path` contains `a` — **including inside dotted flattened
chains**. E.g. path `artistGroupArray.toArtist`, renaming `toArtist` → `b`
gives `artistGroupArray.b`. These references can live on entities other than
the DbRelationship's source. |
+
+No Java impact — DbRelationships are a DB-layer concept. Uniqueness is within
the source DbEntity
+(attrs + rels). Keep the name mirrored with its paired ObjRelationship per
direction (see
+`model-naming-conventions.md`).
+
+## Paired renames
+
+A single FK is usually four coordinated names: two DbRelationships (one per
direction) and two
+ObjRelationships built on them. When you fix a numbered-collision pair, rename
all four so both ends
+read as opposite roles and each ObjRelationship's name still mirrors its
DbRelationship:
+
+```
+DbRelationship ARTIST_ID (Game→Team, to-one) homeTeam
+DbRelationship ARTIST_ID (Team→Game, to-many) homeGames
+ObjRelationship (Game→Team) homeTeam (mirrors db-rel
homeTeam)
+ObjRelationship (Team→Game) homeGames (mirrors db-rel
homeGames)
+```
+
+After renaming a DbRelationship, remember its name appears in the *other*
direction's ObjRelationship
+`db-relationship-path` only when that path traverses it (flattened case) — a
simple one-hop
+ObjRelationship's `db-relationship-path` is just its own backing
DbRelationship's new name.
diff --git a/ai-plugin/skills/cayenne-db-import/SKILL.md
b/ai-plugin/skills/cayenne-db-import/SKILL.md
index d6bbe3477..609c53be1 100644
--- a/ai-plugin/skills/cayenne-db-import/SKILL.md
+++ b/ai-plugin/skills/cayenne-db-import/SKILL.md
@@ -118,6 +118,7 @@ Once the import succeeds:
- Tell the user to **save the project** in the Modeler if it is open (File →
Save). The dialog settings persist as a `<dbImport>` block inside the DataMap
for repeat runs.
- Hand off to `cayenne-cgen` to regenerate Java classes for the new/changed
entities. Quote the DataMap name so the cgen skill can pass it to `cgen_run`.
+- If the imported names look awkward (run-together words, `team1`-style
relationship names, a table prefix leaking into relationships), you can
**offer** the `cayenne-model-naming` skill to polish them — ideally before
cgen. Only run it if the user asks; do not invoke it automatically.
- If the DB has columns that don't follow the user's preferred naming,
recommend tweaking the naming strategy and re-running.
## Anti-patterns
diff --git a/ai-plugin/skills/cayenne-model-naming/SKILL.md
b/ai-plugin/skills/cayenne-model-naming/SKILL.md
new file mode 100644
index 000000000..1a4cabfa2
--- /dev/null
+++ b/ai-plugin/skills/cayenne-model-naming/SKILL.md
@@ -0,0 +1,148 @@
+---
+name: cayenne-model-naming
+description: "Use this skill to clean up Object-layer names in a Cayenne
DataMap — ObjEntity, ObjAttribute, and ObjRelationship names (plus the paired
DbRelationship names, which are arbitrary and conventionally mirror the
ObjRelationship) — so they read as descriptive, consistent Java. Trigger on
phrases like 'clean up the model names', 'fix the entity names', 'these names
look ugly', 'make the names descriptive', 'normalize the
ObjEntity/attribute/relationship names', 'why is this rela [...]
+---
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+# cayenne-model-naming
+
+Polish the Object-layer names in a Cayenne DataMap so they look like idiomatic
Java. This runs on top
+of the names CayenneModeler's reverse engineering already produced — which are
**good for the common
+case**. Your job is a surgical touch-up of the few names its deterministic
algorithm can't get right,
+**not** a rewrite. If a name is already a clean camelCase/PascalCase
transliteration of its DB element
+with the right cardinality, leave it exactly as it is.
+
+## Required reading
+
+- `${CLAUDE_PLUGIN_ROOT}/references/model-naming-conventions.md` — the
deterministic baseline (what's
+ already correct and must be left alone) and the specific gaps where your
judgment adds value.
+- `${CLAUDE_PLUGIN_ROOT}/references/model-naming-rename-safety.md` — the
cross-reference checklist for
+ every rename, so a rename never leaves a dangling reference.
+- `${CLAUDE_PLUGIN_ROOT}/references/datamap-schema.md` — element shapes for
`*.map.xml`.
+- `${CLAUDE_PLUGIN_ROOT}/references/project-layout.md` — locate the project
descriptor and DataMap.
+
+## Step 1 — Determine scope: changed elements vs the entire model
+
+There are two modes, and picking the right one is the most important decision
in this skill:
+
+- **Changed-only** (the default, e.g. when the user runs this after a
`cayenne-db-import`) — only
+ inspect the names on newly-added or just-modified elements. Everything else
in the model was
+ presumably reviewed already; don't churn it.
+- **Entire model** (rare) — inspect every Obj element. Use this **only** when
the user explicitly asks
+ for it ("clean up the whole model", "normalize all the names", "go over
every entity").
+
+`dbimport_run` does **not** report which entities changed (it reports change
counts though, so use it as an extra
+hint), so detect the changed set with git. Run these from the repo containing
the DataMap file:
+
+```bash
+# Is the DataMap under version control at all?
+git -C <repo-dir> rev-parse --is-inside-work-tree
+
+# What did the import change (unstaged), and did anything get staged already?
+git -C <repo-dir> diff -- <path/to/datamap>.map.xml
+git -C <repo-dir> diff --cached -- <path/to/datamap>.map.xml
+```
+
+From the diff, collect the added/modified `<obj-entity>`, `<obj-attribute>`,
`<obj-relationship>`, and
+`<db-relationship>` lines — that added/changed set is your scope. Notes:
+
+- If `git diff` is empty but the file is tracked, there's nothing new to clean
— say so and stop
+ (unless the user asked for an entire-model pass).
+- If the file is **untracked / brand-new** (no prior version in `HEAD`), every
element is effectively
+ "new" — treat it as an entire-model pass, but tell the user that's why.
+- **Fallback:** if it's not a git repository, or git isn't available, you
can't scope by diff — fall
+ back to the entire model and tell the user you couldn't narrow it down.
+
+## Step 2 — Find the names worth changing (and only those)
+
+For each in-scope element, compare against `model-naming-conventions.md`.
**Most names will already be
+correct — leave them.** Flag only the cases the deterministic generator can't
handle:
+
+1. **Run-together names with no separators** — `gametype` → `GameType`,
`dateofbirth` → `dateOfBirth`.
+ Split on a real word boundary you're confident about; never invent one.
+2. **Numbered collision names** (`games` / `games1`, `people` / `people1`)
from more than one
+ relationship between the same two tables. This almost always hits the
**to-many** side, which
+ ignores FK columns and just pluralizes the target — rename those by the
logical inverse role
+ (`homeGames` / `awayGames`). The **to-one** side is usually already fine
(it's FK-based:
+ `HOME_TEAM_ID` → `homeTeam`); only fix it in the fallback case where FK
columns lack an `_ID`
+ suffix and collapse to `employee` / `employee1`.
+3. **A common entity prefix leaking into relationship names** — when every
entity shares a prefix
+ that was **kept** on the class names (`AaCustomer`, `AaOrder`), the
relationship names inherit it
+ (`aaOrders`, `aaCustomer`). Strip the prefix from the relationship names
(`orders`, `customer`);
+ a relationship is a role/property, and the prefix is noise there. **Leave
the entity names alone**
+ — the prefix on classes is the user's choice, and renaming entities
regenerates classes.
+4. **Other clear, defensible improvements** — reserved words, lost acronym
casing, obvious cryptic
+ abbreviations applied consistently, plural-table-to-singular-entity.
Conservative by default; when
+ unsure, leave the baseline name and ask.
+
+Remember DbRelationship names are arbitrary and conventionally mirror the
paired ObjRelationship
+name — when you rename a relationship, plan the mirrored DbRelationship rename
too.
+
+## Step 3 — Present the rename plan
+
+Show the user a table of `old → new` grouped by entity, plus the mirrored
DbRelationship renames, and
+state plainly what you're **leaving alone** so it's clear this isn't a blanket
rewrite.
+
+- **Entire-model mode, or any rename of a name already referenced by user
Java/queries** → confirm
+ before applying. Renaming an ObjEntity regenerates its class and can break
existing code.
+- **Changed-only, right after an import** (new elements not referenced
anywhere yet) → low risk; apply
+ and report, but still list every rename you made.
+
+If there are many domain abbreviations to expand, ask the user for a glossary
rather than guessing.
+
+## Step 4 — Apply the renames safely
+
+Edit the `*.map.xml`. For **every** rename, walk the matching checklist in
+`model-naming-rename-safety.md` and update all references in the same edit:
+
+- ObjEntity → `className`, every `obj-relationship` `source`/`target`, query
`root-name`, EJBQL,
+ `result-entity`.
+- ObjRelationship → prefetch/expression/EJBQL paths (its
`db-relationship-path` is unaffected).
+- ObjAttribute → qualifier/ordering/EJBQL paths (its `db-attribute-path` is
unaffected).
+- DbRelationship → every `db-relationship-path` segment that names it,
including inside dotted
+ flattened chains.
+
+Keep every name unique within its scope, and preserve the file's existing
formatting and element
+order.
+
+## Step 5 — Validate and hand off
+
+- Re-walk the cross-reference checklist: does every `source`/`target`,
`root-name`, `db-relationship-path`
+ segment, and `className` still resolve?
+- Hand off to `cayenne-cgen` to (re)generate the Java classes with the final
names. **Naming must
+ happen before cgen** — if classes were already generated with the old names,
point out the now-stale
+ `.java` files so the user can regenerate/delete them.
+
+## Anti-patterns
+
+- **Don't rewrite names the generator already got right.** A camelCase
attribute, a PascalCase entity,
+ a plural to-many, an FK-derived to-one — all fine. This is polish, not a
redo.
+- **Don't clean the entire model when only an import changed things.** Scope
with `git diff`; default
+ to changed-only.
+- **Don't invent word splits.** Split `gametype` → `GameType` only when the
boundary is real; don't
+ break single words (`status`, `metadata`).
+- **Don't guess abbreviation expansions.** Ask for a glossary when a model is
full of them.
+- **Don't rename an ObjEntity without updating `className`, every reference,
and regenerating** — a
+ bare rename orphans the old class and dangles relationship `source`/`target`
and query `root-name`.
+- **Don't rename a DbRelationship without fixing every `db-relationship-path`
segment**, including
+ flattened chains on other entities.
+- **Don't repoint `db-attribute-path` / `db-relationship-path` targets.** You
rename the element; you
+ never change what a path points to.
+- **Don't run cgen yourself.** Hand off to `cayenne-cgen`.
diff --git a/ai-plugin/skills/cayenne-modeling/SKILL.md
b/ai-plugin/skills/cayenne-modeling/SKILL.md
index 8ee350599..5244b4f19 100644
--- a/ai-plugin/skills/cayenne-modeling/SKILL.md
+++ b/ai-plugin/skills/cayenne-modeling/SKILL.md
@@ -82,6 +82,7 @@ If anything fails, fix it before reporting done.
- **If you modified entities and the DataMap has a `<cgen>` block:** suggest
invoking the `cayenne-cgen` skill to regenerate Java classes. Mention which
entities are affected.
- **If the user added a new entity and there's no Java class yet:** same —
recommend `cayenne-cgen`.
- **If the user is asking about a full DB sync** (importing many tables,
syncing with a changed schema): hand off to `cayenne-db-import`. Do not try to
script this via XML edits.
+- **If the request is about making names descriptive/consistent** (cleaning up
reverse-engineered names, fixing `Gametype`/`team1`-style names, normalizing
entity/attribute/relationship names across the model): hand off to
`cayenne-model-naming` — it knows the naming conventions and the rename
cross-reference checklist.
- **If the change is structurally messy** (bulk renaming relationships, visual
graph rework): suggest the `cayenne-modeler` skill. Otherwise do not.
## Modeler coexistence