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

Reply via email to