This is an automated email from the ASF dual-hosted git repository.
diveshdut pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-plugins.git
The following commit(s) were added to refs/heads/trunk by this push:
new bcfce34fb OFBIZ-13367 - Add REST API and service-contract guidance to
AI agent skills (#325)
bcfce34fb is described below
commit bcfce34fb0d21b19e24f488a161afaabf74be1dd
Author: Divesh Dutta <[email protected]>
AuthorDate: Fri Jul 10 16:02:22 2026 +0530
OFBIZ-13367 - Add REST API and service-contract guidance to AI agent skills
(#325)
## What changed
- updated the existing AI agent skill files so REST guidance now points
to the OFBiz framework REST support
- added a dedicated `manage-rest-api` skill for REST API design,
wrapper-service decisions, composite key handling, and contract-safe API
changes
- improved related REST guidance in `manage-api-integration`
- added supporting improvements in:
- `manage-services`
- `manage-groovy`
- `coding-standards`
- `manage-cache-and-performance`
- updated `README.md` to clarify that these skills work best with Apache
OFBiz `trunk`
- updated `SKILLS_SUMMARY.md` to include the new REST skill
## Why
Since the REST feature moved from the OFBiz plugins repository into the
OFBiz framework, the existing skill guidance needed to be updated so it
refers to REST support from the framework instead of treating it as a
plugin-local capability.
This PR also adds a dedicated REST skill based on practical experience
building APIs for Manufacturing PWA applications. That guidance is
intended to help agents make better decisions around:
- when to expose native OFBiz services directly
- when to introduce thin wrapper services
- how to preserve stable API contracts
- how to handle multi-column keys and typed service boundaries
In addition, this PR includes related improvements in the
service-definition and Groovy skill files so the guidance is more
consistent across REST, service, and query implementation patterns.
## Impact
These changes should help AI agents produce OFBiz code that is more
accurate and more maintainable when working with:
- framework REST APIs
- service definitions and typed contracts
- Groovy service implementations
- query efficiency and cache-related decisions
---
ai-agent-skills/README.md | 5 ++
ai-agent-skills/SKILLS_SUMMARY.md | 1 +
ai-agent-skills/coding-standards/SKILL.md | 2 +
ai-agent-skills/manage-api-integration/SKILL.md | 22 ++++--
.../manage-cache-and-performance/SKILL.md | 6 ++
ai-agent-skills/manage-groovy/SKILL.md | 22 ++++++
ai-agent-skills/manage-rest-api/SKILL.md | 88 ++++++++++++++++++++++
.../references/rest-contract-checklist.md | 39 ++++++++++
.../references/service-wrapper-patterns.md | 67 ++++++++++++++++
ai-agent-skills/manage-services/SKILL.md | 6 ++
10 files changed, 252 insertions(+), 6 deletions(-)
diff --git a/ai-agent-skills/README.md b/ai-agent-skills/README.md
index 33022872d..4ef96b8b0 100644
--- a/ai-agent-skills/README.md
+++ b/ai-agent-skills/README.md
@@ -24,6 +24,11 @@ The `ai-agent-skills` plugin is a repository of standardized
knowledge, best pra
By providing these "skills" in a structured format, we ensure that agents
follow the correct design patterns (e.g., favoring View Entities over manual
iteration, using Worker classes correctly, adhering to security standards)
without manual intervention for every step.
+## Version Compatibility
+These skills work best with Apache OFBiz `trunk`, where the guidance is
maintained and updated first.
+
+If you use them against older release branches or customized codebases, verify
helper availability, service contracts, and framework behavior before applying
the guidance verbatim.
+
## Agent Setup & Activation
For an AI agent (like Antigravity, Cursor, or GitHub Copilot) to effectively
use these skills, they need to be linked or aggregated into the specific
directories those agents expect at the root of your workspace.
diff --git a/ai-agent-skills/SKILLS_SUMMARY.md
b/ai-agent-skills/SKILLS_SUMMARY.md
index 89a4afc42..1057ac4ac 100644
--- a/ai-agent-skills/SKILLS_SUMMARY.md
+++ b/ai-agent-skills/SKILLS_SUMMARY.md
@@ -60,6 +60,7 @@ This document provides a high-level summary of the
specialized skills developed
| Skill | Description |
| :--- | :--- |
| **manage-api-integration** | Expose services via REST or SOAP and handle
JSON/XML data mapping. |
+| **manage-rest-api** | Design, refactor, and verify OFBiz REST APIs with
native services and contract-safe wrappers. |
| **manage-email-services** | Configure SMTP, manage email templates, and
automate outgoing communications. |
## 🔐 Advanced Management
diff --git a/ai-agent-skills/coding-standards/SKILL.md
b/ai-agent-skills/coding-standards/SKILL.md
index 649343f58..2099449d6 100644
--- a/ai-agent-skills/coding-standards/SKILL.md
+++ b/ai-agent-skills/coding-standards/SKILL.md
@@ -54,6 +54,8 @@ Ensure all OFBiz contributions meet production-grade
standards for clarity, main
- **Logging**: Use `Debug.logInfo(...)`, `Debug.logError(...)` with a
`MODULE` tag.
- Use `.filterByDate()` for all date-effective entities (e.g.,
`ProductPrice`, `StatusItem`).
- Prefer `EntityQuery` over `delegator.findOne(...)`.
+ - Prefer shared OFBiz/domain helpers such as `PartyHelper`,
`UtilDateTime`, `ObjectType`, and existing component helpers over local
formatting or conversion helpers.
+ - If service XML already types a parameter as `Timestamp`, `BigDecimal`,
`Double`, `Long`, or `Integer`, use the value directly in Groovy/Java service
logic instead of re-parsing or re-casting it at the service boundary.
5. **Internationalization (i18n)**:
- Never hardcode user-facing strings. Use `uiLabelMap.LabelKey`.
diff --git a/ai-agent-skills/manage-api-integration/SKILL.md
b/ai-agent-skills/manage-api-integration/SKILL.md
index 86a164da0..2e0a65b27 100644
--- a/ai-agent-skills/manage-api-integration/SKILL.md
+++ b/ai-agent-skills/manage-api-integration/SKILL.md
@@ -26,13 +26,15 @@ description: Define and integrate REST and SOAP APIs in
OFBiz.
Integration patterns for exposing OFBiz services via REST and SOAP, and
handling JSON responses.
-## REST API (rest-api)
+## REST API (framework REST API support)
-OFBiz uses the `rest-api` (based on Jersey) to declaratively expose services.
+OFBiz uses framework REST API support to declaratively expose services.
+
+For REST API design/refactoring, wrapper decisions, contract safety, and
PWA/API consumer compatibility, also read the `manage-rest-api` skill. This
skill remains the broader integration guide for REST mechanics, SOAP
integration, and controller JSON responses.
### REST API Definitions
-REST endpoints are defined in `*.rest.xml` files located in the `api/`
directory of an OFBiz component. These files are automatically discovered and
registered by the `rest-api` plugin; no explicit registration in
`ofbiz-component.xml` is required.
+REST endpoints are defined in `*.rest.xml` files located in the `api/`
directory of an OFBiz component. These files are discovered and registered by
the framework REST API support; no explicit registration in
`ofbiz-component.xml` is required.
**Pattern for `*.rest.xml`:**
```xml
@@ -50,7 +52,7 @@ REST endpoints are defined in `*.rest.xml` files located in
the `api/` directory
### Parameter Handling
-Parameter mapping from REST requests to OFBiz services is **implicit**. The
`rest-api` plugin automatically extracts parameters from the request and maps
them to service IN parameters whose names match.
+Parameter mapping from REST requests to OFBiz services is **implicit**. The
framework REST request handler extracts parameters from the request and maps
them to service IN parameters whose names match.
* **Path Parameters:** Extracted from the URI based on placeholders in the
`path` attributes (e.g., `{exampleId}`).
* **Query Parameters:** Extracted from the request URL.
@@ -58,6 +60,13 @@ Parameter mapping from REST requests to OFBiz services is
**implicit**. The `res
All extracted parameters are aggregated into a single context. The
`ServiceRequestHandler` then uses
`dispatcher.getDispatchContext().makeValidContext()` to select only those
parameters that are defined as IN parameters for the target service.
+When implementing endpoint logic, prefer framework helpers that already exist
in the target codebase or are being introduced in the same change. If a helper
is not available yet, prefer service typing, `EntityQuery`, and small local
logic.
+
+### Multi-Column Keys
+When a REST operation targets an OFBiz entity keyed by multiple columns,
prefer sending the native key fields in the request body for create, update,
and remove operations. This often allows the REST endpoint or client to call
native OFBiz services directly instead of adding wrapper services only to
decode a composite path ID.
+
+Use a stable composite REST ID only when the API shape truly needs a single
identifier segment, such as a list/detail link or a client-side row key. Keep
composite ID encoding at the API edge and do not distort the underlying OFBiz
entity or service model.
+
### Security and Response Handling
* **Authentication:** Controlled by the `auth="true|false"` attribute on the
`<operation>` element.
* **Responses:** Services return their OUT parameters (excluding internal
ones) as a JSON object. The response is automatically prefixed with
`&&&START&&&` for XSSI protection.
@@ -72,7 +81,7 @@ OFBiz allows services to be exposed as REST APIs directly via
attributes in the
### Pattern:
- **Attributes**: Set `export="true"` and `action="VERB"` (e.g.,
`action="GET"`).
-- **Result**: The service becomes reachable as a REST endpoint (e.g.,
`/rest/public/findProductById` or similar depending on the rest-api plugin
configuration).
+- **Result**: The service becomes reachable as a REST endpoint (e.g.,
`/rest/public/findProductById` or similar depending on framework REST
configuration).
```xml
<service name="findProductById" engine="java" auth="true" export="true"
action="GET" ...>
@@ -83,6 +92,7 @@ OFBiz allows services to be exposed as REST APIs directly via
attributes in the
### Key Considerations:
- **Simplicity**: No need for separate `rest.xml` or controller mappings.
- **Contract-first**: The service signature defines the API request/response.
+- **Native OFBiz Types**: Prefer `Timestamp`, `BigDecimal`, `Double`, `Long`,
and similar native types in service attributes so the Service Engine performs
coercion before service logic runs.
- **Auth**: Inherits service engine authentication rules.
## JSON Responses
@@ -122,6 +132,6 @@ Use the `soap-engine` or `SOAPClientEngine`.
## Security & Data Mapping
-- **Authentication**: REST API typically uses JWT or API Keys (configured in
`rest-api` common filters).
+- **Authentication**: REST API authentication depends on the framework REST
configuration and the invoked service security.
- **Permissions**: Every service invoked should have proper
`permission-service` or `check-permission` logic.
- **Data Mapping**: JSON/XML fields must precisely match service IN attributes
for automatic mapping. Use `ServiceUtil.getAndValidateParameters` pattern in
custom Java events if manual mapping is needed.
diff --git a/ai-agent-skills/manage-cache-and-performance/SKILL.md
b/ai-agent-skills/manage-cache-and-performance/SKILL.md
index 39044c6f3..038042bc2 100644
--- a/ai-agent-skills/manage-cache-and-performance/SKILL.md
+++ b/ai-agent-skills/manage-cache-and-performance/SKILL.md
@@ -107,6 +107,12 @@ When external systems or direct SQL modify the database,
or when you bypass OFBi
- Transaction-dependent data that must reflect real-time DB state. *(See
Anti-Pattern 6: Treating Cache as Primary Storage)*
- Data used for financial posting or accounting correctness.
+### 1b. Query Efficiency Before Caching
+- Before adding cache, first remove avoidable query overhead:
+- Use `.queryCount()` for existence checks instead of
`queryList()`/`queryOne()`.
+- Push date-effective filtering into the database with `.filterByDate()` or
`.filterByDate("fromField", "thruField")` instead of filtering fetched rows in
memory.
+- Use `.select(...)` to avoid loading wide rows when only a few fields are
needed.
+
### 2. Transaction and Async Stale Data Risks
- **Uncommitted Data**: The Entity Engine cache may return uncommitted data
within an active transaction. Relying on cache inside a transaction can produce
confusing behavior and phantom reads.
- **Async Services**: Async services run concurrently in new transactions. If
an async service depends on cached data from the main transaction before it
commits, it will see stale data.
diff --git a/ai-agent-skills/manage-groovy/SKILL.md
b/ai-agent-skills/manage-groovy/SKILL.md
index 3d580813f..82405edd7 100644
--- a/ai-agent-skills/manage-groovy/SKILL.md
+++ b/ai-agent-skills/manage-groovy/SKILL.md
@@ -75,6 +75,7 @@ Implement business logic, complex data transformations, and
data preparation scr
GenericValue product = from("Product").where("productId",
productId).queryOne()
```
- **Domain Helpers First**: Before writing direct entity queries, look for
existing OFBiz or component helper methods that encode business semantics.
Direct or batched entity queries are still appropriate for enrichment,
projections, or avoiding N+1 lookups once the helper behavior is understood.
+- **Typed Service Boundaries**: If the service definition already types an IN
parameter as `Timestamp`, `BigDecimal`, `Double`, `Long`, or `Integer`, consume
`parameters.foo` directly. Do not add redundant
`ObjectType.simpleTypeOrObjectConvert(...)`, `as Timestamp`, or similar casts
at the service boundary unless the value can also come from a mixed
legacy/string source.
- **Advanced Querying**: Use `EntityCondition` for complex filters:
```groovy
import org.apache.ofbiz.entity.condition.EntityCondition
@@ -116,12 +117,14 @@ String newId = result.newProductId
- **Hardcoding Strings**: Hardcoding error messages instead of UI Labels, or
hardcoding IDs (like `partyId`).
- **Legacy Entity APIs**: **DO NOT** use legacy Delegator APIs like
`delegator.findList(...)`. Always use the modern Entity DSL
`from("Entity").where(...).queryList()`.
- **Custom Conversion Helpers**: Avoid local helper methods for conversions
that OFBiz already provides. Prefer existing utilities such as
`ObjectType.simpleTypeOrObjectConvert(...)` at service/input boundaries and
`UtilMisc.toIntegerObject(...)` for integer conversion.
+- **Redundant Boundary Conversion**: Do not manually re-parse values that the
Service Engine has already coerced from `services.xml`. Reserve
`ObjectType.simpleTypeOrObjectConvert(...)` for legacy string-typed services,
mixed-source fallback logic, or decoding composite REST IDs.
### Database Querying:
- **Unconstrained Queries**: **NEVER** query a table without a `.where(...)`
condition unless explicitly fetching a tiny bounded list (like enumerated
`StatusItem`).
- **N+1 Queries**: **AVOID** querying the database inside a loop. Instead,
gather all IDs, perform a single `IN` query using `EntityOperator.IN`, and map
the results in memory.
- **Over-fetching**: **ALWAYS** use `.select("field1", "field2")` on
`EntityQuery` when you only need a few fields from a wide or heavy table. This
saves memory and JDBC overhead.
- **Fetching Full Records for Existence Checks**: **DO NOT** use `queryList()`
or `queryOne()` if you only need to know if a record exists. Use
`.queryCount()` instead.
+- **Date-Effective Existence Checks**: For date-effective entities, push the
date filtering into the query itself with `.filterByDate()` or
`.filterByDate("fromField", "thruField")` before calling `.queryCount()`. Do
not fetch rows and then filter them in memory just to answer "does one exist?".
- **Large Result Sets**: **NEVER** use `queryList()` for queries that could
return thousands of rows (e.g., all orders). Use `queryIterator()` to get an
`EntityListIterator` and **must** wrap it in a `try-finally` block to ensure
`.close()` is called.
- **Missing Cache Usage**: For highly-read, rarely-changed data (e.g.,
`StatusItem`, `Geo`), missing `.cache(true)` bypasses the entity cache and
impacts database performance unnecessarily.
@@ -172,6 +175,25 @@ def updateInternalName() {
}
```
+**Example 1b: Typed service boundary (The Right Way)**
+```groovy
+def createAssociation() {
+ Timestamp fromDate = parameters.fromDate ?: UtilDateTime.nowTimestamp()
+ BigDecimal quantity = parameters.quantity ?: BigDecimal.ONE
+ // services.xml already typed fromDate and quantity
+ ...
+}
+```
+
+**Example 1c: Date-effective existence check (The Right Way)**
+```groovy
+long supplierProductCount = from("SupplierProduct")
+ .where(productId: productId, currencyUomId: currencyUomId)
+ .filterByDate("availableFromDate", "availableThruDate")
+ .queryCount()
+boolean hasSupplierPrice = supplierProductCount > 0L
+```
+
**Example 2: Data Prep Screen Script (The Right Way)**
```groovy
// Simple top-to-bottom read-only script for a screen
diff --git a/ai-agent-skills/manage-rest-api/SKILL.md
b/ai-agent-skills/manage-rest-api/SKILL.md
new file mode 100644
index 000000000..be1ca1fc6
--- /dev/null
+++ b/ai-agent-skills/manage-rest-api/SKILL.md
@@ -0,0 +1,88 @@
+---
+name: manage-rest-api
+description: Design, expose, refactor, and verify OFBiz REST APIs using the
framework REST support, service engine contracts, native OFBiz services, and
contract-safe wrapper patterns. Use when working with `.rest.xml` files,
REST-exposed services, PWA/API consumers, service export settings, or backend
REST response contracts.
+---
+
+# Skill: Manage REST APIs
+
+## Goal
+Build OFBiz REST APIs that are thin, service-native, contract-safe, and easy
to upstream into OFBiz.
+
+Use OFBiz services, service definitions, entity utilities, and framework REST
conventions first. Add REST wrapper services only when they adapt a transport
contract, aggregate read data, or preserve compatibility for an existing
consumer.
+
+For low-level REST XML syntax, direct service export mechanics, SOAP
integration, or controller JSON response patterns, also read the
`manage-api-integration` skill.
+
+## Core Workflow
+1. **Map the contract first**
+ - Find the REST mapping, service definition, service implementation, and
active consumers.
+ - Search frontend/API clients before removing request parameters or
response fields.
+ - Identify whether the endpoint is a generic service API, a read aggregate,
or a compatibility adapter.
+
+2. **Prefer native OFBiz services**
+ - Reuse existing services before creating wrapper services.
+ - Expose native services directly when their inputs and outputs are already
suitable for the REST contract.
+ - Keep write wrappers thin: resolve REST convenience parameters, delegate
to native services, return the agreed response shape.
+
+3. **Let the service engine validate and coerce**
+ - Define service attributes with precise native types such as `Timestamp`,
`BigDecimal`, `Double`, `Long`, and `Integer`.
+ - Avoid manual parsing in Groovy/Java when service XML already declares the
correct type.
+ - Use OFBiz conversion utilities only when conversion is genuinely needed
inside service logic.
+
+4. **Use native query and paging utilities**
+ - Prefer `EntityQuery`, `filterByDate`, `queryCount`, and helper methods
that already exist in the target codebase over custom query loops.
+ - Avoid N+1 queries by batch-loading related data and mapping in memory.
+ - Use database-level filtering/counting where possible instead of loading
full rows and filtering manually.
+
+5. **Promote shared mechanics carefully**
+ - Reuse existing OFBiz/framework/component utilities before adding local
helpers.
+ - Promote repeated mechanical patterns to the narrowest appropriate shared
utility.
+ - Do not promote domain-specific behavior into generic utilities just
because the method is small.
+
+6. **Verify with project-standard checks**
+ - Run the repository's standard backend compilation, static analysis, and
relevant tests.
+ - Use the runtime/JDK version required by the project build configuration.
+ - If a full check is blocked by environment or unrelated project issues,
run the narrowest equivalent verification and report that it was targeted.
+
+## Helper Availability
+- Before using a helper for paging, partial-list slicing, case-insensitive
parameter lookup, composite REST ID handling, or REST error shaping, confirm
that the helper already exists in the target codebase or is being introduced in
the same change.
+- If a helper is not available yet, prefer native `EntityQuery`, service
engine typing/validation, and small local endpoint logic over adding a
dependency on a missing framework API.
+
+## Wrapper Decision Rules
+Create or keep a wrapper when it:
+- Aggregates multiple native service/entity reads for a REST/PWA screen.
+- Preserves a published REST contract that differs from native OFBiz service
shape.
+- Resolves REST-friendly identifiers into native key fields when the client
cannot send native keys directly.
+- Adds read-side display metadata that avoids excessive client round trips.
+
+Remove or avoid a wrapper when it:
+- Only duplicates a native CRUD service call.
+- Manually validates what the service definition or native service already
validates.
+- Re-parses typed service parameters.
+- Adds local formatting/conversion helpers already available in OFBiz.
+
+Read [service-wrapper-patterns.md](references/service-wrapper-patterns.md)
before adding or removing wrapper services.
+
+## Contract Safety
+Before changing a REST response:
+- Search active consumers for every field you plan to remove.
+- Update frontend/client mappers and tests in the same change when
intentionally changing a contract.
+- Keep compatibility fields only when still used or intentionally supported.
+- Prefer stable semantic field names over implementation-detail names.
+
+Read [rest-contract-checklist.md](references/rest-contract-checklist.md)
before changing existing API inputs, paths, response fields, or service export
settings.
+
+## REST Mapping Guidance
+- Keep REST XML mappings declarative and close to service names.
+- Prefer path parameters for stable resource identity and query parameters for
filtering/paging.
+- Use request bodies for create/update/remove payloads, especially when native
OFBiz services require multiple key fields.
+- For multi-column keys, prefer native key fields in the body when that
enables direct native service use. Use a stable composite REST identifier only
when the REST contract truly needs one path/id value. Do not distort the
underlying entity model.
+- Ensure services exposed through REST are intentionally exported and
authorized.
+
+## Utility Promotion Test
+Before adding a helper, answer:
+- Is this already available in OFBiz?
+- Is this repeated mechanical logic, or domain behavior?
+- Is the helper useful outside this one endpoint?
+- What is the narrowest appropriate owner: existing framework utility,
component utility, or local script?
+
+Good utility candidates are mechanical patterns such as batch lookup,
case-insensitive ID search, pagination wrappers, or composite ID creation, but
only when those helpers already exist in the target codebase or are being
introduced in the same change. Poor candidates are domain queries such as
"routing task associations" or "BOM component rows"; keep those near the
service that owns the domain meaning.
diff --git
a/ai-agent-skills/manage-rest-api/references/rest-contract-checklist.md
b/ai-agent-skills/manage-rest-api/references/rest-contract-checklist.md
new file mode 100644
index 000000000..c72253cb6
--- /dev/null
+++ b/ai-agent-skills/manage-rest-api/references/rest-contract-checklist.md
@@ -0,0 +1,39 @@
+# REST Contract Checklist
+
+Use this checklist before changing existing OFBiz REST endpoints or service
exports.
+
+## Discover
+- Find the `.rest.xml` operation or direct service export.
+- Find the service definition and implementation.
+- Find active consumers, including PWAs, tests, scripts, and API clients.
+- Confirm whether the endpoint is public, authenticated, or internal.
+
+## Inputs
+- Preserve path, query, and body parameter names unless consumers are updated
together.
+- Prefer precise service attribute types so the service engine handles
validation and coercion.
+- Avoid manual parsing of already-typed service parameters.
+- Keep required/optional flags aligned with real REST usage.
+- Confirm service `export` settings are intentional for REST access.
+
+## Outputs
+- Search consumers before removing any response field.
+- Keep fields that drive visible UI, filters, badges, links, or client-side
update/delete calls.
+- Remove unused metadata when contract consumers are updated and tests cover
the change.
+- Avoid returning native internal fields that are not part of the REST
contract.
+- Do not stringify timestamps, quantities, or money unless the endpoint is
explicitly presentation/export oriented.
+
+## Paths and Keys
+- Prefer stable resource paths for common single-key resources.
+- For multi-column native keys, prefer request-body fields for
create/update/remove operations when that enables native OFBiz service calls.
+- Use composite REST IDs only when the contract truly needs one path/id value,
and keep encoding/decoding at the API edge.
+- Do not modify OFBiz entity keys just to make REST paths shorter.
+
+## Compatibility
+- If a frontend/PWA uses a legacy field name, either preserve it or update the
frontend/client mapper in the same change.
+- When simplifying payloads, update type definitions, mappers, and contract
tests together.
+- Document intentional contract changes in the commit/PR summary.
+
+## Verification
+- Run project-standard backend compile/static analysis/tests.
+- Run consumer contract tests when clients are in scope.
+- If only targeted verification was possible, state exactly what was and was
not verified.
diff --git
a/ai-agent-skills/manage-rest-api/references/service-wrapper-patterns.md
b/ai-agent-skills/manage-rest-api/references/service-wrapper-patterns.md
new file mode 100644
index 000000000..b969e402b
--- /dev/null
+++ b/ai-agent-skills/manage-rest-api/references/service-wrapper-patterns.md
@@ -0,0 +1,67 @@
+# Service Wrapper Patterns
+
+Use these patterns when deciding whether a REST-facing service should exist.
+
+## Prefer Direct Native Service Exposure
+Use an existing OFBiz service directly when:
+- The service already performs the business operation.
+- The service attributes match the REST inputs closely.
+- The output is acceptable to the consumer.
+- The service has correct validation, auth, and export settings.
+
+Do not create a wrapper only to copy fields from REST input to the same native
service input.
+
+## Thin Write Adapter
+Use a thin wrapper when REST uses convenience parameters that differ from
native keys and the client cannot reasonably send native key fields directly.
+
+The wrapper should:
+- Validate only what the service definition cannot express.
+- Resolve convenience identifiers into native service fields.
+- Call the native OFBiz service.
+- Check `ServiceUtil.isError(result)` and return the native error when
appropriate.
+- Return only the agreed REST response fields.
+
+Avoid adding duplicate business validation already enforced by the native
service.
+
+## Read Aggregate Wrapper
+Use a read wrapper when one API call intentionally serves a screen or workflow
by combining data from several entities/services.
+
+The wrapper should:
+- Batch-load related records instead of querying per row.
+- Use `EntityQuery`, `filterByDate`, `queryCount`, and paging helpers.
+- Keep response fields screen/use-case oriented, not raw database dumps.
+- Avoid heavy summaries that are not displayed or otherwise consumed.
+
+## Lookup/Search Endpoint
+Use a lookup endpoint when multiple clients need the same typeahead or option
list.
+
+The service should:
+- Accept generic filters where practical.
+- Use case-insensitive search helpers or OFBiz-native search utilities only
when they already exist in the target codebase.
+- Page or cap result sizes.
+- Return stable IDs plus display names/labels.
+- Avoid embedding one component's business assumptions into a generic lookup.
+
+## Utility Promotion
+Promote helper code only when it is repeated mechanical logic.
+
+Good candidates:
+- Load entities by ID list and return a map keyed by ID.
+- Search an entity across configured fields and return IDs.
+- Count/group rows by a selected field.
+- Build stable composite identifiers from key parts.
+
+When a candidate depends on helper methods that are not yet available in the
target codebase, either introduce those helpers in the same change or keep the
endpoint logic self-contained.
+
+Poor candidates:
+- Domain relationship queries.
+- Screen-specific payload assembly.
+- Business validation rules.
+- Helpers used by only one endpoint with no clear reuse.
+
+## Deletion Check
+Before deleting a wrapper:
+- Confirm the native service is exported and authorized correctly.
+- Confirm REST clients can send the native key fields.
+- Confirm response handling still works, or update client mappers/tests.
+- Keep a wrapper if it protects clients from unstable internal service details.
diff --git a/ai-agent-skills/manage-services/SKILL.md
b/ai-agent-skills/manage-services/SKILL.md
index baaf97037..6ee192877 100644
--- a/ai-agent-skills/manage-services/SKILL.md
+++ b/ai-agent-skills/manage-services/SKILL.md
@@ -50,6 +50,7 @@ Define and implement business logic as reusable,
transactional, and securely enf
- **Naming**: Use `verbNoun` format for all service names (e.g.,
`updateExample`, `createOrder`).
- **In/Out Parameters**: Use `<attribute name="..." mode="IN|OUT|INOUT"
type="..." optional="true|false"/>`. NEVER bypass attribute validation by
casually omitting them or making them wrongly optional.
- **Native Contract Types**: Backend/data services should keep OFBiz-native
service types such as `Timestamp` and `BigDecimal` in IN/OUT attributes and
returned maps.
+- **Type In XML First**: If a service consumes dates, numbers, or booleans,
declare them with native OFBiz/Java types in `services.xml` first. Prefer
fixing the service contract over keeping Groovy-side parsing code forever.
- **Auth & Security**: Set `auth="true"` for protected services. ALWAYS
implement permission checks (e.g., `<permission-service>`).
- **Export & REST**: Set `export="true"` to expose the service to external
callers. Set `action="GET|POST|PUT|DELETE"` to automatically export the service
as a REST API endpoint.
- **Service Overrides**: Use `<implements service="..." />` to inherit
attributes from an existing service.
@@ -91,6 +92,11 @@ Define and implement business logic as reusable,
transactional, and securely enf
### 4. Boilerplate CRUD Avoidance
- Do NOT use a Java or Groovy service simply to write `delegator.create()`.
Rely on the `entity-auto` engine.
+### 5. API Wrapper Services
+- Thin API wrappers may adapt naming, shape, or multi-call orchestration for a
PWA or REST endpoint, but they should still preserve OFBiz-native types in the
service contract.
+- Do not stringify timestamps, quantities, or money in wrapper services unless
the service is explicitly a presentation/export formatter.
+- For multi-column primary keys exposed through a single REST path segment,
use a stable composite ID mapping in the API wrapper rather than flattening the
underlying entity model.
+
## Anti-Patterns
### Anti-Pattern 1: Swallowing Errors in runSync