From f9c211966428b2fb84ec22cd8fd0a01755f00642 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina Date: Wed, 13 May 2026 10:51:16 +0200 Subject: [PATCH 01/18] feat(skills): add ABAP-to-BTP-CAP modernization skill chain (MVP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a side-by-side modernization workflow that takes a custom ABAP package (Z*) on S/4HANA / ECC and produces a complete BTP-native CAP project scaffold (CDS schema + service + handler stubs + Fiori Elements V4 + MTA deployment artifacts) without touching the source ABAP system. This is the MVP foundation. Orchestrator + 2 core sub-skills (schema + service) included. Remaining 4 sub-skills (clean-core-gap, fiori-elements, auth-mapping, btp-mta) tracked as follow-up. ## What's included ### New skills (3 markdown files, 1211 lines) - skills/modernize-abap-to-btp-cap.md (orchestrator, 357 lines) - End-to-end workflow: inventory → schema → service → fiori → auth → mta → ADR - Smart Defaults table, BTP vs On-Premise comparison, error handling - References sub-skills via inline markdown links - skills/modernize-abap-cap-schema.md (370 lines) - DDIC → CDS type mapping table (24 types) - Foreign-key → Association/Composition inference - cuid / managed aspect auto-application (sysuuid_x16 + audit fields) - Currency / Quantity semantic annotations - mandt / CLNT exclusion (single-tenant per service) - skills/modernize-abap-cap-service.md (484 lines) - FM/program/class → CAP service action mapping - Bound vs unbound action classification - TypeScript handler stubs with TODO + ABAP source excerpts - 13 common ABAP → CAP handler pattern mappings (SELECT, MODIFY, LOOP AT, READ TABLE, CALL FUNCTION, etc.) - Type extraction to srv/types.cds for complex inline types ### README update - New "BTP CAP Modernization (Preview)" section in skills/README.md - Explains relationship vs sap-clean-core-atc, migrate-custom-code (those keep source in SAP; this rebuilds as BTP CAP) - Typical workflow chain documented (7 steps) ## Design decisions - **Side-by-side, NOT in-place**: leaves source ABAP system read-only. Generates parallel CAP target — no SAPWrite calls needed. - **Skill-first contribution**: aligned with arc-1's 17-skill community pattern; NO new MCP tool, NO core code change. Honors the 12-tool token-efficient principle (CLAUDE.md §"Design Principles"). - **Markdown orchestration**: skills chain via inline markdown links (consistent with sap-clean-core-atc → migrate-custom-code pattern). - **Sandbox output by default**: writes to /.target-cap-staging/ for review before apply. Reversible. - **Smart Defaults**: every decision documented with rationale; avoid asking the user unless ambiguous. - **TODO scaffolds, NOT auto-implementation**: handler bodies are stubs with embedded ABAP source excerpts; user implements business logic in a second pass. Honest about what the LLM can/cannot translate. ## Use cases addressed - Greenfield BTP CAP migration of a Z* package from ECC / on-prem S/4 - Lift-and-redesign for BTP-native architecture - POC / pilot for evaluating BTP CAP as target - Quarterly modernization assessment (run with --skip flags for analysis-only) - Pre-deployment audit before committing to BTP roadmap ## NOT addressed (intentionally out of scope) - No SAP write operations - No CF deployment automation (cf push stays manual) - No HANA service provisioning - No XSUAA service creation - No automatic handler logic generation - No regression test execution - No transport-coordinated migration - No Kyma target (CF only for v1) - No CAP Java target (Node.js only for v1) - No Freestyle UI5 (Fiori Elements V4 only) ## Follow-up Track sub-skill development: - modernize-abap-clean-core-gap (~400 lines) Batch Clean Core L-A per-edition compliance check + replacement suggestions - modernize-abap-fiori-elements (~500 lines) Service → Fiori Elements V4 LROP scaffold (manifest + annotations) - modernize-abap-auth-mapping (~300 lines) AUTHORITY-CHECK → XSUAA scopes + @restrict annotations - modernize-abap-btp-mta (~300 lines) CAP project → mta.yaml + Dockerfile + manifest.yml ## Testing This is a skill (markdown), no automated tests apply per arc-1 convention. Smoke-testable manually by: 1. Connecting ARC-1 to a system with Z packages 2. Invoking the skill via Claude Code / Cursor / Copilot 3. Verifying scaffold output compiles (npx cds compile) ## Acknowledgments Inspired by: - generate-rap-service-researched (research-first pattern) - sap-clean-core-atc (Clean Core classification) - migrate-custom-code (ATC-driven fix pattern) --- skills/README.md | 36 ++ skills/modernize-abap-cap-schema.md | 370 ++++++++++++++++++++ skills/modernize-abap-cap-service.md | 484 +++++++++++++++++++++++++++ skills/modernize-abap-to-btp-cap.md | 357 ++++++++++++++++++++ 4 files changed, 1247 insertions(+) create mode 100644 skills/modernize-abap-cap-schema.md create mode 100644 skills/modernize-abap-cap-service.md create mode 100644 skills/modernize-abap-to-btp-cap.md diff --git a/skills/README.md b/skills/README.md index a3b41749..a49df35c 100644 --- a/skills/README.md +++ b/skills/README.md @@ -102,6 +102,42 @@ Both skills produce the same RAP artifact stack. The difference is how they get | [sap-clean-core-atc](sap-clean-core-atc.md) | Audits a package of custom code and buckets every Z/Y object into Clean Core Levels A–D using mcp-sap-docs + ATC | Planning an ECC→S/4HANA Cloud or BTP move; quarterly custom-code health check | | [sap-unused-code](sap-unused-code.md) | Finds Z/Y objects never called at runtime using SCMON or SUSG, then cross-references static where-used | Scoping a custom-code retirement project; pre-migration dead-code cleanup (requires `SAP_ALLOW_FREE_SQL=true` + `S_TABU_NAM` on `SCMON_*`/`SUSG_*`) | +### BTP CAP Modernization (Preview) + +End-to-end greenfield migration from classic ABAP custom code (Z* packages) to BTP-native CAP applications. Side-by-side approach — leaves the source ABAP system untouched and produces a complete target CAP project (CDS schema, services, Fiori Elements V4 app, CF deployment artifacts) for review and manual activation. + +| Skill | What it does | When to use | +|---|---|---| +| [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) | End-to-end migration orchestrator: Z package → BTP CAP scaffold (CDS + service + Fiori + xs-security + mta.yaml + ADRs) | Planning ECC / on-prem S/4 → BTP CAP greenfield migration | +| [modernize-abap-cap-schema](modernize-abap-cap-schema.md) | Z-tables (SE11) → CAP CDS entities with DDIC→CDS type mapping, FK→association inference, `cuid`/`managed` aspect auto-application | Data-model migration step; standalone for reverse-engineering Z tables to CDS | +| [modernize-abap-cap-service](modernize-abap-cap-service.md) | Z function modules / reports / classes → CAP service definitions + TypeScript handler stubs with TODO markers + ABAP source excerpts | Service-layer migration step; produces compile-clean scaffold ready for business-logic translation | + +#### Modernization vs Cloud-Readiness skills + +- [sap-clean-core-atc](sap-clean-core-atc.md) classifies whether code **can stay in ABAP and move to S/4HANA Cloud / ABAP Cloud**. Source system stays SAP. +- [migrate-custom-code](migrate-custom-code.md) **fixes ATC findings in place** to make ABAP cloud-ready. Source system stays SAP. +- [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) **rebuilds the application as BTP CAP** — leaves source ABAP untouched, produces a parallel BTP-native stack. Use when the target architecture is a CAP application, not just cloud-ready ABAP. + +#### Typical BTP modernization workflow + +``` +1. bootstrap-system-context → Capture source SID, release, features +2. sap-unused-code → Scope: skip dead code from migration +3. sap-clean-core-atc → Risk assessment per Z object +4. modernize-abap-to-btp-cap → Generate target CAP scaffold (orchestrator) + ├── modernize-abap-clean-core-gap → (sub-skill, planned) per-edition availability + ├── modernize-abap-cap-schema → db/schema.cds + ├── modernize-abap-cap-service → srv/*.cds + handlers stubs + ├── modernize-abap-fiori-elements → (sub-skill, planned) app// + ├── modernize-abap-auth-mapping → (sub-skill, planned) xs-security.json + └── modernize-abap-btp-mta → (sub-skill, planned) mta.yaml + Dockerfile +5. Manual: implement handler TODOs → Translate ABAP business logic +6. generate-cds-unit-test → Test the new CAP entities +7. mbt build + cf deploy → Ship to BTP CF +``` + +> **Status**: Preview / Work-in-Progress. Orchestrator + schema + service skills available now. Remaining sub-skills (`clean-core-gap`, `fiori-elements`, `auth-mapping`, `btp-mta`) tracked in [#TBD upstream issue]. Feedback welcome on the [PR thread]. + ### System Context & Local Workflow | Skill | What it does | When to use | diff --git a/skills/modernize-abap-cap-schema.md b/skills/modernize-abap-cap-schema.md new file mode 100644 index 00000000..8716dd8d --- /dev/null +++ b/skills/modernize-abap-cap-schema.md @@ -0,0 +1,370 @@ +# Modernize ABAP CAP Schema + +Generate a CAP CDS data model (`db/schema.cds`) from a Z* package's database tables (`TABL`) and structures. Maps DDIC types to CDS types, infers associations from foreign-key references, applies `cuid` / `managed` aspects automatically, and emits a single namespace-scoped schema ready for `cds compile`. + +Sub-skill of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) but also usable standalone when only data-model migration is needed (e.g., reverse-engineer a Z table inventory into CAP CDS for a fresh project). + +This is greenfield CDS *for CAP runtime* (`@sap/cds`) — NOT ABAP CDS DDL. The two share syntax fragments but have different semantics, type systems, and tooling. + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Output file | `/db/schema.cds` | CAP convention | +| Namespace | derived from package: `com.example.` | Customer-namespace by default; user can override | +| Aspect: cuid | Auto-applied when primary key is `sysuuid_x16` (RAW(16)) | RAP convention; cleanest mapping to CAP `cuid` | +| Aspect: managed | Auto-applied when table has any of `crusr` / `crdat` / `cruzt` / `chusr` / `chdat` / `chuzt` fields | Standard ABAP timestamp / user audit columns | +| Aspect: temporal | NOT auto-applied | Temporal modeling rarely 1:1 from ABAP; flag for user review | +| Default key | First field with key flag set in DDIC | DDIC primary-key inheritance | +| Type mapping | DDIC → CDS table (see Step 3) | Lossless where possible; documented where ambiguous | +| Associations | Inferred from foreign-key references (`KEY` table relations) | Composition vs Association heuristic in Step 4 | +| Currency / Quantity reference | `@Semantics.amount.currencyCode` / `@Semantics.quantity.unitOfMeasure` | Required by CAP runtime for proper formatting | +| Comments | Preserved from DDIC table short text + field labels | `@Common.Label: ''` annotation | +| Output format | Pretty-printed, 2-space indent | CAP standard | + +## Input + +The user provides: + +- **Z package name** (required) — e.g., `ZSALES_PKG` +- **Target directory** (required) — where `db/schema.cds` will be written + +Optionally: + +- **Namespace override** — e.g., `com.acme.sales` (overrides default) +- **Object scope** — comma list of TABL names to include (default: all in package) +- **Skip aspects** — disable `cuid`/`managed` auto-application +- **No-associations** — emit flat entities without inferred Associations + +## Step 1: Enumerate Database Tables + +### 1a. List TABL objects in package + +``` +SAPRead(type="DEVC", name="") +``` + +Filter results to keep only `objectType=TABL` entries. Sort by name. + +For very large packages, partition by name pattern if user provides one (e.g., `ZSALES_TBL*` for header tables only). + +### 1b. Skip non-transparent table types + +Read the DDIC `tabClass` for each candidate; keep only: + +- `TRANSP` — Transparent table (most common, maps directly to CDS entity) +- `POOL` / `CLUSTER` — Skip with warning (pooled/cluster tables don't map cleanly to CDS — recommend redesign) +- `VIEW` — Skip (treat as DDLS via `modernize-abap-clean-core-gap`) +- `STRU` / `INTTAB` — Include but emit as `type` (not `entity`) + +### 1c. Read each table + +For each TABL retained: + +``` +SAPRead(type="TABL", name="") +``` + +The response includes field list with: `name`, `dataElement`, `domain`, `dataType`, `length`, `decimals`, `key`, `notNull`, `checkTable` (foreign-key target), `searchHelp`, `shortText`. + +## Step 2: Read Domain + Data Element Details (where needed) + +For fields where the DDIC type is custom (`Z*_DTE` / `Z*_DOM`): + +``` +SAPRead(type="DTEL", name="") +SAPRead(type="DOMA", name="") +``` + +Extract: + +- `dataType` (numeric, character, decimal) +- `length` and `decimals` +- `fixedValues` (for domains with value list — useful for CDS `enum` generation) +- `conversionExit` (e.g., `MATN1`, `ALPHA` — flag in comment, NOT auto-applied because CAP doesn't have built-in conversion exits) + +Cache results — same domain often referenced from many tables. + +## Step 3: DDIC → CDS Type Mapping + +Apply the standard DDIC → CDS type table. **Implement these as canonical mapping; do NOT improvise**: + +| DDIC type | DDIC length | CDS type | Notes | +|---|---|---|---| +| `CHAR` | 1..N | `String(N)` | If N = 1 and domain is boolean-like (`XFELD`, `BOOLE_D`), map to `Boolean` | +| `NUMC` | 1..N | `String(N)` | NOT `Integer` — preserve leading zeros via String | +| `DEC` | N,M | `Decimal(N, M)` | Both precision + scale preserved | +| `INT1` | 1 | `Integer` (8-bit) | CDS doesn't have 8-bit; use `Integer` and `@assert.range: [0, 255]` | +| `INT2` | 2 | `Integer` (16-bit) | `Integer` + `@assert.range: [-32768, 32767]` | +| `INT4` | 4 | `Integer` | Direct map | +| `INT8` | 8 | `Integer64` | CAP-native | +| `FLTP` | 8 | `Double` | IEEE-754 | +| `DATS` | 8 | `Date` | YYYYMMDD source; CAP normalizes | +| `TIMS` | 6 | `Time` | HHMMSS source | +| `TZNTSTMPS` | 14 | `Timestamp` | UTC timestamp | +| `TZNTSTMPL` | 21 | `Timestamp` | Long timestamp with milliseconds | +| `UTCLONG` | 8 | `Timestamp` | New ABAP type; map directly | +| `CURR` | N,M | `Decimal(N, M) @Semantics.amount.amount` | Must have `currencyCode` reference field | +| `QUAN` | N,M | `Decimal(N, M) @Semantics.quantity.value` | Must have `unitOfMeasure` reference field | +| `LANG` | 1 | `String(2)` | Language key (ISO 639-1 in CAP) | +| `RAW` | N | `Binary(N)` | Direct map | +| `RAWSTRING` | unlimited | `LargeBinary` | Streaming blob | +| `LRAW` | N | `LargeBinary` | Same as `RAWSTRING` | +| `STRING` | unlimited | `LargeString` | Streaming text | +| `LCHR` | N | `LargeString` | Same as `STRING` | +| `CUKY` | 5 | `String(5)` + `@Semantics.amount.currencyCode` if referenced from a CURR field | Currency code | +| `UNIT` | 3 | `String(3)` + `@Semantics.quantity.unitOfMeasure` if referenced from a QUAN field | Unit of measure | +| `CLNT` | 3 | OMIT | CAP is single-tenant per service; `mandt` should NOT appear in target | +| `MANDT` | 3 | OMIT | Same as CLNT | +| `XSEQUENCE` | N | `Binary(N)` | Numbering sequence | +| `XFELD` | 1 | `Boolean` | Boolean flag (X = true, ' ' = false) | +| `BOOLE_D` | 1 | `Boolean` | Boolean data element | +| `SYSUUID_X16` | 16 | (key only) → triggers `cuid` aspect | Special: don't emit field, apply `cuid` | + +### Edge cases + +- **Multi-currency tables** — a table with two `CURR` fields needs two `CUKY` references; preserve both +- **Computed fields** (`KEYFLAG = 'X'` for header amount totals) — flag as virtual: `virtual amount: Decimal(15, 2);` +- **Append structures** — read separately via `SAPRead(type="TABL", name="")` and merge fields into target entity +- **Include structures** — same as append but with `INCLUDE` keyword in DDIC; merge fields inline + +## Step 4: Infer Associations + +For each field with `checkTable` set (foreign-key reference): + +### 4a. Determine cardinality + +- **1:N** — checkTable is parent → emit `Association to one ` on child with FK field name +- **N:1** — fields on the target table reference back → emit `Composition of many ` on parent + +Heuristic: + +- If field is part of primary key AND the source table has additional non-key fields → composition (child) +- If checkTable points to a customizing table (`T*` namespace, customizing tab class) → Association +- If checkTable is in same package → Composition unless explicitly modeled otherwise + +### 4b. Emit association definition + +```cds +entity Order : cuid, managed { + customer : Association to one Customer on customer.ID = customerId; + customerId : String(10); + items : Composition of many OrderItem on items.parent = $self; + totalAmount : Decimal(15, 2) @Semantics.amount.amount; + currency : String(5) @Semantics.amount.currencyCode; +} +``` + +### 4c. Flag ambiguous mappings + +If the FK relationship is unclear (e.g., shared key fields with no `checkTable` declared), emit a CDS comment: + +```cds +// REVIEW: foreign-key relationship implied but not declared in DDIC. +// Source table: ZSALES_ITEM target hint: ZSALES_HEADER +customerId : String(10); +``` + +## Step 5: Apply Aspects (cuid / managed / temporal) + +### 5a. `cuid` aspect + +If the table has a single primary key of type `SYSUUID_X16`: + +- Remove the primary-key field from the entity body +- Add `: cuid` after the entity name + +```cds +entity Customer : cuid { + name : String(80); + // ID is provided by cuid aspect +} +``` + +### 5b. `managed` aspect + +If the table has timestamp / user audit fields (`crusr`/`crdat`/`cruzt`/`chusr`/`chdat`/`chuzt`): + +- Remove those fields from entity body +- Add `, managed` after `cuid` (or directly if no `cuid`) + +```cds +entity Customer : cuid, managed { + name : String(80); + // createdAt, createdBy, modifiedAt, modifiedBy provided by managed aspect +} +``` + +### 5c. Skip temporal aspect + +CAP `temporal` aspect requires `validFrom` / `validTo` with specific semantics; do NOT auto-apply even if such fields exist. Flag for user review: + +```cds +entity ContractVersion { + // REVIEW: this table has validFrom/validTo — consider applying ': cuid, managed, temporal' if business semantics match. + ... +} +``` + +## Step 6: Generate db/schema.cds + +Emit a single file with structure: + +```cds +namespace ; + +using { sap.common.CodeList } from '@sap/cds/common'; +using { cuid, managed, temporal } from '@sap/cds/common'; + +@Common.Label: '' +entity : cuid, managed { + // fields + // associations +} + +@Common.Label: '' +entity : cuid { + // fields +} + +// REVIEW: +``` + +### Naming + +- DDIC table `ZSALES_ORDER_H` → CDS entity `Order` (drop `Z`, drop `_H` suffix, CamelCase) +- DDIC field `KUNNR` → CDS field `customer` (use ABAP semantic name if `dataElement` provides one) OR keep ABAP name lower-cased: `kunnr` +- Default heuristic: if `dataElement` matches a known SAP standard (`KUNNR` → customer, `MATNR` → material, `BUKRS` → companyCode), apply standard name; otherwise lower-case ABAP field name + +Track ABAP → CDS naming map in a comment at the top of the file: + +```cds +// Field naming map (ABAP source → CDS target): +// ZSALES_ORDER_H.KUNNR → Order.customer +// ZSALES_ORDER_H.MATNR → Order.material +// ZSALES_ORDER_H.WAERS → Order.currency +// ZSALES_ORDER_H.NETWR → Order.netAmount +``` + +## Step 7: Validate + +### 7a. CDS compile + +```bash +npx cds compile /db/schema.cds --to edmx > /dev/null +``` + +Must succeed. Common errors: + +| Error | Cause | Fix | +|---|---|---| +| `Cannot resolve association target` | FK reference points to a table not in scope | Add the parent table to package scope or remove the association | +| `Type 'X' is not supported` | DDIC type without mapping | Add missing entry in Step 3 table, regenerate | +| `Duplicate field name` | Append structure collision | Manual review; rename or merge fields | +| `Currency reference missing` | CURR field without CUKY companion | Add `@Semantics.amount.currencyCode` reference | +| `Namespace conflict` | Namespace mismatches between schema and using-imports | Verify `namespace` header matches CAP namespace conventions | + +### 7b. CDS-to-SQL generation + +```bash +npx cds compile /db/schema.cds --to sql > /dev/null +``` + +Validates that the schema can be deployed to an actual database (HANA + SQLite both). + +### 7c. Lint (optional) + +If the target project has CAP eslint configured: + +```bash +npx eslint /db/schema.cds +``` + +## Step 8: Emit Migration Notes + +Append at end of `db/schema.cds`: + +```cds +// ============================================================================ +// Migration notes (ABAP source: → CAP CDS schema) +// ============================================================================ +// +// - entities generated from source TABL objects +// -

associations inferred from FK references +// - tables with cuid aspect applied (SYSUUID_X16 primary key) +// - tables with managed aspect applied (audit timestamps) +// - tables flagged for REVIEW (manual mapping required) +// +// REVIEW items: +// - +// +// SKIPPED items: +// - +// ============================================================================ +``` + +## Re-validate + +After user edits the REVIEW items: + +```bash +npx cds compile /db/schema.cds --to edmx +npx cds compile /db/schema.cds --to sql +``` + +## BTP vs On-Premise Differences + +| Aspect | BTP target | On-prem CAP (out of scope v1) | +|---|---|---| +| HANA deployment | HANA Cloud + HDI container | HANA on-prem + HDI | +| `mandt` / `CLNT` | Always OMIT — single-tenant per service | Same: CAP target is multi-tenant via service tenancy, not table field | +| Reserved words | CAP CDS reserves: `entity`, `aspect`, `type`, `service`, `action`, `function` — rename collisions | Same | +| Currency / Unit | Same semantics annotations | Same | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| `SAPRead TABL` returns empty | Table doesn't exist or wrong type | Verify with `SAPSearch query=""` | +| Append structure not detected | Append linked via `APPEND` flag | Re-read with explicit DDIC dictionary scan | +| Field name collision (e.g., two `ORDER_ID` fields) | Append merged from multiple sources | Manual rename; flag in REVIEW section | +| Foreign key target outside scope | checkTable points outside Z package | Choice: extend scope, omit association, or use `String` FK reference | +| Currency without companion | DDIC table has `CURR` without referenced `CUKY` field | Manual fix; emit entity with `// REVIEW: missing currency reference` | +| `cds compile` fails on `Decimal(31, 0)` | DDIC `DEC(31)` exceeds CAP max | Cap at `Decimal(34, 2)` or use `Double` if precision not critical | + +## What This Skill Does NOT Do + +- **No table data migration** — generates schema only, NOT INSERT scripts +- **No HANA-specific syntax** — generates portable CAP CDS, not HDI artifacts (CAP `cds build` does that) +- **No append-structure deep recursion** — handles direct appends, not nested appends-of-appends +- **No CDS view migration** — that's [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) territory (Z CDS views → released SAP views OR keep as Z) +- **No semantic merge** — if two tables represent the same business entity (rare), no auto-merge; manual modeling needed +- **No table-function (`TF`) handling** — function-based tables are skipped with warning + +## When to Use This Skill + +- As Step 3 of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) +- Standalone: greenfield CAP project that needs to mirror existing Z table inventory +- Standalone: refactor an existing CAP schema after schema drift (regenerate baseline, compare with current) +- During architecture spike: get a quick CDS skeleton from a Z package for evaluation + +## When NOT to Use This Skill + +- Source tables are pooled / clustered → redesign required, this skill skips them +- Tables are in standard SAP namespace (T*, V*, M*) → use released SAP CDS views via `@sap/cds-types-released`; don't regenerate +- Target is HANA HDI native (not CAP) → use `hana-cli` or HANA Web IDE Cloud generators + +## Follow-up + +After this skill produces the schema: + +- [modernize-abap-cap-service](modernize-abap-cap-service.md) — generate service definitions on top of these entities +- [generate-cds-unit-test](generate-cds-unit-test.md) — generate unit tests for the entities with calculations +- Manual: review REVIEW items, refine type mappings, decide on association policies (Association vs Composition) + +## References + +- [CAP CDS reference](https://cap.cloud.sap/docs/cds/cdl) +- [CAP common aspects](https://cap.cloud.sap/docs/cds/common) +- [CAP semantic annotations](https://cap.cloud.sap/docs/cds/annotations) +- [DDIC type reference](https://help.sap.com/docs/abap-cloud/abap-rap-cloud-development-tools/data-types) diff --git a/skills/modernize-abap-cap-service.md b/skills/modernize-abap-cap-service.md new file mode 100644 index 00000000..f88ca4a9 --- /dev/null +++ b/skills/modernize-abap-cap-service.md @@ -0,0 +1,484 @@ +# Modernize ABAP CAP Service + +Generate CAP service definitions (`srv/*.cds`) and TypeScript handler stubs (`srv/handlers/*.ts`) from a Z* package's reports (`PROG`), function modules (`FUNC`), and behavior-relevant classes (`CLAS`). Maps ABAP procedural / object-oriented logic to CAP intent: entities exposed as projections, action signatures derived from FM parameters, handler scaffolds with TODO markers for business logic. + +Sub-skill of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md). Assumes the schema sub-skill [modernize-abap-cap-schema](modernize-abap-cap-schema.md) has produced `db/schema.cds` (entities + associations exist) before this skill runs. + +This skill produces a **scaffold + plan**, not production-ready business logic. Handler bodies are stubs with `// TODO: implement` comments and parameter passing wired up; the user is expected to translate ABAP business logic in a second pass. + +## v1 Guardrails (fast path) + +- **Read-only ARC-1** — no SAPWrite calls +- **CAP runtime: Node.js** — TypeScript handlers +- **OData V4 only** +- **One service per package by default** — single `srv/service.cds` covers the whole package; user can split later +- **One handler file per service** — `srv/handlers/.ts` aggregates all action handlers +- **No CDS query rewriting** — ABAP SELECT statements appear in handler comments, not auto-translated to CDS-QL + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Service file | `/srv/service.cds` | Single-service per package | +| Service name | derived: `Service` (e.g., `ZSALES_PKG` → `SalesService`) | Drops Z prefix, CamelCase | +| Service namespace | matches schema namespace | Coherent with [modernize-abap-cap-schema](modernize-abap-cap-schema.md) | +| Entity exposure | All entities from schema projected READ-only | Safe default; user adds CRUD where needed | +| Action mapping | FM → unbound action (with first ENTITY param → bound action) | OData V4 convention | +| Handler runtime | Node.js TypeScript | CAP-native | +| Handler file pattern | `srv/handlers/.ts` (one per service) | Easier discovery | +| Action body | `// TODO: implement` stub + parameter destructuring | Compile-clean, runtime-rejected with `req.reject(501)` | +| Error handling | `rejectSafe` pattern | Don't leak `err.message` to client | +| Audit | `@audit-log` annotation on write actions | BTP-native | +| Validation | `@assert.range` / `@assert.format` mirrored from DDIC | Inherit DDIC-level checks | +| Authorization | placeholder `@(restrict: [{ to: 'authenticated-user' }])` | Refined later by [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md) | + +## Input + +The user provides: + +- **Z package name** (required) — same package used in schema step +- **Target directory** (required) — must already contain `db/schema.cds` + +Optionally: + +- **Service split** — `--split-by entity` (one service per entity) | `--split-by submodule` (use sub-package boundaries) +- **Action policy** — `--actions-only` (skip projections) | `--projections-only` (skip actions) +- **Object scope** — comma list of PROG/FUNC/CLAS names to include +- **Skip classes** — `--skip-class` if user has already migrated class logic separately + +## Step 1: Verify Prerequisite + +### 1a. Ensure schema exists + +```bash +test -f /db/schema.cds +``` + +If missing, stop and recommend running [modernize-abap-cap-schema](modernize-abap-cap-schema.md) first. + +### 1b. Parse entity list from schema + +Read `/db/schema.cds` and extract entity names + their associations. Use these for: + +- Projection generation in service.cds +- Action parameter typing (e.g., `customer: Association to Customer` ↔ `customerID: UUID` in action signature) + +## Step 2: Enumerate Source Objects + +### 2a. List PROG/FUNC/CLAS in package + +``` +SAPRead(type="DEVC", name="") +``` + +Filter to keep `objectType in (PROG, FUNC, FUGR, CLAS)`. For FUGR (function group), traverse to list contained FUNC objects: + +``` +SAPRead(type="FUGR", name="") +``` + +### 2b. Classify by intent + +| ABAP object | Heuristic | CAP target | +|---|---|---| +| `FUNC` RFC-enabled with `IMPORTING`/`EXPORTING` params | Likely entry-point | Service action (unbound or bound) | +| `FUNC` local (no RFC) | Helper/utility | Handler private function (NOT exposed in service) | +| `PROG` (executable report) | Batch / one-shot job | Service action `runReport(...)` or cron job | +| `PROG` (module pool) | UI flow + business logic | Multiple actions decomposed from flow logic + Fiori-mapped UI | +| `CLAS` with `STATIC` factory methods | Business logic | Handler import + method-by-method to action | +| `CLAS` instance with state | Stateful logic | Refactor: extract pure functions into handler | +| `CLAS` with `ENH_*` prefix | Enhancement | Skip (Clean Core violation; flag in [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md)) | + +### 2c. Read source for analysis + +For each retained object: + +``` +SAPRead(type="", name="") +``` + +Cache responses — same object may be analyzed twice (e.g., as entry-point and as called helper). + +## Step 3: Extract Action Signatures from Function Modules + +For each FUNC retained as entry-point: + +### 3a. Parse FM signature + +ARC-1 `SAPRead(type="FUNC", name="")` returns a structured response including: + +- `importing`: array of `{name, type, optional, default}` +- `exporting`: array of `{name, type}` +- `changing`: array of `{name, type, optional}` +- `tables`: array of `{name, type, structure}` +- `exceptions`: array of `{name, description}` +- `rfcEnabled`: boolean + +Build the action signature according to these rules: + +#### Importing → action parameters + +```cds +// ABAP FM signature (example): +// IMPORTING iv_customer_id TYPE kunnr +// iv_company_code TYPE bukrs +// is_options TYPE zsales_options +// +// CAP action signature: +action getCustomerOrders( + customerId: String(10), + companyCode: String(4), + options: SalesOptionsType +) returns array of Order; +``` + +#### Type mapping for parameters + +Same DDIC → CDS type table as [modernize-abap-cap-schema](modernize-abap-cap-schema.md) Step 3. Additionally: + +| ABAP FM parameter type | CAP action target | +|---|---| +| Single field (`TYPE kunnr`) | `: String(10)` (or whatever DDIC maps to) | +| Structure (`TYPE zsales_header`) | `: { ... }` inline type OR `: TypeName` from `srv/types.cds` | +| Internal table (`TABLE OF ...`) | `: array of { ... }` | +| Returning table (in `EXPORTING`) | `returns array of ` | +| Returning single | `returns ` | +| Returning structure | `returns ` | +| OK / FAIL pattern (`E_RETURN TYPE bapiret2`) | NOT a return — use `req.reject` or `req.error` pattern in handler | + +#### Exceptions → CAP error pattern + +ABAP exceptions don't have direct CAP equivalents. Map to handler error handling: + +```typescript +// ABAP FM raised exception NOT_FOUND +// CAP handler equivalent: +if (!customer) { + return req.reject(404, `Customer ${customerId} not found`); +} +``` + +### 3b. Decide bound vs unbound action + +| Heuristic | Decision | +|---|---| +| First parameter is an entity key (e.g., `iv_customer_id` for `Customer`) | **Bound** action on that entity: `entity Customer { ... } actions { action getOrders() returns array of Order; }` | +| All parameters are filters / config | **Unbound** action: `action runReport(...)` | +| Returns a single instance of a known entity | **Bound** action that returns `$self` after modification, or unbound with explicit return | + +### 3c. Emit action in service.cds + +```cds +service SalesService { + // Entity projections + entity Customers as projection on schema.Customer; + entity Orders as projection on schema.Order; + + // Bound actions on Customer + extend entity Customers with actions { + @audit-log + action getOrders(filter: SalesFilterType) returns array of Orders; + } + + // Unbound actions + @audit-log + action runMonthlyReport(month: Integer, year: Integer) returns array of ReportLine; +} + +// Inline types declared in srv/types.cds +type SalesFilterType : { + fromDate : Date; + toDate : Date; + customerId : String(10) @assert.format: 'KUNNR-pattern'; +}; + +type ReportLine : { + customerId : String(10); + customerName: String(80); + netAmount : Decimal(15, 2) @Semantics.amount.amount; + currency : String(5) @Semantics.amount.currencyCode; +}; +``` + +## Step 4: Generate Handler Scaffolds + +### 4a. Handler file structure + +For each service, emit `srv/handlers/.ts`: + +```typescript +import cds from '@sap/cds'; +import type { Service, Request } from '@sap/cds'; + +const LOG = cds.log('sales-service'); + +export default class SalesServiceImpl extends cds.ApplicationService { + async init() { + const { Customers, Orders } = this.entities; + + // Bound action: getOrders on Customer + this.on('getOrders', Customers, async (req: Request) => { + const customerId = req.params[0]?.ID; + const { filter } = req.data; + + LOG.info(`getOrders called for customer=${customerId}, filter=${JSON.stringify(filter)}`); + + // TODO: implement business logic + // ABAP source: FUNCTION Z_GET_CUSTOMER_ORDERS (package: ZSALES_PKG) + // ABAP signature: + // IMPORTING iv_customer_id TYPE kunnr + // is_filter TYPE zsales_filter + // EXPORTING et_orders TYPE TABLE OF zsales_order + // EXCEPTIONS not_found -> req.reject(404) + // bad_input -> req.reject(400) + // + // ABAP business logic excerpt (first 20 lines): + // SELECT * FROM zsales_order_h + // INTO TABLE @et_orders + // WHERE kunnr = @iv_customer_id + // AND erdat BETWEEN @is_filter-from_date AND @is_filter-to_date. + // IF sy-subrc <> 0. + // RAISE not_found. + // ENDIF. + // + // CAP equivalent suggestion (CDS-QL): + // return SELECT.from(Orders).where({ customer_ID: customerId, ... }); + + return req.reject(501, 'Not yet implemented'); + }); + + // Unbound action: runMonthlyReport + this.on('runMonthlyReport', async (req: Request) => { + const { month, year } = req.data; + + LOG.info(`runMonthlyReport called month=${month} year=${year}`); + + // TODO: implement business logic + // ABAP source: PROGRAM Z_MONTHLY_REPORT + // Excerpt: ... + + return req.reject(501, 'Not yet implemented'); + }); + + await super.init(); + } +} +``` + +### 4b. ABAP source excerpt injection + +For each TODO comment, include a 10-20 line excerpt from the ABAP source to give the user immediate context when implementing: + +- The FM signature (parsed) +- The first 15-20 lines of the FORM/METHOD/main logic +- Any `RAISE` / `EXIT` / `MESSAGE E` statements (these become `req.reject`) +- Major `SELECT` statements (suggest CDS-QL translation in comment) + +### 4c. Common ABAP → CAP handler pattern mappings + +Embed these as inline guidance comments where relevant: + +| ABAP pattern | CAP TypeScript handler | +|---|---| +| `SELECT ... FROM ... INTO TABLE ... WHERE ...` | `await SELECT.from().where({ ... })` | +| `MODIFY FROM TABLE ...` | `await UPDATE().set(...).where(...)` or `cds.update(, key).with(...)` | +| `INSERT FROM TABLE ...` | `await INSERT.into().entries(...)` | +| `DELETE FROM WHERE ...` | `await DELETE.from().where(...)` | +| `LOOP AT ... ASSIGNING ` | `for (const item of items) { ... }` | +| `READ TABLE ... WITH KEY ... ` | `items.find(it => it.key === ...)` | +| `CALL FUNCTION 'Z_HELPER_FM'` | Refactor: inline function OR if released SAP API: `await cds.connect.to('').(...)` | +| `RAISE EXCEPTION TYPE cx_*` | `throw new Error(...)` OR `req.reject(code, message)` | +| `MESSAGE E001(zsales)` | `req.reject(400, '')` | +| `COMMIT WORK.` | implicit in CAP: `req.on('succeeded', ...)` post-commit hooks | +| `ROLLBACK WORK.` | `req.on('failed', ...)` | +| `AUTHORITY-CHECK OBJECT 'Z_AUTH'` | `if (!req.user.is('ScopeName')) return req.reject(403)` | +| `WRITE: / ULINE.` | NOT supported — UI rendered by Fiori app, not handler | +| `CALL SCREEN xxx.` | NOT supported — UI flow → Fiori Elements routing | + +### 4d. Class method extraction + +For `CLAS` retained: + +- Each PUBLIC instance/static method that mutates state → handler action or private helper +- Each PRIVATE method → handler private function +- Constructor logic → handler `init()` setup +- Class attributes → handler module-scope state (warn: avoid stateful handlers) + +Example for `ZCL_SALES_CALCULATOR.compute_total`: + +```typescript +// Imported from class ZCL_SALES_CALCULATOR.compute_total +async function computeTotal(orderId: string): Promise<{ amount: number; currency: string }> { + // TODO: implement + // ABAP method signature: + // compute_total IMPORTING iv_order_id TYPE zsales_order-id + // RETURNING VALUE(rs_result) TYPE zsales_total. + // + // ABAP excerpt: + // SELECT SUM( netwr ) AS amount, waers AS currency + // FROM zsales_item + // INTO @rs_result + // WHERE order_id = @iv_order_id + // GROUP BY waers. + + throw new Error('computeTotal not yet implemented'); +} +``` + +## Step 5: Generate srv/types.cds (if needed) + +If any action signature references a complex inline type, extract to `srv/types.cds`: + +```cds +namespace ; + +type SalesFilterType : { + fromDate : Date; + toDate : Date; + customerId : String(10); +}; + +type ReportLine : { + customerId : String(10); + customerName : String(80); + netAmount : Decimal(15, 2) @Semantics.amount.amount; + currency : String(5) @Semantics.amount.currencyCode; +}; +``` + +Import in `srv/service.cds`: + +```cds +using { .SalesFilterType, .ReportLine } from './types'; +``` + +## Step 6: Validate + +### 6a. CDS compile + +```bash +npx cds compile /srv --service all --to edmx > /dev/null +``` + +Must succeed. Common errors: + +| Error | Cause | Fix | +|---|---|---| +| `Cannot resolve entity 'X' in projection` | Entity name mismatch with schema.cds | Re-read schema.cds; verify namespace and exact entity name | +| `Action parameter type 'Y' not found` | Inline type referenced but not declared | Move type definition to `srv/types.cds` | +| `Duplicate action 'foo'` | Same FM name appears in two FUGR | Disambiguate: prefix with FUGR name or refactor | +| `Bound action requires entity context` | First param doesn't match an entity key | Re-evaluate bound vs unbound classification | + +### 6b. TypeScript compile + +```bash +cd && npx tsc --noEmit srv/handlers/*.ts +``` + +If errors → most likely typing issue with action parameters. Add explicit type annotations or import types from `@cap-js/cds-types` (auto-generated). + +### 6c. Lint (CAP eslint if configured) + +```bash +cd && npx eslint srv/ +``` + +## Step 7: Emit Migration Notes + +Append at end of `srv/service.cds`: + +```cds +// ============================================================================ +// Migration notes (ABAP source: → CAP service) +// ============================================================================ +// +// - entities exposed as projections +// - bound actions (derived from

Function Modules) +// - unbound actions (derived from reports) +// - handler files generated with TODO stubs +// +// Handlers requiring manual implementation: +// - +// +// ABAP objects SKIPPED: +// - +// +// Suggested follow-up: +// 1. Review each TODO in srv/handlers/*.ts +// 2. Translate ABAP business logic per the inline pattern hints +// 3. Run modernize-abap-fiori-elements to generate UI for these entities +// 4. Run modernize-abap-auth-mapping to refine the @restrict placeholders +// ============================================================================ +``` + +## Re-validate + +After user implements handler logic: + +```bash +cd +npx cds compile srv --service all --to edmx +npx tsc --noEmit srv/handlers/*.ts +npm test +``` + +## BTP vs On-Premise Differences + +| Aspect | BTP target | On-prem CAP (out of scope v1) | +|---|---|---| +| Handler runtime | Node.js on Cloud Foundry | Node.js on-prem or Cloud Foundry on-prem | +| Logging | `cds.log()` → BTP Application Logging | `cds.log()` → stdout (caller-defined sink) | +| Audit | `@audit-log` → BTP Audit Log Service | `@audit-log` → custom adapter | +| Remote calls | `cds.connect.to('')` via BTP Destination Service | Same — Destination Service is portable | +| Transactions | `cds.tx(req)` — auto-commit on `req.on('succeeded')` | Same | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| `SAPRead FUNC` returns empty signature | FM has no parameters (only TABLES or LOCAL types) | Treat as unbound action; flag in TODO | +| FM uses `BAPIRET2` for error return | Standard SAP error pattern | Map to `req.reject` in handler; document in TODO | +| Class method calls a deprecated SAP API | Clean Core gap (Level B/C/D) | Skill emits TODO with `// REVIEW: replace with released API per modernize-abap-clean-core-gap report` | +| Program has SY-* dependency (sy-uname, sy-datum) | Implicit context | Map to: `sy-uname` → `req.user.id`; `sy-datum` → `new Date().toISOString().slice(0, 10)` | +| Function group has internal tables shared across FMs | State leakage | Refactor: handler module state is generally unsafe; flag for redesign | +| Recursive call to `Z_HELPER_FM` | Infinite recursion risk | Skill doesn't translate logic, just adds comment; user must avoid in handler | + +## What This Skill Does NOT Do + +- **No business logic translation** — handlers are stubs with TODO comments + ABAP excerpts for context +- **No SELECT → CDS-QL auto-conversion** — suggests CDS-QL in comments; manual translation needed +- **No PERFORM → handler function auto-conversion** — flagged in comments +- **No screen flow / module pool decomposition** — UI lives in Fiori Elements ([modernize-abap-fiori-elements](modernize-abap-fiori-elements.md)) +- **No CALL SCREEN handling** — UI flow doesn't map to CAP backend +- **No FM tables-parameter migration** — flagged as deprecated pattern; user redesigns to entities/actions +- **No ABAP exception class hierarchy mapping** — single error pattern via `req.reject` / `Error` +- **No replacement of deprecated APIs** — that's [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) + +## When to Use This Skill + +- As Step 4 of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md), after schema generation +- Standalone: extend an existing CAP service with new actions derived from Z FMs +- Onboarding: get a quick map of "what ABAP entry-points exist in this package" without running the orchestrator + +## When NOT to Use This Skill + +- ABAP package uses CFW (Control Framework) heavily → UI logic doesn't port; redesign UI in Fiori Elements first +- ABAP code uses dynamic call (`PERFORM ... IN PROGRAM`, `CALL FUNCTION DESTINATION DYNAMIC`) → handler scaffolds will not capture indirect dispatch; manual analysis required +- Source contains BSP applications → use [convert-ui5-to-fiori-elements](convert-ui5-to-fiori-elements.md) or equivalent UI skill + +## Follow-up + +After this skill produces the service scaffold: + +- [modernize-abap-fiori-elements](modernize-abap-fiori-elements.md) — generate Fiori Elements V4 UI for the new service +- [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md) — refine `@restrict` annotations from AUTHORITY-CHECK in source +- [generate-cds-unit-test](generate-cds-unit-test.md) — generate tests for service projections with CASE/computed fields +- Manual: implement handler TODOs by translating ABAP business logic + +## References + +- [CAP service definitions](https://cap.cloud.sap/docs/cds/cdl#services) +- [CAP TypeScript handlers](https://cap.cloud.sap/docs/node.js/typescript) +- [CAP CDS-QL](https://cap.cloud.sap/docs/node.js/cds-ql) +- [OData V4 bound/unbound actions](https://cap.cloud.sap/docs/cds/cdl#actions-and-functions) +- [CAP error handling](https://cap.cloud.sap/docs/node.js/best-practices#error-handling) diff --git a/skills/modernize-abap-to-btp-cap.md b/skills/modernize-abap-to-btp-cap.md new file mode 100644 index 00000000..56a13500 --- /dev/null +++ b/skills/modernize-abap-to-btp-cap.md @@ -0,0 +1,357 @@ +# Modernize ABAP to BTP CAP + +End-to-end migration orchestrator from classic ABAP custom code (Z* packages) to a BTP-native CAP application — Fiori Elements V4 frontend, Node.js backend, Cloud Foundry deployment artifacts. + +This skill chains together specialized `modernize-abap-*` skills (Clean Core gap analysis → CDS schema → CAP service → Fiori Elements → auth mapping → MTA deployment) to produce a complete target CAP project. Combines ARC-1 (source / dependency / lint via ADT) with `mcp-sap-docs` (Clean Core release state via [`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc)). + +Different from [migrate-custom-code](migrate-custom-code.md): that skill fixes ATC findings inside an ABAP system; this one **leaves the ABAP system untouched** and produces a side-by-side BTP CAP target project that consumes released S/4HANA APIs instead. + +## v1 Guardrails (fast path) + +- **Single Z* package per run** — split very large packages (> 200 objects) into sub-packages first +- **CAP Node.js target** — Java target deferred to v2 +- **Fiori Elements V4 only** — Freestyle UI5 deferred +- **Cloud Foundry deployment** — Kyma deferred to v2 +- **Read-only ARC-1 access** — no `SAPWrite` needed; can run against any ARC-1 deployment without `--allow-writes` +- **Sandbox output** — generates everything under `./target-cap-staging/`; user reviews + activates with explicit `--apply` flag +- **Scaffold + plan, not auto-deploy** — `cf push` and HANA service binding stay manual + +For research-first, multi-package, or production-quality migrations, run each sub-skill individually with explicit configuration instead of this orchestrator. + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Target system type | `public_cloud` | Clean Core Level A is the migration goal | +| ATC variant | `ABAP_CLOUD_READINESS` if available, else system default | Cloud-target focus | +| CAP runtime | Node.js | Default for greenfield BTP CAP; broader ecosystem | +| OData version | V4 | Current SAP standard; required for Fiori Elements V4 | +| Fiori app pattern | List Report + Object Page (LROP) | Most common CRUD pattern | +| Auth provider | XSUAA | BTP-native; Clean Core L-A compliant | +| DB target | HANA Cloud (prod) + SQLite (dev) | CAP-native; multi-target via `cds.requires` | +| Output mode | Sandbox (`./target-cap-staging/`) | Reversible review before apply | +| Output language | English (i18n bundle) | Default; user can add IT/DE/ES bundles later | +| Sub-skill chain | All 6 sequential | Full end-to-end | +| MTA build tool | `mbt` (Multitarget Application Build Tool) | SAP standard for BTP CF | + +## Input + +The user provides: + +- **Z package name** (required) — e.g., `ZSALES_PKG`, `Z_FI_CUSTOM` +- **Target directory** (required) — where CAP project will be generated (must not exist or must be empty) + +Optionally: + +- **ATC variant override** — `S4HANA_2023`, `ABAP_CLOUD_READINESS`, system default +- **CAP runtime override** — `node` (default) | `java` (v2 only) +- **Skip sub-skills** — comma list, e.g., `--skip fiori,mta` if user wants only schema + service +- **Fiori app namespace** — default derived from package name (`zsales_pkg` → `com.example.zsalespkg`) +- **Output mode** — `sandbox` (default) | `apply` (write directly to target dir) + +If only the package name is provided, ask for the target directory once and proceed with all defaults. + +## Step 1: System Probe + Target Setup + +### 1a. Verify ARC-1 + ADT availability + +``` +SAPManage(action="probe") +``` + +**Critical gate:** If probe reports CDS/RAP unavailable, stop — modernization needs the source system for inventory. If the system is BTP and the package is in `$TMP`, warn the user that the migration target should use a transportable package on the source side first. + +### 1b. Validate target directory + +Ensure the user-provided target directory either does not exist, is empty, or contains only a previous staging output. Do NOT overwrite an existing CAP project without explicit confirmation. + +### 1c. Initialize CAP skeleton + +Create the target structure (sandbox): + +``` +/.target-cap-staging/ +├── db/ +│ └── schema.cds (empty — filled by Step 3) +├── srv/ +│ ├── service.cds (empty — filled by Step 4) +│ └── handlers/ (empty — filled by Step 4) +├── app/ (empty — filled by Step 5) +├── xs-security.json (empty — filled by Step 6) +├── mta.yaml (empty — filled by Step 7) +├── package.json (CAP boilerplate) +├── .cdsrc.json +├── .gitignore +└── README.md (porting plan + ADRs) +``` + +The skeleton mirrors a `cds init` output; the orchestrator owns it. Generate `package.json` with: + +```json +{ + "name": "", + "version": "0.1.0", + "dependencies": { + "@sap/cds": "^9", + "@cap-js/sqlite": "^2", + "@cap-js/hana": "^2", + "express": "^5" + }, + "scripts": { + "start": "cds-serve", + "watch": "cds watch", + "build": "cds build --production", + "test": "cds test" + } +} +``` + +## Step 2: Run modernize-abap-clean-core-gap (sub-skill) + +Hand off to [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) with the same package + target directory. + +**Expected output**: `/.target-cap-staging/docs/clean-core-gap.md` containing: + +- Per-object Clean Core level (A/B/C/D) for every `Z*` object in scope +- Cross-edition compliance matrix (`public_cloud` × `private_cloud` × `on_premise`) +- Replacement suggestions for Level B/C/D references +- Risk assessment: per-object migration effort estimate + +**Gate**: if more than 30% of objects are Level C/D, the skill warns the user that a "lift-and-shift" approach may be infeasible and recommends [migrate-custom-code](migrate-custom-code.md) first to fix ATC findings before retrying modernization. + +## Step 3: Run modernize-abap-cap-schema (sub-skill) + +Hand off to [modernize-abap-cap-schema](modernize-abap-cap-schema.md) with the same package + target directory. + +**Expected output**: `/.target-cap-staging/db/schema.cds` containing: + +- Namespace derived from package (e.g., `namespace com.example.zsalespkg;`) +- One CDS entity per `TABL` in the source package +- DDIC → CDS type mapping applied (DEC → Decimal, CHAR → String, DATS → Date, …) +- Associations / compositions from foreign-key references +- `@assert` annotations for NOT NULL fields +- `@Common.Label` from ABAP table short text +- `cuid` / `managed` aspects auto-applied where keys are sysuuid_x16 or fields named `created_*`/`changed_*` + +**Validation**: + +```bash +npx cds compile /.target-cap-staging/db/schema.cds --to edmx +``` + +Must succeed. If errors → log + stop + show user the offending entities. + +## Step 4: Run modernize-abap-cap-service (sub-skill) + +Hand off to [modernize-abap-cap-service](modernize-abap-cap-service.md) with the same package + target directory. + +**Expected output**: +- `/.target-cap-staging/srv/service.cds` with entity projections + action signatures derived from Z* function modules + reports +- `/.target-cap-staging/srv/handlers/*.ts` TypeScript stub files with `// TODO` placeholders for business logic +- One handler file per service action + +**Validation**: + +```bash +npx cds compile /.target-cap-staging/srv --to edmx +``` + +## Step 5: Run modernize-abap-fiori-elements (sub-skill, optional) + +Skip if `--skip fiori` was provided. + +Hand off to [modernize-abap-fiori-elements](modernize-abap-fiori-elements.md). + +**Expected output**: `/.target-cap-staging/app//` for each main entity, containing: + +- `webapp/manifest.json` with Fiori Elements V4 LROP route configuration +- `webapp/Component.ts` +- `annotations/annotations.cds` with `UI.LineItem` + `UI.HeaderInfo` + `UI.FieldGroup` + `UI.Facets` +- `webapp/i18n/i18n.properties` (default EN) + +## Step 6: Run modernize-abap-auth-mapping (sub-skill, optional) + +Skip if `--skip auth` was provided. + +Hand off to [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md). + +**Expected output**: +- `/.target-cap-staging/xs-security.json` with scopes derived from `AUTHORITY-CHECK` statements +- `@restrict` annotations injected into `srv/service.cds` +- Role-templates aligned with ABAP authorization objects + +## Step 7: Run modernize-abap-btp-mta (sub-skill, optional) + +Skip if `--skip mta` was provided. + +Hand off to [modernize-abap-btp-mta](modernize-abap-btp-mta.md). + +**Expected output**: +- `/.target-cap-staging/mta.yaml` — MTA descriptor with srv + db-deployer + approuter modules +- `/.target-cap-staging/Dockerfile` — multi-stage Node.js build +- `/.target-cap-staging/manifest.yml` — CF deployment manifest +- `/.target-cap-staging/.cfignore` + +## Step 8: Generate Architecture Decision Records + +Write `/.target-cap-staging/docs/adr/` with one ADR per major decision: + +- `0001-cap-runtime-node.md` — Why Node.js (vs Java) +- `0002-db-target-hana-sqlite.md` — Why HANA Cloud prod + SQLite dev +- `0003-auth-xsuaa.md` — Why XSUAA (vs IAS / Keycloak) +- `0004-fiori-elements-v4-lrop.md` — Why FE V4 LROP pattern +- `0005-clean-core-level-a.md` — Why released APIs only + replacement strategy from Step 2 +- `0006-side-by-side-no-abap-write.md` — Why we leave the ABAP system read-only + +Each ADR follows the MADR (Markdown Architecture Decision Record) format: + +```markdown +# - + +## Status +Proposed + +## Context +<from inventory: what we observed in the Z package> + +## Decision +<the choice we made + rationale linked to Smart Defaults> + +## Consequences +<what enables / what blocks / what to revisit> +``` + +## Step 9: Final Audit + +Run pre-flight checks against the generated scaffold: + +```bash +cd <target>/.target-cap-staging +npm install +npx cds compile srv --service all --to edmx > /dev/null +npx cds compile db/schema.cds --to sql > /dev/null +``` + +If the target NOVA-style project has `scripts/ci/check-s4-compat-coverage.sh` (or equivalent Clean Core CI gate), invoke it. + +Emit final summary report at `<target>/.target-cap-staging/docs/migration-summary.md`: + +``` +Modernization Summary — <package> → BTP CAP +=========================================== + +Source ABAP: + Package: ZSALES_PKG + Objects audited: 47 + Tables (TABL): 12 + Reports (PROG): 8 + Function modules: 18 + Classes (CLAS): 6 + CDS views (DDLS): 3 + +Clean Core gap (target: public_cloud): + Level A objects: 31 (66%) + Level B objects: 9 (19%) + Level C objects: 5 (11%) + Level D objects: 2 (4%) + +Generated artifacts: + db/schema.cds 12 entities, 4 associations + srv/service.cds 3 services, 14 actions + srv/handlers/*.ts 14 handler stubs + app/*/webapp/manifest.json 2 Fiori Elements V4 apps + xs-security.json 9 scopes, 4 role-templates + mta.yaml 3 modules, 2 resources + docs/adr/ 6 ADR drafts + docs/clean-core-gap.md Risk assessment + replacement table + +Next steps (manual): + 1. Review docs/migration-summary.md + docs/clean-core-gap.md + 2. Review handler TODO comments and implement business logic + 3. Test locally: cd target-cap-staging && cds watch + 4. Bind HANA Cloud + run: cds deploy --to hana + 5. Build MTA: mbt build + 6. Deploy: cf deploy mta_archives/*.mtar +``` + +## Re-validate + +Re-run after manual handler implementations: + +```bash +cd <target>/.target-cap-staging +npm test +npx cds compile srv --service all --to edmx +``` + +## BTP vs On-Premise Differences (target architecture) + +| Aspect | BTP target (this skill) | On-Premise CAP target (out of scope v1) | +|---|---|---| +| Runtime | Node.js on Cloud Foundry | Node.js on ABAP Cloud or external server | +| Auth | XSUAA + Destination Service | XSUAA on CF or IAS or Keycloak | +| DB | HANA Cloud (prod) + SQLite (dev) | HANA on-prem + SQLite (dev) | +| S/4 connection | Destination Service + Communication Arrangement | Cloud Connector + Destination | +| Build | `mbt build` → MTA → `cf deploy` | `npm run build` + custom CI/CD | +| Auth propagation | Principal Propagation via X.509 | SAML or SSO custom | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| `SAPManage probe` reports CDS unavailable | System lacks RAP/CDS support | Stop — modernization needs source CDS read access | +| Target directory not empty | Risk of overwriting unrelated content | Ask user to confirm or pick a fresh directory | +| CDS compile fails after schema generation | Type mapping edge case (e.g., unusual `CURR` with non-standard `Semantics`) | Log offending entity, show source, allow user to edit + retry | +| `cf push` step missing CF CLI | User runs without CF CLI installed | Surface clearly + link to CF CLI install docs; this skill stops at scaffold | +| Clean Core gap > 30% C/D | Package too coupled to internal/deprecated APIs | Recommend [migrate-custom-code](migrate-custom-code.md) first to address findings, then retry | +| `npm install` fails offline | No network for CAP deps | Skill output is generated; user runs `npm install` separately | +| Source package contains > 200 objects | Too large for single-run handoff | Suggest splitting into sub-packages; skill processes top-level only | +| sub-skill output missing expected files | Sub-skill failed silently | Re-run failed sub-skill standalone with verbose flag to diagnose | + +## What This Skill Does NOT Do + +- **No SAP write operations** — leaves source ABAP system untouched (read-only ARC-1 mode) +- **No CF deployment automation** — generates artifacts only; `cf push` / `cf deploy` stay manual +- **No HANA service provisioning** — user creates HANA Cloud instance + binds manually +- **No XSUAA service creation** — user creates `xsuaa` service instance via `cf create-service` or BTP cockpit +- **No automatic handler logic generation** — handlers are stubs with `// TODO`; user implements business logic +- **No regression test execution** — generates test scaffold via `generate-cds-unit-test` (separate skill); execution is user's responsibility +- **No migration of ABAP CDS Views to released SAP CDS Views** — the [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) report suggests replacements; manual mapping required +- **No transport-coordinated migration** — this is greenfield CAP, no ABAP transport involvement +- **No big-bang cutover plan** — assume coexistence (source ABAP + target CAP) for a transition period +- **No source-system ATC fix application** — that's [migrate-custom-code](migrate-custom-code.md) + +## When to Use This Skill + +- **Greenfield BTP CAP migration** of a Z* package previously in ECC / S/4HANA on-premise / PCE +- **Lift-and-redesign** of a custom ABAP-only application stack to BTP-native architecture +- **POC / pilot** for evaluating BTP CAP as a target for custom code +- **Quarterly modernization assessment** — run with `--skip fiori,mta,auth` to get just the inventory + gap analysis as a planning artifact +- **Pre-deployment audit** — verify Clean Core compliance before committing to a BTP migration roadmap + +## When NOT to Use This Skill + +- **The package must stay in S/4 as ABAP Cloud** — use [generate-rap-service-researched](generate-rap-service-researched.md) instead (RAP on ABAP Cloud) +- **Single object refactor** — use [migrate-custom-code](migrate-custom-code.md) (ATC-driven fix) +- **Unused code retirement** — run [sap-unused-code](sap-unused-code.md) first to scope what's actually live +- **The package has > 30% Level C/D objects** — too risky; iterate on source-side fixes first +- **You need Kyma deployment** — v1 targets CF only; track Kyma support in v2 + +## Follow-up + +After successful run, the user typically runs: + +- [generate-cds-unit-test](generate-cds-unit-test.md) → for the new CAP CDS entities +- [generate-abap-unit-test](generate-abap-unit-test.md) → for any remaining source ABAP objects kept in coexistence +- [analyze-chat-session](analyze-chat-session.md) → review the modernization run, capture learnings +- Manual `cds watch` + Fiori UI review +- Manual `mbt build` + `cf deploy <mtar>` + +## References + +- [SAP CAP documentation](https://cap.cloud.sap) +- [SAP Fiori Elements V4](https://experience.sap.com/fiori-design-web/floorplan-list-report/) +- [BTP Cloud Foundry deployment](https://help.sap.com/docs/btp/sap-business-technology-platform/cloud-foundry-environment) +- [Clean Core principles](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) +- [SAP API release state repository](https://github.com/SAP/abap-atc-cr-cv-s4hc) +- [MTA development descriptor](https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications-in-cloud-foundry-environment) From a45a5b4f31affc1ac74c0ca5cd2afe4282d58bca Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.local> Date: Wed, 13 May 2026 12:07:28 +0200 Subject: [PATCH 02/18] refactor(skills): migrate modernize-abap-* to directory format with YAML frontmatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arc-1 main migrated skills from flat .md files to directory + SKILL.md + YAML frontmatter format (compatible with vercel-labs/skills CLI and the Anthropic Agent Skills standard). This PR's skills now follow the new format. ## Changes - `skills/modernize-abap-to-btp-cap.md` → `skills/modernize-abap-to-btp-cap/SKILL.md` - `skills/modernize-abap-cap-schema.md` → `skills/modernize-abap-cap-schema/SKILL.md` - `skills/modernize-abap-cap-service.md` → `skills/modernize-abap-cap-service/SKILL.md` Each SKILL.md now starts with YAML frontmatter: ```yaml --- name: <skill-name> description: <discovery description with "Use when..." pattern> --- ``` Cross-links between skills updated from `[name](name.md)` to `[name](../name/SKILL.md)` for sibling skills, matching the pattern used in existing skills (e.g., sap-clean-core-atc/SKILL.md → ../migrate-custom-code/SKILL.md). skills/README.md links updated to point to the new directory paths. ## Verification - All 3 skills have YAML frontmatter with discovery-friendly descriptions - All inline cross-links resolved to new paths - No orphan `*.md` files (git mv handled the rename, history preserved) - skills/README.md reflects new paths This is a zero-content-change refactor — only structural reorganization to align with the new arc-1 skill format introduced in upstream main. --- skills/README.md | 8 ++-- .../SKILL.md} | 15 ++++--- .../SKILL.md} | 31 ++++++++------- .../SKILL.md} | 39 +++++++++++-------- 4 files changed, 54 insertions(+), 39 deletions(-) rename skills/{modernize-abap-cap-schema.md => modernize-abap-cap-schema/SKILL.md} (93%) rename skills/{modernize-abap-cap-service.md => modernize-abap-cap-service/SKILL.md} (90%) rename skills/{modernize-abap-to-btp-cap.md => modernize-abap-to-btp-cap/SKILL.md} (86%) diff --git a/skills/README.md b/skills/README.md index a49df35c..aaa6f7d9 100644 --- a/skills/README.md +++ b/skills/README.md @@ -108,15 +108,15 @@ End-to-end greenfield migration from classic ABAP custom code (Z* packages) to B | Skill | What it does | When to use | |---|---|---| -| [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) | End-to-end migration orchestrator: Z package → BTP CAP scaffold (CDS + service + Fiori + xs-security + mta.yaml + ADRs) | Planning ECC / on-prem S/4 → BTP CAP greenfield migration | -| [modernize-abap-cap-schema](modernize-abap-cap-schema.md) | Z-tables (SE11) → CAP CDS entities with DDIC→CDS type mapping, FK→association inference, `cuid`/`managed` aspect auto-application | Data-model migration step; standalone for reverse-engineering Z tables to CDS | -| [modernize-abap-cap-service](modernize-abap-cap-service.md) | Z function modules / reports / classes → CAP service definitions + TypeScript handler stubs with TODO markers + ABAP source excerpts | Service-layer migration step; produces compile-clean scaffold ready for business-logic translation | +| [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap/SKILL.md) | End-to-end migration orchestrator: Z package → BTP CAP scaffold (CDS + service + Fiori + xs-security + mta.yaml + ADRs) | Planning ECC / on-prem S/4 → BTP CAP greenfield migration | +| [modernize-abap-cap-schema](modernize-abap-cap-schema/SKILL.md) | Z-tables (SE11) → CAP CDS entities with DDIC→CDS type mapping, FK→association inference, `cuid`/`managed` aspect auto-application | Data-model migration step; standalone for reverse-engineering Z tables to CDS | +| [modernize-abap-cap-service](modernize-abap-cap-service/SKILL.md) | Z function modules / reports / classes → CAP service definitions + TypeScript handler stubs with TODO markers + ABAP source excerpts | Service-layer migration step; produces compile-clean scaffold ready for business-logic translation | #### Modernization vs Cloud-Readiness skills - [sap-clean-core-atc](sap-clean-core-atc.md) classifies whether code **can stay in ABAP and move to S/4HANA Cloud / ABAP Cloud**. Source system stays SAP. - [migrate-custom-code](migrate-custom-code.md) **fixes ATC findings in place** to make ABAP cloud-ready. Source system stays SAP. -- [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) **rebuilds the application as BTP CAP** — leaves source ABAP untouched, produces a parallel BTP-native stack. Use when the target architecture is a CAP application, not just cloud-ready ABAP. +- [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap/SKILL.md) **rebuilds the application as BTP CAP** — leaves source ABAP untouched, produces a parallel BTP-native stack. Use when the target architecture is a CAP application, not just cloud-ready ABAP. #### Typical BTP modernization workflow diff --git a/skills/modernize-abap-cap-schema.md b/skills/modernize-abap-cap-schema/SKILL.md similarity index 93% rename from skills/modernize-abap-cap-schema.md rename to skills/modernize-abap-cap-schema/SKILL.md index 8716dd8d..5a068c4b 100644 --- a/skills/modernize-abap-cap-schema.md +++ b/skills/modernize-abap-cap-schema/SKILL.md @@ -1,8 +1,13 @@ +--- +name: modernize-abap-cap-schema +description: Generate a CAP CDS data model (db/schema.cds) from a Z* package's ABAP database tables (TABL). Applies DDIC→CDS type mapping (24 types), infers Associations/Compositions from foreign-key references, auto-applies cuid/managed aspects when applicable. Use when asked to "convert this Z table to CDS entity", "generate CAP schema from ABAP TABL", "reverse-engineer Z tables to CDS", or as sub-skill of modernize-abap-to-btp-cap. +--- + # Modernize ABAP CAP Schema Generate a CAP CDS data model (`db/schema.cds`) from a Z* package's database tables (`TABL`) and structures. Maps DDIC types to CDS types, infers associations from foreign-key references, applies `cuid` / `managed` aspects automatically, and emits a single namespace-scoped schema ready for `cds compile`. -Sub-skill of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) but also usable standalone when only data-model migration is needed (e.g., reverse-engineer a Z table inventory into CAP CDS for a fresh project). +Sub-skill of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) but also usable standalone when only data-model migration is needed (e.g., reverse-engineer a Z table inventory into CAP CDS for a fresh project). This is greenfield CDS *for CAP runtime* (`@sap/cds`) — NOT ABAP CDS DDL. The two share syntax fragments but have different semantics, type systems, and tooling. @@ -337,13 +342,13 @@ npx cds compile <target>/db/schema.cds --to sql - **No table data migration** — generates schema only, NOT INSERT scripts - **No HANA-specific syntax** — generates portable CAP CDS, not HDI artifacts (CAP `cds build` does that) - **No append-structure deep recursion** — handles direct appends, not nested appends-of-appends -- **No CDS view migration** — that's [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) territory (Z CDS views → released SAP views OR keep as Z) +- **No CDS view migration** — that's [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) territory (Z CDS views → released SAP views OR keep as Z) - **No semantic merge** — if two tables represent the same business entity (rare), no auto-merge; manual modeling needed - **No table-function (`TF`) handling** — function-based tables are skipped with warning ## When to Use This Skill -- As Step 3 of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md) +- As Step 3 of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) - Standalone: greenfield CAP project that needs to mirror existing Z table inventory - Standalone: refactor an existing CAP schema after schema drift (regenerate baseline, compare with current) - During architecture spike: get a quick CDS skeleton from a Z package for evaluation @@ -358,8 +363,8 @@ npx cds compile <target>/db/schema.cds --to sql After this skill produces the schema: -- [modernize-abap-cap-service](modernize-abap-cap-service.md) — generate service definitions on top of these entities -- [generate-cds-unit-test](generate-cds-unit-test.md) — generate unit tests for the entities with calculations +- [modernize-abap-cap-service](../modernize-abap-cap-service/SKILL.md) — generate service definitions on top of these entities +- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) — generate unit tests for the entities with calculations - Manual: review REVIEW items, refine type mappings, decide on association policies (Association vs Composition) ## References diff --git a/skills/modernize-abap-cap-service.md b/skills/modernize-abap-cap-service/SKILL.md similarity index 90% rename from skills/modernize-abap-cap-service.md rename to skills/modernize-abap-cap-service/SKILL.md index f88ca4a9..4d49b3b6 100644 --- a/skills/modernize-abap-cap-service.md +++ b/skills/modernize-abap-cap-service/SKILL.md @@ -1,8 +1,13 @@ +--- +name: modernize-abap-cap-service +description: Generate CAP service definitions (srv/*.cds) and TypeScript handler stubs (srv/handlers/*.ts) from a Z* package's reports (PROG), function modules (FUNC), and classes (CLAS). Maps ABAP signatures to OData V4 action signatures, classifies bound vs unbound actions, embeds ABAP source excerpts as TODO context. Use when asked to "convert this FM to CAP action", "generate CAP service from ABAP", "scaffold CAP handlers from Z program", or as sub-skill of modernize-abap-to-btp-cap. +--- + # Modernize ABAP CAP Service Generate CAP service definitions (`srv/*.cds`) and TypeScript handler stubs (`srv/handlers/*.ts`) from a Z* package's reports (`PROG`), function modules (`FUNC`), and behavior-relevant classes (`CLAS`). Maps ABAP procedural / object-oriented logic to CAP intent: entities exposed as projections, action signatures derived from FM parameters, handler scaffolds with TODO markers for business logic. -Sub-skill of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md). Assumes the schema sub-skill [modernize-abap-cap-schema](modernize-abap-cap-schema.md) has produced `db/schema.cds` (entities + associations exist) before this skill runs. +Sub-skill of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md). Assumes the schema sub-skill [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) has produced `db/schema.cds` (entities + associations exist) before this skill runs. This skill produces a **scaffold + plan**, not production-ready business logic. Handler bodies are stubs with `// TODO: implement` comments and parameter passing wired up; the user is expected to translate ABAP business logic in a second pass. @@ -21,7 +26,7 @@ This skill produces a **scaffold + plan**, not production-ready business logic. |---|---|---| | Service file | `<target>/srv/service.cds` | Single-service per package | | Service name | derived: `<NamespaceCamelCase>Service` (e.g., `ZSALES_PKG` → `SalesService`) | Drops Z prefix, CamelCase | -| Service namespace | matches schema namespace | Coherent with [modernize-abap-cap-schema](modernize-abap-cap-schema.md) | +| Service namespace | matches schema namespace | Coherent with [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) | | Entity exposure | All entities from schema projected READ-only | Safe default; user adds CRUD where needed | | Action mapping | FM → unbound action (with first ENTITY param → bound action) | OData V4 convention | | Handler runtime | Node.js TypeScript | CAP-native | @@ -30,7 +35,7 @@ This skill produces a **scaffold + plan**, not production-ready business logic. | Error handling | `rejectSafe` pattern | Don't leak `err.message` to client | | Audit | `@audit-log` annotation on write actions | BTP-native | | Validation | `@assert.range` / `@assert.format` mirrored from DDIC | Inherit DDIC-level checks | -| Authorization | placeholder `@(restrict: [{ to: 'authenticated-user' }])` | Refined later by [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md) | +| Authorization | placeholder `@(restrict: [{ to: 'authenticated-user' }])` | Refined later by [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md) | ## Input @@ -54,7 +59,7 @@ Optionally: test -f <target>/db/schema.cds ``` -If missing, stop and recommend running [modernize-abap-cap-schema](modernize-abap-cap-schema.md) first. +If missing, stop and recommend running [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) first. ### 1b. Parse entity list from schema @@ -87,7 +92,7 @@ SAPRead(type="FUGR", name="<group_name>") | `PROG` (module pool) | UI flow + business logic | Multiple actions decomposed from flow logic + Fiori-mapped UI | | `CLAS` with `STATIC` factory methods | Business logic | Handler import + method-by-method to action | | `CLAS` instance with state | Stateful logic | Refactor: extract pure functions into handler | -| `CLAS` with `ENH_*` prefix | Enhancement | Skip (Clean Core violation; flag in [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md)) | +| `CLAS` with `ENH_*` prefix | Enhancement | Skip (Clean Core violation; flag in [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md)) | ### 2c. Read source for analysis @@ -134,7 +139,7 @@ action getCustomerOrders( #### Type mapping for parameters -Same DDIC → CDS type table as [modernize-abap-cap-schema](modernize-abap-cap-schema.md) Step 3. Additionally: +Same DDIC → CDS type table as [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) Step 3. Additionally: | ABAP FM parameter type | CAP action target | |---|---| @@ -448,15 +453,15 @@ npm test - **No business logic translation** — handlers are stubs with TODO comments + ABAP excerpts for context - **No SELECT → CDS-QL auto-conversion** — suggests CDS-QL in comments; manual translation needed - **No PERFORM → handler function auto-conversion** — flagged in comments -- **No screen flow / module pool decomposition** — UI lives in Fiori Elements ([modernize-abap-fiori-elements](modernize-abap-fiori-elements.md)) +- **No screen flow / module pool decomposition** — UI lives in Fiori Elements ([modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md)) - **No CALL SCREEN handling** — UI flow doesn't map to CAP backend - **No FM tables-parameter migration** — flagged as deprecated pattern; user redesigns to entities/actions - **No ABAP exception class hierarchy mapping** — single error pattern via `req.reject` / `Error` -- **No replacement of deprecated APIs** — that's [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) +- **No replacement of deprecated APIs** — that's [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) ## When to Use This Skill -- As Step 4 of [modernize-abap-to-btp-cap](modernize-abap-to-btp-cap.md), after schema generation +- As Step 4 of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md), after schema generation - Standalone: extend an existing CAP service with new actions derived from Z FMs - Onboarding: get a quick map of "what ABAP entry-points exist in this package" without running the orchestrator @@ -464,15 +469,15 @@ npm test - ABAP package uses CFW (Control Framework) heavily → UI logic doesn't port; redesign UI in Fiori Elements first - ABAP code uses dynamic call (`PERFORM ... IN PROGRAM`, `CALL FUNCTION DESTINATION DYNAMIC`) → handler scaffolds will not capture indirect dispatch; manual analysis required -- Source contains BSP applications → use [convert-ui5-to-fiori-elements](convert-ui5-to-fiori-elements.md) or equivalent UI skill +- Source contains BSP applications → use [convert-ui5-to-fiori-elements](../convert-ui5-to-fiori-elements/SKILL.md) or equivalent UI skill ## Follow-up After this skill produces the service scaffold: -- [modernize-abap-fiori-elements](modernize-abap-fiori-elements.md) — generate Fiori Elements V4 UI for the new service -- [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md) — refine `@restrict` annotations from AUTHORITY-CHECK in source -- [generate-cds-unit-test](generate-cds-unit-test.md) — generate tests for service projections with CASE/computed fields +- [modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md) — generate Fiori Elements V4 UI for the new service +- [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md) — refine `@restrict` annotations from AUTHORITY-CHECK in source +- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) — generate tests for service projections with CASE/computed fields - Manual: implement handler TODOs by translating ABAP business logic ## References diff --git a/skills/modernize-abap-to-btp-cap.md b/skills/modernize-abap-to-btp-cap/SKILL.md similarity index 86% rename from skills/modernize-abap-to-btp-cap.md rename to skills/modernize-abap-to-btp-cap/SKILL.md index 56a13500..5509eec7 100644 --- a/skills/modernize-abap-to-btp-cap.md +++ b/skills/modernize-abap-to-btp-cap/SKILL.md @@ -1,10 +1,15 @@ +--- +name: modernize-abap-to-btp-cap +description: End-to-end migration orchestrator from classic ABAP Z* packages to a BTP-native CAP application (CAP Node.js + Fiori Elements V4 + Cloud Foundry). Chains together specialized modernize-abap-* sub-skills (clean-core-gap, schema, service, fiori, auth-mapping, mta). Use when asked to "port this Z package to BTP CAP", "modernize this ABAP code to CAP", "generate a CAP scaffold from this Z package", or "migrate this custom code to BTP greenfield". +--- + # Modernize ABAP to BTP CAP End-to-end migration orchestrator from classic ABAP custom code (Z* packages) to a BTP-native CAP application — Fiori Elements V4 frontend, Node.js backend, Cloud Foundry deployment artifacts. This skill chains together specialized `modernize-abap-*` skills (Clean Core gap analysis → CDS schema → CAP service → Fiori Elements → auth mapping → MTA deployment) to produce a complete target CAP project. Combines ARC-1 (source / dependency / lint via ADT) with `mcp-sap-docs` (Clean Core release state via [`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc)). -Different from [migrate-custom-code](migrate-custom-code.md): that skill fixes ATC findings inside an ABAP system; this one **leaves the ABAP system untouched** and produces a side-by-side BTP CAP target project that consumes released S/4HANA APIs instead. +Different from [migrate-custom-code](../migrate-custom-code/SKILL.md): that skill fixes ATC findings inside an ABAP system; this one **leaves the ABAP system untouched** and produces a side-by-side BTP CAP target project that consumes released S/4HANA APIs instead. ## v1 Guardrails (fast path) @@ -108,7 +113,7 @@ The skeleton mirrors a `cds init` output; the orchestrator owns it. Generate `pa ## Step 2: Run modernize-abap-clean-core-gap (sub-skill) -Hand off to [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) with the same package + target directory. +Hand off to [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) with the same package + target directory. **Expected output**: `<target>/.target-cap-staging/docs/clean-core-gap.md` containing: @@ -117,11 +122,11 @@ Hand off to [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) wi - Replacement suggestions for Level B/C/D references - Risk assessment: per-object migration effort estimate -**Gate**: if more than 30% of objects are Level C/D, the skill warns the user that a "lift-and-shift" approach may be infeasible and recommends [migrate-custom-code](migrate-custom-code.md) first to fix ATC findings before retrying modernization. +**Gate**: if more than 30% of objects are Level C/D, the skill warns the user that a "lift-and-shift" approach may be infeasible and recommends [migrate-custom-code](../migrate-custom-code/SKILL.md) first to fix ATC findings before retrying modernization. ## Step 3: Run modernize-abap-cap-schema (sub-skill) -Hand off to [modernize-abap-cap-schema](modernize-abap-cap-schema.md) with the same package + target directory. +Hand off to [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) with the same package + target directory. **Expected output**: `<target>/.target-cap-staging/db/schema.cds` containing: @@ -143,7 +148,7 @@ Must succeed. If errors → log + stop + show user the offending entities. ## Step 4: Run modernize-abap-cap-service (sub-skill) -Hand off to [modernize-abap-cap-service](modernize-abap-cap-service.md) with the same package + target directory. +Hand off to [modernize-abap-cap-service](../modernize-abap-cap-service/SKILL.md) with the same package + target directory. **Expected output**: - `<target>/.target-cap-staging/srv/service.cds` with entity projections + action signatures derived from Z* function modules + reports @@ -160,7 +165,7 @@ npx cds compile <target>/.target-cap-staging/srv --to edmx Skip if `--skip fiori` was provided. -Hand off to [modernize-abap-fiori-elements](modernize-abap-fiori-elements.md). +Hand off to [modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md). **Expected output**: `<target>/.target-cap-staging/app/<entity>/` for each main entity, containing: @@ -173,7 +178,7 @@ Hand off to [modernize-abap-fiori-elements](modernize-abap-fiori-elements.md). Skip if `--skip auth` was provided. -Hand off to [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md). +Hand off to [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md). **Expected output**: - `<target>/.target-cap-staging/xs-security.json` with scopes derived from `AUTHORITY-CHECK` statements @@ -184,7 +189,7 @@ Hand off to [modernize-abap-auth-mapping](modernize-abap-auth-mapping.md). Skip if `--skip mta` was provided. -Hand off to [modernize-abap-btp-mta](modernize-abap-btp-mta.md). +Hand off to [modernize-abap-btp-mta](../modernize-abap-btp-mta/SKILL.md). **Expected output**: - `<target>/.target-cap-staging/mta.yaml` — MTA descriptor with srv + db-deployer + approuter modules @@ -303,7 +308,7 @@ npx cds compile srv --service all --to edmx | Target directory not empty | Risk of overwriting unrelated content | Ask user to confirm or pick a fresh directory | | CDS compile fails after schema generation | Type mapping edge case (e.g., unusual `CURR` with non-standard `Semantics`) | Log offending entity, show source, allow user to edit + retry | | `cf push` step missing CF CLI | User runs without CF CLI installed | Surface clearly + link to CF CLI install docs; this skill stops at scaffold | -| Clean Core gap > 30% C/D | Package too coupled to internal/deprecated APIs | Recommend [migrate-custom-code](migrate-custom-code.md) first to address findings, then retry | +| Clean Core gap > 30% C/D | Package too coupled to internal/deprecated APIs | Recommend [migrate-custom-code](../migrate-custom-code/SKILL.md) first to address findings, then retry | | `npm install` fails offline | No network for CAP deps | Skill output is generated; user runs `npm install` separately | | Source package contains > 200 objects | Too large for single-run handoff | Suggest splitting into sub-packages; skill processes top-level only | | sub-skill output missing expected files | Sub-skill failed silently | Re-run failed sub-skill standalone with verbose flag to diagnose | @@ -316,10 +321,10 @@ npx cds compile srv --service all --to edmx - **No XSUAA service creation** — user creates `xsuaa` service instance via `cf create-service` or BTP cockpit - **No automatic handler logic generation** — handlers are stubs with `// TODO`; user implements business logic - **No regression test execution** — generates test scaffold via `generate-cds-unit-test` (separate skill); execution is user's responsibility -- **No migration of ABAP CDS Views to released SAP CDS Views** — the [modernize-abap-clean-core-gap](modernize-abap-clean-core-gap.md) report suggests replacements; manual mapping required +- **No migration of ABAP CDS Views to released SAP CDS Views** — the [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) report suggests replacements; manual mapping required - **No transport-coordinated migration** — this is greenfield CAP, no ABAP transport involvement - **No big-bang cutover plan** — assume coexistence (source ABAP + target CAP) for a transition period -- **No source-system ATC fix application** — that's [migrate-custom-code](migrate-custom-code.md) +- **No source-system ATC fix application** — that's [migrate-custom-code](../migrate-custom-code/SKILL.md) ## When to Use This Skill @@ -331,9 +336,9 @@ npx cds compile srv --service all --to edmx ## When NOT to Use This Skill -- **The package must stay in S/4 as ABAP Cloud** — use [generate-rap-service-researched](generate-rap-service-researched.md) instead (RAP on ABAP Cloud) -- **Single object refactor** — use [migrate-custom-code](migrate-custom-code.md) (ATC-driven fix) -- **Unused code retirement** — run [sap-unused-code](sap-unused-code.md) first to scope what's actually live +- **The package must stay in S/4 as ABAP Cloud** — use [generate-rap-service-researched](../generate-rap-service-researched/SKILL.md) instead (RAP on ABAP Cloud) +- **Single object refactor** — use [migrate-custom-code](../migrate-custom-code/SKILL.md) (ATC-driven fix) +- **Unused code retirement** — run [sap-unused-code](../sap-unused-code/SKILL.md) first to scope what's actually live - **The package has > 30% Level C/D objects** — too risky; iterate on source-side fixes first - **You need Kyma deployment** — v1 targets CF only; track Kyma support in v2 @@ -341,9 +346,9 @@ npx cds compile srv --service all --to edmx After successful run, the user typically runs: -- [generate-cds-unit-test](generate-cds-unit-test.md) → for the new CAP CDS entities -- [generate-abap-unit-test](generate-abap-unit-test.md) → for any remaining source ABAP objects kept in coexistence -- [analyze-chat-session](analyze-chat-session.md) → review the modernization run, capture learnings +- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) → for the new CAP CDS entities +- [generate-abap-unit-test](../generate-abap-unit-test/SKILL.md) → for any remaining source ABAP objects kept in coexistence +- [analyze-chat-session](../analyze-chat-session/SKILL.md) → review the modernization run, capture learnings - Manual `cds watch` + Fiori UI review - Manual `mbt build` + `cf deploy <mtar>` From ca684bc23d7073235912784d1a8d5bbc9df8d7db Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.local> Date: Wed, 13 May 2026 12:20:14 +0200 Subject: [PATCH 03/18] feat(skills): add SAP CAP Enterprise Audit skill chain Wave 1 (clean-core + customizing) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces 2 read-only audit skills for SAP CAP applications deployed on BTP consuming S/4HANA Tier-2 services. Skill-first, no MCP tool changes, zero code dependencies. ## What's included ### skills/sap-cap-clean-core-enforce/SKILL.md (386 lines) Discovery-driven Clean Core Level A enforcement audit. - Scans cds.connect.to() runtime + @cds.external service contracts - 4-source dynamic discovery (runtime + externals + catalog + CSV) - Probe identification heuristics (catalog explicit > naming convention > MCP search) - 3-edition MCP verification matrix (public_cloud × private_cloud × on_premise) - Catalog drift detection (over-declared / under-declared / missing-field) - Multi-dimensional compliance rollup (per-service / per-SAP_COM / coverage summary) - Safe auto-fix mode for under-declared availability + missing probeObject ### skills/sap-cap-customizing-honor/SKILL.md (494 lines) Bidirectional CSV ↔ code customizing audit. - Forward orphans (CSV seeded but never consumed) - Inverse orphans (code reads parameter but no CSV seed) - Hardcoded business decisions sweep (thresholds, timeouts, retry policies, UX timing) - Adapter factory dual-source verification (SystemParameter → cds.env → env → hardcoded) - Master-data FK ValueList enforcement (filter bar + edit form + TextArrangement cascade) - Catalog raise-site coverage check (ProcessStepCheck pattern, if applicable) - Safe auto-fix mode: add missing CSV seed rows + add filter-bar ValueList annotations ### skills/README.md update New "SAP CAP Enterprise Audit (Preview)" section listing both skills with discovery descriptions. Marked as Preview / WIP since full audit cluster (planned 6+ skills total) is iterative. ## Design decisions - **Read-only / idempotent** — no SAP write operations needed; both skills work against any ARC-1 deployment without --allow-writes - **mcp-sap-docs dependency** — clean-core-enforce uses mcp-sap-docs for SAP API release-state queries; customizing-honor uses no MCP (pure code analysis) - **Zero project-specific naming** — 100% generic, suitable for any CAP+S/4 stack; auto-detects project-specific patterns (compatibility policy file, CSV path) - **Safe-by-default auto-fix** — only additive changes auto-applied (expand availability, add ValueList annotations); destructive changes (remove catalog entries, remove CSV seeds) require manual decision - **Quarterly cadence** — designed for Q-review compliance checks + ad-hoc pre-deployment audits ## Follow-up Additional skills planned (separate PRs): - [ ] sap-cap-security-rbac-matrix (~600 lines) Multi-area parallel security audit + RBAC matrix + OWASP/ASVS mapping - [ ] sap-cap-lifecycle-matrix-audit (~500 lines) Workflow phase × step × check × state × role × policy matrix audit - [ ] sap-fiori-app-audit (~450 lines) End-to-end Fiori Elements V4 app audit (manifest + annotations + flags + EDMX) - [ ] sap-cap-text-polish (~400 lines) User-visible text polish for CAP + Fiori (i18n + microcopy + PII sanitize) - [ ] sap-cap-stack-audit-full (~400 lines) Multi-tool SAP stack audit orchestrator (UI5 + Fiori + CAP + BTP) - [ ] sap-cap-ci-gates-pattern (~400 lines) Library of 5 CI gate patterns for CAP projects ## Compatibility - Zero code changes - Zero new dependencies - Zero new MCP tools - Zero new ACTION_POLICY entries - Compatible with all ARC-1 versions (read-only SAPRead/SAPSearch/SAPContext) - Optional mcp-sap-docs dependency (clean-core-enforce only) ## Inspired by - sap-clean-core-atc (ABAP-side companion pattern) - migrate-custom-code (audit-style structure) - generate-rap-service-researched (research-first / smart-defaults pattern) --- skills/README.md | 11 + skills/sap-cap-clean-core-enforce/SKILL.md | 386 ++++++++++++++++ skills/sap-cap-customizing-honor/SKILL.md | 494 +++++++++++++++++++++ 3 files changed, 891 insertions(+) create mode 100644 skills/sap-cap-clean-core-enforce/SKILL.md create mode 100644 skills/sap-cap-customizing-honor/SKILL.md diff --git a/skills/README.md b/skills/README.md index 689acb01..4ed71a5f 100644 --- a/skills/README.md +++ b/skills/README.md @@ -100,6 +100,17 @@ Both skills produce the same RAP artifact stack. The difference is how they get | [migrate-custom-code](migrate-custom-code/SKILL.md) | Runs ATC readiness checks, groups findings by priority, and generates replacement code | Preparing custom code for S/4HANA migration or ABAP Cloud readiness | | [sap-object-documenter](sap-object-documenter/SKILL.md) | Batch-documents many custom objects at once — purpose, style (Classic/Modern/Mixed), dependencies — as Markdown | Onboarding packages, handoffs, seeding a repo wiki (vs. explain-abap-code which is single-object interactive) | +### SAP CAP Enterprise Audit (Preview) + +End-to-end audit and compliance verification for SAP CAP applications deployed on BTP (Cloud Foundry or Kyma) consuming S/4HANA Tier-2 services. Read-only skills that produce committable markdown reports. + +| Skill | What it does | When to use | +|---|---|---| +| [sap-cap-clean-core-enforce](sap-cap-clean-core-enforce/SKILL.md) | Discovery-driven Clean Core Level A audit. Scans `cds.connect.to()` runtime + `@cds.external` services, probes SAP API release-state repository via mcp-sap-docs, builds availability matrix (Public × Private × On-Premise), detects catalog drift, suggests SAP-released replacements | Pre-deployment audit / quarterly compliance check for CAP+S/4 stacks | +| [sap-cap-customizing-honor](sap-cap-customizing-honor/SKILL.md) | Bidirectional CSV↔code customizing audit: forward orphans (seeded but unused) + inverse orphans (code-read but unseeded) + hardcoded business-decision sweep + master-data FK ValueList enforcement | Verifying that admin Setup UI parameters are wired to code consumers; pre-release coverage check | + +> **Status**: Preview / Work-in-progress. Wave 1 (Clean Core + Customizing) available now. Additional skills (Security RBAC matrix, Lifecycle matrix audit, Fiori app audit, Text polish, Stack audit orchestrator, CI gates pattern) tracked in follow-up PRs. + ### Clean Core & Custom Code Retirement | Skill | What it does | When to use | diff --git a/skills/sap-cap-clean-core-enforce/SKILL.md b/skills/sap-cap-clean-core-enforce/SKILL.md new file mode 100644 index 00000000..0d1c9573 --- /dev/null +++ b/skills/sap-cap-clean-core-enforce/SKILL.md @@ -0,0 +1,386 @@ +--- +name: sap-cap-clean-core-enforce +description: Discovery-driven Clean Core Level A enforcement audit for SAP CAP + S/4HANA projects. Scans `cds.connect.to()` runtime calls + `@cds.external` services, identifies SAP-released probe objects, builds an availability matrix (Public Cloud × Private Cloud × On-Premise) via `mcp-sap-docs`, detects catalog drift versus the project's declared compatibility policy, and suggests SAP-released replacements for non-released references. Use when asked to "verify Clean Core Level A compliance", "audit S/4 API usage", "check which S/4 services we consume are released", "are we Clean Core compliant on BTP", or "build a Clean Core compliance matrix". +--- + +# SAP CAP Clean Core Level A Enforcement + +Discovery-driven Clean Core Level A compliance audit for SAP CAP + S/4HANA projects. This skill **scans your CAP codebase for S/4 API consumption**, **verifies each service against the SAP API release-state repository** ([`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc)) on all three editions (Public Cloud, Private Cloud / RISE, On-Premise), and produces a **compliance matrix** that can be checked into the repo as compliance evidence. + +Unlike [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) which classifies **ABAP custom code on the SAP side** into Levels A-D, this skill classifies **the BTP CAP application's outbound S/4 API consumption** — useful when the CAP app is the consumer and the question is "are all S/4 APIs we call actually released?". + +Read-only audit, idempotent, ~5 minute run, zero side-effects on either the source SAP system or the target CAP project (unless `--apply` mode is selected). + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Target edition | All 3: `public_cloud`, `private_cloud`, `on_premise` | Full matrix — multi-customer-deployable projects need to know per-edition availability | +| Clean Core level | `A` (Released APIs only) | The most restrictive level — works for any cloud target | +| Compatibility policy file | Auto-detect: `srv/integration/s4*Policy*.{ts,js,cds}` → `srv/config/s4*.{ts,js}` → `src/config/clean-core*.{ts,js}` | Project-specific files vary; auto-detect first, fall back to discovery-only | +| Probe identification | Catalog `probeObject` field if present → CDS view naming heuristic (`I_*API01` / `I_*`) → MCP search fallback | Multi-strategy gives best coverage | +| BTP managed services | Excluded from S/4 audit | `db`, `auth`, `messaging`, `connectivity`, `responsibility-management`, `enterprise-messaging`, `audit-log-service`, `nats`, `redis` are not S/4 | +| Report output | `docs/audit/<yyyy-mm-dd>-clean-core-level-a.md` | Markdown + committable for traceability | +| Mode | `report` (read-only) | Reversible by default; user opts into `apply` mode for catalog updates | +| Drift severity | Over-declared = HIGH, Under-declared = MEDIUM, Missing-field = LOW | HIGH if catalog claims availability that MCP denies (customer would fail in prod) | + +## Input + +Optional flags (all auto-detected by default): + +- **scope** — `all` (default) | `discovery-only` (skip MCP) | `matrix-only` (presume discovery cached) | `<SERVICE_NAME>` (focus single service) +- **policy file path** — explicit override for the compatibility policy file +- **target editions** — comma list to restrict, e.g., `public_cloud,private_cloud` +- **mode** — `report` (default) | `apply` (auto-update catalog `availability[]` field if MCP-verified differs from declared) + +If neither flag is provided, run with defaults and full discovery. + +## Step 1: Pre-flight + Compatibility Policy Discovery + +### 1a. Verify project type + +```bash +test -f package.json && grep -q '"@sap/cds"' package.json +``` + +If not a CAP project, stop and inform the user this skill targets CAP+S/4HANA stacks. + +### 1b. Detect compatibility policy file + +Search for the project's S/4 compatibility policy file (commonly used pattern: a JS/TS module exporting a `SERVICE_CATALOG` constant that maps logical service names to OData version + destination aliases + entity sets + Communication Scenarios). + +Auto-detect order: + +```bash +find srv/integration -name "s4*Policy*.{ts,js}" -maxdepth 2 2>/dev/null +find srv/config -name "*compat*.{ts,js}" -maxdepth 2 2>/dev/null +find src/config -name "clean-core*.{ts,js}" -maxdepth 2 2>/dev/null +``` + +If multiple candidates → ask user to pick one. If none → proceed with `discovery-only` scope (no catalog drift detection, only inventory + MCP verification). + +### 1c. Parse compatibility policy + +Extract from the policy file (regardless of exact location): + +- Service entries (keys of `SERVICE_CATALOG` or equivalent): logical names like `BUSINESS_PARTNER`, `SUPPLIER_INVOICE`, `PURCHASE_ORDER`, etc. +- Per-entry: `odata` (versions array), `destinations` (alias names), `entitySets`, `communicationScenarios`, optionally `availability` (already-declared edition list), optionally `probeObject` (already-declared SAP probe object). + +If the policy file does not exist or doesn't follow this convention, proceed with code-grep discovery only. + +## Step 2: Dynamic Discovery (4 sources) + +Combine 4 input sources to build the candidate service list. **Do not** start from a hardcoded list — discover dynamically. + +### 2a. Runtime destinations (`cds.connect.to`) + +```bash +grep -rhE "cds\.connect\.to\(['\"]([A-Z][A-Z0-9_-]+)['\"]" srv/ \ + --include="*.ts" --include="*.js" 2>/dev/null \ + | grep -oE "['\"][A-Z][A-Z0-9_-]+['\"]" \ + | tr -d "'\"" \ + | sort -u +``` + +### 2b. External service contracts + +```bash +ls srv/external/*.cds 2>/dev/null | xargs -I {} basename {} .cds | sort -u +``` + +These are typically `@cds.external` services or remote OData proxies with metadata committed to the project. + +### 2c. SERVICE_CATALOG declared destinations (if Step 1c succeeded) + +Extract from the parsed policy file. + +### 2d. ServiceConnectors seed CSV (if present) + +```bash +test -f db/data/sap.*-ServiceConnectors.csv && \ + awk -F',' 'NR>1 {print $2}' db/data/sap.*-ServiceConnectors.csv | sort -u +``` + +This is a common pattern for CAP apps that declare admin-configurable S/4 service connectors as master data. + +### 2e. Apply exclusions + +Remove BTP managed services (not S/4): + +``` +db | auth | messaging | connectivity | responsibility-management | +enterprise-messaging | audit-log-service | nats | redis +``` + +Also remove dev / mock / test destinations (matching `mock-*`, `test-*`, `dev-*`). + +The result is the **service candidate list** to audit. + +## Step 3: Probe Identification + +For each service candidate, identify the representative SAP object to query against the SAP API Release State repository. + +### Heuristic order (most preferred first) + +1. **Explicit `probeObject`** from policy file (if Step 1c found it) — zero cost, already documented. +2. **Naming convention** — SAP's pattern for OData service-backing CDS views: + - Destination `API_*` (e.g., `API_PURCHASEORDER_PROCESS_SRV`) → probe `I_*API01` (e.g., `I_PURCHASEORDERAPI01`) + - Destination `I_*` directly (e.g., `I_PURCHASEORDERHISTORYAPI01`) → probe is the destination itself +3. **Special cases** (known SAP framework patterns): + - Electronic Document Files / DRC services → probe `DTEL EDOC_TYPE` (component `CA-GTF-CSC-EDO`) + - Attachment service (`API_CV_ATTACHMENT_SRV`) → probe `CLAS CL_ATTACHMENT_SERVICE_API` (component `CA-DMS`) + - Workflow services (`s4-flexible-workflow`) → probe `DDLS I_WORKFLOWEXTERNALSTATUS` (component `BC-BMT-WFM`) +4. **MCP search fallback** for ambiguous cases: + ``` + mcp-sap-docs:sap_search_objects( + query="<keyword derived from logical name>", + system_type="public_cloud", + clean_core_level="A", + object_type="DDLS", + limit=10 + ) + ``` + Take the first result matching `I_<TOPIC>API01` or `I_<TOPIC>` pattern. + +### Untraceable services + +If no probe object can be identified for a service (none of the heuristics produce a match), emit a **LOW finding** in the report: + +``` +[LOW] Untraceable service: <SERVICE_NAME> + - Destination(s): <list> + - Inferred attempts: + - Naming heuristic I_*API01: no MCP match + - Search fallback: no result matching pattern + - Recommendation: manually add `probeObject` field to the catalog entry +``` + +The skill continues without crashing — untraceable services are flagged but not treated as compliance gaps (they may be released, just not auto-detectable). + +## Step 4: Matrix MCP-Verified (3 editions × N services) + +For each `(service, probe_object)` identified, issue **3 parallel MCP queries** — one per edition: + +``` +mcp-sap-docs:sap_get_object_details( + object_type=<probe.type>, + object_name=<probe.name>, + system_type="public_cloud", # then private_cloud, then on_premise + target_clean_core_level="A" +) +``` + +### Per-cell evaluation + +For each `(service, edition)` cell of the matrix: + +| MCP response | Interpretation | Matrix value | +|---|---|---| +| `found: true && state: "released" && cleanCoreLevel: "A" && complianceStatus: "compliant"` | Released L-A available | ✅ | +| `found: true && state: "released" && cleanCoreLevel: "B/C/D"` | Released but not L-A | 🟡 (level mismatch) | +| `found: true && state: "deprecated"` | Available but deprecated | ⚠️ (deprecated, migration needed) | +| `found: true && state: "notToBeReleased*"` | Not released, internal use | 🔴 (Clean Core violation) | +| `found: false` | Not in repository | ⚪ (unknown — probe may be wrong) | + +### Cache MCP results + +Cache by `(object_type, object_name, system_type)` — the same probe is often queried for multiple services that share a CDS view backbone (e.g., `I_BUSINESSPARTNER` underlies multiple API_* services). 30+ queries for a 12-service catalog is normal. + +## Step 5: Catalog Drift Detection + +For each catalog entry, compare declared `availability[]` against MCP-verified result. + +### Drift classification + +| Drift type | Declared vs Verified | Severity | Impact | +|---|---|---|---| +| **NONE** | Declared matches verified exactly | — | Healthy | +| **OVER_DECLARED** | Catalog says `['PUBLIC', 'PRIVATE', 'ONPREM']` but MCP confirms only 2 editions | 🔴 **HIGH** | Customer on Public Cloud configures connector → runtime fails with 404. False positive. | +| **UNDER_DECLARED** | Catalog says `['PRIVATE']` but MCP confirms 3 editions | 🟡 **MEDIUM** | Customer Public Cloud blocked unnecessarily. False negative. | +| **MISSING_FIELD** | Catalog has no `availability` field | 🟢 **LOW** | Undefined runtime behavior. Defaults may be too permissive. | +| **STATE_CHANGE** | Catalog implies released but MCP reports deprecated | ⚠️ **MEDIUM** | Migration needed (probably future cutoff). | + +## Step 6: Compliance Matrix Rollup + +Aggregate the per-cell matrix into multi-dimensional reports. + +### Rollup A: Per-service matrix + +``` +| Service | Public | Private | OnPrem | Probe | App Component | Drift | +|------------------|--------|---------|--------|-----------------------------|---------------------|----------------| +| BUSINESS_PARTNER | ✅ | ✅ | ✅ | DDLS I_BUSINESSPARTNER | AP-MD-BP | NONE | +| PURCHASE_ORDER | ✅ | ✅ | ✅ | DDLS I_PURCHASEORDERAPI01 | MM-PUR-PO-2CL | NONE | +| HYPOTHETICAL_X | ❌ | ✅ | ✅ | DDLS I_HYPOTHETICALX | (not found public) | OVER_DECLARED | +``` + +### Rollup B: Per Communication Scenario + +For each `SAP_COM_XXXX` in the catalog, list which services use it and their edition coverage: + +``` +| SAP_COM | Description | Services NOVA using | Edition coverage | +|--------------|-----------------------------------|---------------------------|----------------------| +| SAP_COM_0008 | Business Partner Integration | BUSINESS_PARTNER | All 3 ✅ | +| SAP_COM_0057 | Supplier Invoice + Attachments | SUPPLIER_INVOICE, ATTACHMENT | All 3 ✅ | +``` + +### Rollup C: Coverage summary + +``` +Total services audited: N +Cells (services × 3 editions): M +Cells compliant (L-A all 3): X (X/M %) +Cells with drift HIGH: n +Cells with drift MEDIUM: n +Cells with drift LOW: n +Untraceable services: n +Z-namespace runtime references: n + +Rating: A (≥95% compliant) / A- (90-94%) / B (75-89%) / C (<75%) +``` + +## Step 7: Emit Report + +Save markdown to `docs/audit/<yyyy-mm-dd>-clean-core-level-a.md` (create `docs/audit/` directory if needed): + +```markdown +# Clean Core Level A Compliance Audit — <yyyy-mm-dd> + +## Pre-flight +- Branch: <branch> +- Commit: <sha> +- Compatibility policy file: <path or 'not detected'> +- Scope: <discovery sources used> + +## Discovery +- Runtime destinations (cds.connect.to): N +- External service contracts: N +- SERVICE_CATALOG entries: N +- ServiceConnectors CSV: N +- Effective services (post-dedup + post-exclusion): N + +## Probe Identification +| Service | Probe | Type | App Component | Source | + +## Matrix (MCP-verified) +| Service | Public | Private | OnPrem | Notes | + +## Drift Detection +| Service | Declared | Verified | Drift | Severity | + +## Compliance per Communication Scenario +| SAP_COM | Description | Services | All editions | + +## Findings (≥0.8 confidence) +### [HIGH] OVER_DECLARED: <service> +- Catalog: <declared> +- MCP verified: <actual> +- Impact: <concrete failure scenario> +- Fix: <recommendation> + +## Compliance Summary +- Services: N · Cells compliant: X/M +- Rating: A / A- / B / C + +## Fix Plan / Applied +| Service | Field | Before | After | Status | + +## Re-verification process +- Cadence: quarterly (Q-review) or on SAP API sunset announcement +- Command: `sap-cap-clean-core-enforce` (this skill) +- Output: re-save report with current date +``` + +## Step 8: Apply Fixes (only when `--apply` mode) + +When user explicitly opts in: + +### Safe auto-fixes + +1. **Sync `availability[]` field** for services where MCP-verified is a **superset** of declared (UNDER_DECLARED case) — adding editions never breaks customers; only removing does. +2. **Add `probeObject` field** when missing but heuristic-identified — improves traceability for re-verification. +3. **Add timestamp comment** at the top of the catalog file recording verification date. + +### NOT auto-applied (require manual review) + +- **Remove edition from `availability[]`** when OVER_DECLARED — risks breaking customers on the over-declared edition. Surface as a finding with recommendation, but never auto-remove. +- **Add new service entry** not currently in catalog. Use `--add-service <name>` explicit flag. +- **Z-namespace usage** at runtime — architectural decision, not auto-fixable. + +### Verification after fix + +Re-run Steps 4-6 against the updated catalog to confirm zero drift remains. + +## BTP vs On-Premise Differences + +| Aspect | BTP target (Public Cloud) | Private Cloud / RISE | On-Premise | +|---|---|---|---| +| Software component | `SAPSCORE` | `S4CORE` | `S4CORE` or `SAP_BASIS` | +| App component suffix | `-2CL` ("To Cloud") | base name (no suffix) | base name (no suffix) | +| Released API set | Most restrictive | Includes legacy classic APIs | Same as Private | +| ABAP CDS views | `I_*` views released L-A | `I_*` views (no -2CL suffix) | Same as Private | +| Cross-edition probes | Use Public Cloud `-2CL` as authoritative | Both base + -2CL may exist | base name preferred | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| `mcp-sap-docs not connected` | MCP server unavailable | Run installation: `npx skills add marianfoo/arc-1` includes recommended MCP setup | +| `mcp-sap-docs returns found: false` for known SAP object | Repository dataset lag or wrong probe object | Try alternative probe (variant CDS view name); flag service as untraceable | +| Policy file detected but parse fails | Non-standard policy file format | Skill falls back to `discovery-only` scope and logs warning | +| Multiple policy files candidates found | Project structure ambiguity | Ask user to pick one (interactive prompt) | +| Probe object naming heuristic miss | Custom destination name doesn't match `API_*` / `I_*` pattern | Manual probe specification via policy file `probeObject` field | +| No `cds.connect.to` calls found | Project uses different connection pattern (e.g., `cds.requires.<dest>`) | Extend Step 2a grep with project-specific pattern; report what was found | +| Catalog file in TypeScript with complex syntax | Cannot statically parse with regex | Read entries via `node -e` evaluation or ask user to export JSON snapshot | + +## What This Skill Does NOT Do + +- **No SAP write operations** — read-only on SAP side via mcp-sap-docs queries +- **No replacement code generation** — suggests replacement APIs (in drift report), but does not generate the replacement CAP service binding +- **No ABAP-side classification** — that's [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) (which audits Z code on SAP) +- **No SystemParameter / customizing validation** — separate concern (see [sap-cap-customizing-honor](../sap-cap-customizing-honor/SKILL.md)) +- **No deployment / runtime testing** — static audit only; doesn't make HTTP calls to the destination +- **No CDS compile validation** — assumes the project already compiles; this skill audits the S/4 API surface, not CDS syntax +- **No Z-namespace runtime replacement** — flags Z-namespace usage as finding but architectural redesign is out of scope +- **No catalog file generation** — works against an existing compatibility policy or with `discovery-only` if none exists + +## When to Use This Skill + +- **Pre-deployment audit** — before committing to a BTP customer go-live, verify that all S/4 APIs consumed are released for that customer's edition +- **Quarterly compliance check** — schedule as part of release governance; re-verifies the catalog matches latest SAP API release state +- **After adding a new S/4 destination** — verify the new service is released before merge +- **Pre-CI gate validation** — pair with a CI script (e.g., `scripts/ci/check-s4-compat-coverage.sh`) for runtime drift prevention +- **Pre-acquisition audit** — when assessing a 3rd-party CAP application, verify it's Clean Core L-A before adoption +- **Documentation evidence** — auditor-friendly report committed to `docs/audit/` for compliance trail + +## When NOT to Use This Skill + +- **Greenfield project with no SERVICE_CATALOG yet** — there's nothing to audit yet; start with [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) to scaffold the project, then audit later +- **ABAP-only codebase** (no CAP) — use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) instead +- **Custom Z* CDS views on the S/4 side that are NOT meant to be released** — that's a design choice; this skill measures consumption, not source generation +- **Single-edition deployment fixed by customer contract** — you can use this skill but most of the value (cross-edition matrix) is lost; consider `--target-editions public_cloud` flag + +## Follow-up + +After this skill produces the compliance matrix: + +- **HIGH drift findings**: investigate manually, decide whether to remove edition from declared availability OR switch to a different SAP service that IS released on the affected edition +- **UNDER_DECLARED findings**: run with `--apply` to expand `availability[]` and unlock customer editions +- **Untraceable services**: manually add `probeObject` field to policy entries +- **Deprecated state warnings**: plan migration to successor APIs (use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) for ABAP-side migrations) +- **Z-namespace runtime references**: architectural review — either Clean Core B opt-in (documented exception) or replacement + +Related skills: + +- [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) — ABAP-side classification (companion, source side) +- [migrate-custom-code](../migrate-custom-code/SKILL.md) — ABAP-side ATC fixes when replacements are needed +- [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) — generate the CAP scaffold this skill later audits + +## References + +- [SAP API Release State repository](https://github.com/SAP/abap-atc-cr-cv-s4hc) — authoritative source for object classifications +- [Clean Core principles](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) +- [SAP API Business Hub](https://api.sap.com) +- [CAP `cds.connect.to` documentation](https://cap.cloud.sap/docs/node.js/cds-connect) +- [SAP Communication Management (S/4HANA Cloud)](https://help.sap.com/docs/SAP_S4HANA_CLOUD/0f69f8fb28ac4bf48d2b57b9637e81fa/community-management.html) diff --git a/skills/sap-cap-customizing-honor/SKILL.md b/skills/sap-cap-customizing-honor/SKILL.md new file mode 100644 index 00000000..6380c6bb --- /dev/null +++ b/skills/sap-cap-customizing-honor/SKILL.md @@ -0,0 +1,494 @@ +--- +name: sap-cap-customizing-honor +description: Bidirectional CSV ↔ code customizing audit for SAP CAP applications that use a SystemParameter / settings-table pattern. Detects forward orphans (CSV seeded but never consumed in code), inverse orphans (code reads parameter but no CSV seed), hardcoded business decisions that bypass the customizing mechanism (thresholds, timeouts, retry policies, severity mappings), and master-data foreign-key fields missing `@Common.ValueList` annotation. Use when asked to "audit customizing coverage", "verify SystemParameter consumption is 100%", "find hardcoded business decisions", "check that customizing is honored end-to-end", or "is admin config actually being read by the code?". +--- + +# SAP CAP Customizing Honor Audit + +Exhaustive audit that **enforces customizing is honored at 100%** in a SAP CAP application — both forward (every SystemParameter seed in CSV has a code consumer) and inverse (every `params.X` read in code has a CSV seed). Additionally, sweeps for **hardcoded business decisions** that bypass the customizing mechanism, and verifies that every **foreign-key field to master data** is bound to `@Common.ValueList` on filter bars and edit forms — no free-text where customizing exists. + +Read-only audit, idempotent, ~3 minute run, zero side-effects (unless `--apply` mode auto-fixes safe cases). + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Scope | `all` — entire `srv/` + `app/` codebase | Default to full sweep; user narrows if needed | +| SystemParameter source | `db/data/*-SystemParameters.csv` (CAP convention) | CAP CSV seed pattern is conventional | +| Code consumer pattern | `getSystemParamReader().get(...)` + `params.X` / `cfg.X` / `paramReader.X` | Common CAP customizing reader patterns | +| Inverse orphan severity | ERROR (admin sees param in Setup UI but code ignores it) | Customer-facing problem | +| Forward orphan severity | WARNING | Internal cleanliness, less urgent | +| Hardcoded business decision severity | HIGH for thresholds in lifecycle handlers, MEDIUM otherwise | Production impact-aware | +| Master-data unreferenced fields severity | P1 (filter bar) / P2 (edit form) / P3 (display only) | UX-first priority | +| Report output | `docs/audit/<yyyy-mm-dd>-customizing-honor.md` | Committed for traceability | +| Mode | `report` (read-only) | Safe by default | +| Auto-fix scope | Add missing seed CSV rows for inverse orphans + add `@Common.ValueList` annotations for master-data filter bars | Reversible, contained | + +## Input + +Optional flags: + +- **scope** — `all` (default) | `<app-name>` (focus single Fiori app) | `srv-only` | `app-only` | `<subfolder>` (e.g., `srv/handlers`) +- **mode** — `report` (default) | `fix` (auto-apply safe fixes on a dedicated branch) +- **csv pattern** — override default `db/data/*-SystemParameters.csv` glob +- **reader pattern** — override regex for code-side consumer detection +- **skip checks** — comma list of check categories to skip (e.g., `--skip hardcoded,master-data`) + +## Step 1: Pre-flight + Project Detection + +### 1a. Verify project type + +```bash +test -f package.json && grep -q '"@sap/cds"' package.json && test -d srv +``` + +If not a CAP project, stop and inform user this skill targets CAP apps. + +### 1b. Detect SystemParameter CSV files + +```bash +find db/data -name "*-SystemParameter*.csv" -maxdepth 2 2>/dev/null +``` + +Expected pattern: `db/data/<namespace>-SystemParameters.csv` with columns `ParamKey,CompanyCode,ParamValue,Description,Category,IsSecret`. + +If multiple files found → process all; if none → warn user and proceed with inverse-orphan check only. + +### 1c. Detect code-side consumer pattern + +```bash +grep -rohE "(getSystemParamReader|paramReader|params\.|cfg\.)[A-Z][A-Z0-9_]+\(" srv/ \ + --include="*.ts" --include="*.js" 2>/dev/null | head -10 +``` + +If matches are found, the reader pattern is confirmed. Otherwise the project may not use the customizing pattern at all; ask user to confirm. + +### 1d. Resolve scope + +| scope value | Folder paths to audit | +|---|---| +| `all` | `srv/` + `app/` | +| `<app-name>` | `app/<app-name>/` only | +| `srv-only` | `srv/` only | +| `app-only` | `app/` only | +| `<subfolder>` | exact path | + +### 1e. Branch creation (if mode=fix) + +```bash +git checkout -b codex/customizing-honor-<scope>-$(date +%Y-%m-%d) +``` + +## Step 2: Bidirectional CSV ↔ Code Check (Forward + Inverse Orphans) + +### 2a. Extract CSV-seeded parameter keys + +```bash +awk -F',' 'NR>1 {print $1}' db/data/*-SystemParameters.csv 2>/dev/null \ + | sort -u > /tmp/csv-keys.txt +``` + +Result: deduplicated `ParamKey` list (header `ParamKey,...`). + +### 2b. Extract code-consumed parameter keys + +```bash +grep -rohE "(p|params|cfg|wfParams|matchingParams|techParams|aiParams|monitoringParams|envCfg|dbCfg|slaParams|apprParams)(\?\.\|\[['\"]|\.)([A-Z][A-Z0-9_]+)" "$SCOPE" \ + --include="*.ts" --include="*.js" 2>/dev/null \ + | grep -oE "[A-Z][A-Z0-9_]+\b" \ + | sort -u > /tmp/code-keys.txt +``` + +This greps common reader variable names (`params.X`, `cfg.X`, `wfParams.X`, etc.) and extracts the parameter key. + +### 2c. Compute orphans + +```bash +# Inverse orphan: code reads but CSV doesn't seed +comm -23 /tmp/code-keys.txt /tmp/csv-keys.txt > /tmp/inverse-orphans.txt + +# Forward orphan: CSV seeds but code never reads +comm -13 /tmp/code-keys.txt /tmp/csv-keys.txt > /tmp/forward-orphans.txt +``` + +### 2d. Classify orphans + +For each inverse orphan, find the source file + line: + +```bash +while IFS= read -r key; do + grep -rn "\.${key}\b\|\['${key}'\]\|\[\"${key}\"\]" srv/ app/ \ + --include="*.ts" --include="*.js" 2>/dev/null | head -3 +done < /tmp/inverse-orphans.txt +``` + +For each forward orphan, find the CSV line: + +```bash +while IFS= read -r key; do + grep -n "^${key}," db/data/*-SystemParameters.csv 2>/dev/null +done < /tmp/forward-orphans.txt +``` + +### 2e. Compile per-category counts + +Group orphans by `Category` (from CSV column 5): + +``` +SystemParameter category Forward Inverse +INTEGRATION 3 7 +TECHNICAL 1 12 +APPROVAL 0 4 +... +``` + +## Step 3: Hardcoded Business Decisions Sweep + +For each file in scope (`*.ts`, `*.js`), search for patterns that represent **business decisions** but are NOT routed through `getSystemParamReader()`. + +### 3a. Numeric thresholds + +```bash +grep -rnE ">=\s*[0-9]+|>\s*[0-9]+|<=\s*[0-9]+|<\s*[0-9]+|=\s*[0-9]{2,}\b" "$SCOPE" \ + --include="*.ts" --include="*.js" 2>/dev/null \ + | grep -vE "//|^.*\*|\.test\.|HTTP|status:|\.length|substring|setTimeout|process\.env|throw|>=\s*0\b|>\s*0\b|<=\s*100\b" \ + | head -50 +``` + +For each match, classify: + +- **TRUE positive (business threshold)** — e.g., `if (riskScore >= 80) markHighRisk()` — should be `params.HIGH_RISK_THRESHOLD` +- **FALSE positive** — HTTP status codes (`200`, `400`, `500`), array index, `.length`, `setTimeout` ms — ignore + +### 3b. Hardcoded fragment XML thresholds (Fiori app) + +```bash +grep -rnE ">=\s*[0-9]+|>\s*[0-9]+|<\s*[0-9]+" "$SCOPE" \ + --include="*.xml" 2>/dev/null \ + | grep -vE "//|^.*\*|ErrorCount|currentStep|>= ?\$\{|> ?\$\{|>= 0\b|> 0\b|>= 1\b" \ + | head -30 +``` + +### 3c. `cds.env` reads not dual-source + +```bash +grep -rn "cds\.env\." "$SCOPE" --include="*.ts" 2>/dev/null \ + | grep -vE "//|^.*\*|cds\.env\.(profile|profiles|requires|features|build|sql|hana|odata|telemetry|i18n|log|app|server|effective)" \ + | grep -v "_resolveArchiverConfig\|fallback\|legacy" \ + | head -20 +``` + +The **dual-source pattern** is: SystemParameter first, fallback to `cds.env`, fallback to `process.env`, fallback to hardcoded. Direct `cds.env.X` reads without going through SystemParamReader are findings (admin can't override). + +### 3d. UX timing / delay hardcoded + +```bash +grep -rnE "setTimeout.*[0-9]{3,}|DELAY|INTERVAL_MS|TTL\s*=\s*[0-9]" "$SCOPE" \ + --include="*.ts" 2>/dev/null \ + | grep -vE "//|^.*\*|\.test\." | head -10 +``` + +### 3e. i18n hardcoded inline (user-facing strings) + +```bash +grep -rnE "MessageBox\.|MessageToast\.show\(|req\.notify\(" "$SCOPE" \ + --include="*.ts" 2>/dev/null \ + | grep -vE "_t\(|i18n>|getText\(|bundle\.|//|^.*\*" \ + | head -10 +``` + +User-visible text should be i18n keys, not inline strings. Hardcoded inline strings are findings (no localization). + +### 3f. Per-finding triage + +For each match, decide: + +- **TRUE positive** → business decision tunable → add to findings list with file:line + suggested fix +- **FALSE positive** → security boundary / HTTP code / internal cache TTL / `.length` → ignore + +## Step 4: Catalog Raise Coverage (if applicable) + +For CAP apps with a `ProcessStepCheck` catalog (a common pattern for workflow-driven apps): + +```bash +test -f db/data/*-ProcessStepCheck.csv && \ + bash scripts/ci/check-catalog-raise-coverage.sh 2>/dev/null +``` + +This verifies every CSV-seeded check code has a runtime `raiseCatalogException(...)` call in the codebase. + +If the CI script is not present, run an equivalent check inline: + +```bash +# Extract active check codes +awk -F',' 'NR>1 && $X=="true" {print $Y}' db/data/*-ProcessStepCheck.csv | sort -u + +# Find raise sites +grep -rhE "raiseCatalogException\(.*['\"]([A-Z][A-Z0-9_]+)['\"]" srv/ \ + --include="*.ts" | grep -oE "['\"][A-Z][A-Z0-9_]+['\"]" | tr -d "'\"" | sort -u + +# Diff +comm -23 <(active_codes) <(raise_sites) +``` + +Active checks without a raise site = code-coverage gap. + +## Step 5: Adapter Factory Dual-Source Verification + +For CAP apps using adapter factory patterns (`srv/{notifications,messaging,monitoring,...}/`): + +```bash +find srv -path "*/Factory*.ts" -o -name "*AdapterFactory*.ts" 2>/dev/null +``` + +For each factory, verify the canonical pattern: + +```typescript +// Expected pattern (4-layer fallback): +const adapterName = + params['<KEY>_ADAPTER'] || // 1. SystemParameter (admin-configurable) + env.adapter || // 2. cds.env fallback + process.env.ADAPTER_OVERRIDE || // 3. env var fallback + 'default-adapter'; // 4. hardcoded last resort +``` + +If a factory reads only from `cds.env` or `process.env` without going through SystemParameter, that's a finding — "adapter not customizing-driven". + +## Step 6: Master-Data Reference Audit (Filter + Edit Form) + +For CAP+Fiori projects, verify that every field referencing master data has `@Common.ValueList` annotation. + +### 6a. Master data catalog (target entities) + +Common SAP master-data CodeList patterns in CAP: + +| Pattern | Master data target | +|---|---| +| `*CompanyCode*` | `CompanyCodeMappings` | +| `*BusinessPartner*` | `BusinessPartner` / `Suppliers` | +| `*Currency*` | `Currencies` (CodeList) | +| `*PaymentMethod*`, `*PaymentBlock*` | `PaymentMethods` / `PaymentBlockCodes` | +| `*Status*` | Status CodeList (domain-specific) | +| `step_ID`, `check_ID` (process workflow) | `ProcessSteps` / `ProcessStepChecks` | +| `*Severity*` | `Severities` CodeList | +| `*GLAccount*`, `*CostCenter*`, `*WBSElement*` | cost-object master data | + +### 6b. Filter bar (SelectionFields) check + +For each `UI.SelectionFields: [...]` in `app/annotations/*.cds`: + +```bash +grep -rnE "UI\.SelectionFields:\s*\[" app/annotations/ app/*.cds 2>/dev/null +``` + +For each field listed, verify a `@Common.ValueList` annotation exists, either: + +1. **Direct annotation** on the field +2. **Indirect via Association** — e.g., `step` (Association) has the ValueList, and `step_ID` (foreign-key column) is the actual SelectionField + +Pattern verification: + +```bash +for FIELD in CompanyCode SupplierBP Currency PaymentMethod ...; do + # Pattern 1: direct annotation on field + DIRECT=$(grep -rnE "$FIELD\s*@Common\.ValueList" app/annotations/ srv/ 2>/dev/null | wc -l) + # Pattern 2: annotation on Association (FK column ends with _ID) + ASSOC=$(echo "$FIELD" | sed 's/_ID$//') + if [ "$ASSOC" != "$FIELD" ]; then + INDIRECT=$(grep -rnE "$ASSOC\s+@Common\.ValueList" app/annotations/ srv/ 2>/dev/null \ + | grep -E "LocalDataProperty:\s*$FIELD\b" | wc -l) + else + INDIRECT=0 + fi + TOTAL=$((DIRECT + INDIRECT)) + if [ $TOTAL -eq 0 ]; then + echo "❌ $FIELD: missing ValueList in filter bar" + fi +done +``` + +### 6c. Edit form (FieldGroup / LineItem) check + +For each `@odata.draft.enabled` entity, check editable fields against master-data patterns. Edit-form ValueList is **strongly recommended** but can require context-dependent filtering (e.g., `step` filter on active template) — flag for manual review when filter cardinality is non-trivial. + +### 6d. TextArrangement check + +For each FK field with `ValueList`, verify the display-name cascade is configured: + +```cds +@Common.Text: <X>.Name +@Common.TextArrangement: #TextOnly +``` + +Without TextArrangement, the UI displays the raw ID — bad UX. + +### 6e. False-positive whitelist + +These fields are legitimately free-text (NOT findings): + +- Descriptive: `Description`, `Name`, `Notes`, `Comment`, `Justification`, `RejectReason` +- Natural external identifiers: `eDocumentGuid`, `InvoiceNumber`, `IBAN`, `TaxCode`, `VATIdNumber` (input via parsing, not catalog-pick) +- Amounts / quantities: `Amount`, `Percentage`, `Quantity`, `Price`, `Score`, `Confidence` +- Timestamps: `*At`, `*Date`, `*Timestamp` +- Technical: `ID`, `UUID`, `*_ID` (internal keys handled by framework) +- File-system: `FileName`, `FilePath`, `Url` + +## Step 7: Output Report + +Save markdown to `docs/audit/<yyyy-mm-dd>-customizing-honor.md`: + +```markdown +# Customizing Honor Audit — <scope> — <yyyy-mm-dd> + +## Summary +- Forward orphans: N (CSV seeded, code doesn't read) +- Inverse orphans: N (code reads, CSV doesn't seed) +- Hardcoded business decisions: N +- Master-data unreferenced fields: filter=N1, edit=N2, missing TextArrangement=N3 +- Catalog coverage: ✅ PASS / ❌ N CheckCode without raise site +- Adapter dual-source: ✅ M/N OK / ❌ N missing + +## Inverse Orphans +| ParamKey | File:line | Default used | Suggested category | + +## Forward Orphans +| ParamKey | CSV line | Verification grep | Action (remove seed or wire consumer) | + +## Hardcoded Business Decisions +| File:line | Pattern | Type | Severity | Suggested fix | + +## Master Data Unreferenced +| Entity.Field | Position (filter/edit/header) | File:line | Master data target | Severity | Suggested fix | + +## Fix Plan +1. [P1] hardcoded threshold — suggested diff: +```diff +- const HIGH_RISK_THRESHOLD = 80; ++ async function _resolveHighRiskThreshold(cc) { ++ const params = await getSystemParamReader().get('RISK', cc); ++ const n = parseInt(params.HIGH_RISK_THRESHOLD, 10); ++ return Number.isFinite(n) && n > 0 ? n : 80; ++ } +``` + +2. [P1] master-data unreferenced (filter bar) — suggested diff: +```diff ++ annotate service.MyEntity with { ++ companyCode @Common.ValueListWithFixedValues: true ++ @Common.ValueList: { ... }; ++ companyCode @Common.Text: companyCode.CompanyName @Common.TextArrangement: #TextOnly; ++ }; +``` + +## Verifications +- bash scripts/ci/check-systemparams-bidirectional.sh: PASS / FAIL +- bash scripts/ci/check-catalog-raise-coverage.sh: PASS / FAIL +- npm run lint:csv: PASS / FAIL +- npx cds compile srv app: PASS / FAIL + +## Residual Risk +- <list of items requiring manual review> +``` + +## Step 8: Apply Fixes (only when `--apply` / `mode=fix`) + +### Safe auto-applicable fixes + +1. **Add missing CSV seed rows** for inverse orphans: + ```csv + <PARAM_KEY>,,<detected_default>,<placeholder description>,<inferred category>,false + ``` + One commit per seed: `chore(seed): add <PARAM_KEY> for inverse-orphan fix`. + +2. **Add `@Common.ValueList` annotation** on filter bar fields for master-data references (SAFE — non-breaking, additive): + ```cds + annotate service.<Entity> with { + <field> @Common.ValueListWithFixedValues: true + @Common.ValueList: { + CollectionPath: '<TargetEntity>', + Parameters: [ + { $Type: 'Common.ValueListParameterInOut', LocalDataProperty: <field>, ValueListProperty: '<key>' }, + { $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: '<displayName>' } + ] + }; + <field> @Common.Text: <field>.<displayName> @Common.TextArrangement: #TextOnly; + }; + ``` + One commit per annotation: `feat(masterdata): add ValueList on <Entity>.<field>`. + +### NOT auto-applied (manual decision required) + +- **Removing forward-orphan CSV seeds** — could break customer environments that currently rely on the param (even if unused in code), and a future PR may add a consumer. Surface as finding only. +- **Refactoring hardcoded business decisions** — code refactor involves logic changes; risk too high for auto-apply. Surface as finding with suggested diff. +- **Adding ValueList on edit-form fields** — can require filter context (cascading dropdowns); test manually before applying. + +### Post-fix verification + +Re-run Step 2 + Step 6 and confirm orphan/unreferenced counts decrease as expected. + +## BTP vs On-Premise Differences + +This skill is target-edition-neutral — the same audit applies for CAP apps deployed to BTP Cloud Foundry, Kyma, or on-premise. The only consideration: + +| Aspect | BTP | On-Premise | +|---|---|---| +| SystemParameter storage | HANA Cloud / HDI | HANA or PostgreSQL | +| CSV seed loading | `cds deploy` at boot | Same | +| Per-tenant customizing | Single-tenant per service (multi-customer = multi-deployment) | Same | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| No `db/data/*-SystemParameters.csv` found | Project doesn't use the SystemParameter pattern | Skip Steps 2 and 5; run hardcoded sweep + master-data check only | +| `getSystemParamReader` not found in codebase | Project uses different reader pattern | Ask user for the reader function name + adjust grep pattern | +| Grep returns thousands of "matches" in hardcoded check | Permissive regex catches FP | Tighten patterns in Step 3a/3b; user can override with `--strict-thresholds` flag | +| Annotations folder doesn't exist | Project uses inline annotations or non-standard structure | Skip Step 6 or ask user to point to annotation locations | +| Multiple SystemParameter CSV files | Multi-namespace project | Audit all CSVs in scope; deduplicate keys | +| `forward orphan` is in user-known allowlist (e.g., NATS_URL only used in NATS adapter) | False positive | User can extend `FORWARD_ALLOWLIST` in the CSV's frontmatter comment or `--allowlist` flag | + +## What This Skill Does NOT Do + +- **No refactoring** — hardcoded business decisions are flagged with suggested fix, not auto-refactored +- **No CSV seed removal** — never deletes seeded parameters (breaking risk for customer envs) +- **No CDS schema validation** — assumes the schema compiles; audits the customizing coverage, not entity structure +- **No semantic verification** — doesn't check if the param value chosen by admin is a sensible default +- **No fallback chain testing** — verifies dual-source pattern is present, doesn't test runtime fallback behavior +- **No i18n translation generation** — flags hardcoded user-facing strings; user translates manually +- **No master-data CodeList generation** — references existing master data; doesn't create new CodeList entities + +## When to Use This Skill + +- **Pre-release audit** — verify admin Setup UI parameters are all wired to code consumers +- **Quarterly compliance check** — customizing drift over time (params added/removed) +- **Onboarding new developer** — understand which parameters drive runtime behavior +- **Customer escalation** — "admin changed param X but nothing happened" → run this skill to verify code reads it +- **Pre-acquisition audit** — when assessing a 3rd-party CAP app, verify customizing surface area is honest +- **Before refactor / cleanup** — identify which "dead" parameters can safely be removed (forward orphans) + +## When NOT to Use This Skill + +- **Project doesn't use SystemParameter pattern** — skill loses its core value; only hardcoded sweep applies +- **Pure greenfield project** — too early; come back after first iteration when params accumulate +- **Single-tenant fixed-config deployment** — customizing is less relevant; UX-level master-data check is still useful + +## Follow-up + +After this skill produces the audit report: + +- **Inverse orphans**: with `--apply` mode, the skill auto-adds CSV rows. Manually verify the inferred default + category, then commit. +- **Forward orphans**: manually decide for each: (a) remove from CSV (truly unused) or (b) wire consumer (currently TODO). +- **Hardcoded business decisions**: refactor each to `getSystemParamReader()` pattern, one parameter per commit, tested individually. +- **Master-data unreferenced fields**: with `--apply` mode, the skill auto-adds filter-bar ValueList. Manually verify edit-form bindings (context-dependent filters). + +Related skills: + +- [sap-cap-clean-core-enforce](../sap-cap-clean-core-enforce/SKILL.md) — verifies the S/4 API destinations consumed are Clean Core L-A (complementary audit) +- [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) — ABAP-side compliance (companion, source-side) + +## References + +- [CAP `cds.env` profile-aware configuration](https://cap.cloud.sap/docs/node.js/cds-env) +- [CAP CSV seed pattern](https://cap.cloud.sap/docs/guides/databases#providing-initial-data) +- [CAP `@Common.ValueList` annotation](https://cap.cloud.sap/docs/advanced/odata#valuehelp-annotations) +- [Fiori Elements V4 value-help integration](https://experience.sap.com/fiori-design-web/value-helper/) From 4c0d79519e7bd88264e55ea2f3a06f66f18e12b4 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 14:55:53 +0200 Subject: [PATCH 04/18] feat(skills): add SAP CAP Enterprise Audit Waves 2-3 (5 skills) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the operational complement to PR #279 (Waves 1: clean-core + customizing): - sap-cap-security-rbac-matrix — multi-area parallel security scan + role coherence matrix across 4 layers (xs-security <-> IdP realm <-> services-auth <-> handlers) + OWASP / ASVS / NIST CSF / CIS / SAP-SOM / GDPR / SOX mapping. - sap-fiori-app-audit — single Fiori Elements V4 app audit: user journey, frontend/backend contract chain, manifest/annotations/EDMX alignment, computed flag matrix, i18n coverage, action availability, draft behaviour. Safe quick-win fixes on a dedicated branch. - sap-cap-text-polish — user-visible text audit (backend reject/throw, helper rejects, toasts/dialogs, i18n bundles, CDS labels, CodeList descr). Detects ten anti-patterns including PII leak. Locale-aware, tone-profile- driven, additive safe rewrites only. - sap-cap-stack-audit-full — orchestrator that runs UI5 linter, manifest validation, CDS compile, TS typecheck, hardcoded-customizing sweep, test suite + the specialized audit skills above in parallel, with deduplicated consolidated report. - sap-cap-ci-gates-pattern — library of five reusable CI gate patterns (bidirectional CSV<->code, catalog raise-coverage, API-availability drift, convention-matrix drift, CSV schema lint). Generates portable shell scripts + GitHub Actions / GitLab CI / Jenkins workflow YAML. README updated with the seven-skill toolkit overview and a typical workflow chain entry point. All skills are read-only by default; fix modes are limited to safe additive corrections on dedicated branches. None modify production code or open PRs without explicit user opt-in. Discovery-driven (no hardcoded entity names); each skill explains the conditions under which it applies and degrades gracefully when sources are absent. Cross-references: - depends on / complements PR #279 (Waves 1: clean-core-enforce + customizing-honor) - Original Wave 2 plan included a lifecycle-matrix-audit skill, dropped after review: too domain-specific to a particular process-engine pattern (template + step + check + override-policy catalog) to generalize cleanly. [skip ci] --- skills/README.md | 36 ++ skills/sap-cap-ci-gates-pattern/SKILL.md | 409 +++++++++++++++++ skills/sap-cap-security-rbac-matrix/SKILL.md | 441 +++++++++++++++++++ skills/sap-cap-stack-audit-full/SKILL.md | 371 ++++++++++++++++ skills/sap-cap-text-polish/SKILL.md | 383 ++++++++++++++++ skills/sap-fiori-app-audit/SKILL.md | 359 +++++++++++++++ 6 files changed, 1999 insertions(+) create mode 100644 skills/sap-cap-ci-gates-pattern/SKILL.md create mode 100644 skills/sap-cap-security-rbac-matrix/SKILL.md create mode 100644 skills/sap-cap-stack-audit-full/SKILL.md create mode 100644 skills/sap-cap-text-polish/SKILL.md create mode 100644 skills/sap-fiori-app-audit/SKILL.md diff --git a/skills/README.md b/skills/README.md index 689acb01..1cdf24c3 100644 --- a/skills/README.md +++ b/skills/README.md @@ -100,6 +100,22 @@ Both skills produce the same RAP artifact stack. The difference is how they get | [migrate-custom-code](migrate-custom-code/SKILL.md) | Runs ATC readiness checks, groups findings by priority, and generates replacement code | Preparing custom code for S/4HANA migration or ABAP Cloud readiness | | [sap-object-documenter](sap-object-documenter/SKILL.md) | Batch-documents many custom objects at once — purpose, style (Classic/Modern/Mixed), dependencies — as Markdown | Onboarding packages, handoffs, seeding a repo wiki (vs. explain-abap-code which is single-object interactive) | +### SAP CAP Enterprise Audit (Preview) + +End-to-end audit and compliance verification for SAP CAP applications deployed on BTP (Cloud Foundry or Kyma) consuming S/4HANA Tier-2 services. Read-only skills that produce committable markdown reports; `fix` modes are limited to safe additive corrections on dedicated branches. + +| Skill | What it does | When to use | +|---|---|---| +| [sap-cap-clean-core-enforce](sap-cap-clean-core-enforce/SKILL.md) | Discovery-driven Clean Core Level A audit. Scans `cds.connect.to()` runtime + `@cds.external` services, probes SAP API release-state repository via mcp-sap-docs, builds availability matrix (Public × Private × On-Premise), detects catalog drift, suggests SAP-released replacements | Pre-deployment audit / quarterly compliance check for CAP+S/4 stacks | +| [sap-cap-customizing-honor](sap-cap-customizing-honor/SKILL.md) | Bidirectional CSV↔code customizing audit: forward orphans (seeded but unused) + inverse orphans (code-read but unseeded) + hardcoded business-decision sweep + master-data FK ValueList enforcement | Verifying that admin Setup UI parameters are wired to code consumers; pre-release coverage check | +| [sap-cap-security-rbac-matrix](sap-cap-security-rbac-matrix/SKILL.md) | Multi-area parallel security scan (handlers, MCP, file-upload, deploy, jobs) + OWASP Top 10 orthogonal pass + role coherence matrix across 4 layers (xs-security ↔ IdP realm ↔ services-auth ↔ handlers) + compliance mapping (OWASP / ASVS / NIST CSF / CIS / SAP-SOM / GDPR / SOX) | Pre-release security audit; quarterly compliance verification; auditor evidence pack | +| [sap-fiori-app-audit](sap-fiori-app-audit/SKILL.md) | Single Fiori Elements V4 app audit — user journey, frontend/backend contract chain, manifest + annotations + EDMX alignment, computed flag matrix, i18n coverage, action availability, draft behaviour. Optional safe quick-win fixes on a dedicated branch | Before merging a Fiori app PR; after UI5 version bump; quarterly regression check | +| [sap-cap-text-polish](sap-cap-text-polish/SKILL.md) | Audit and rewrite user-visible text (backend reject/throw, helper rejects, frontend toasts/dialogs, i18n bundles, CDS labels, CodeList descriptions). Detects ten anti-patterns including PII leak. Locale-aware, tone-profile-driven, additive safe rewrites only | Pre-release polish; PII safety net for audit logging; after localization phase | +| [sap-cap-stack-audit-full](sap-cap-stack-audit-full/SKILL.md) | Orchestrator that runs the full audit stack in parallel — UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded-customizing sweep, test suite + the specialized audit skills above — and consolidates findings into a single deduplicated report | Pre-release situational awareness; project hand-off baseline; after large refactors | +| [sap-cap-ci-gates-pattern](sap-cap-ci-gates-pattern/SKILL.md) | Library of five reusable CI gate patterns (bidirectional CSV↔code, catalog raise-coverage, API-availability drift, convention-matrix drift, CSV schema lint). Generates portable shell scripts + GitHub Actions / GitLab / Jenkins workflow YAML | Setting up CI for a new CAP project; locking in audit findings as enforced gates | + +> **Status**: Preview / Work-in-progress. The seven skills above form the **SAP CAP Enterprise Audit toolkit** delivered in two waves — Wave 1 (Clean Core + Customizing) in PR #279, Waves 2-3 (the remaining five) in the current PR. They are designed to chain together: see the *Typical Workflow* sections below for `sap-cap-stack-audit-full` and `sap-cap-ci-gates-pattern` as composition entry points. + ### Clean Core & Custom Code Retirement | Skill | What it does | When to use | @@ -191,3 +207,23 @@ For end-to-end legacy SEGW + UI5 modernization (backend + UI): ``` The three migration skills are explicitly designed as parallel paths after the backend lands. You don't run both UI skills — you pick the one whose architecture matches your legacy app's complexity and your team's preference. + +For SAP CAP enterprise audit (pre-release readiness for a CAP + Fiori Elements + S/4 Tier-2 stack on BTP): + +``` +1. sap-cap-stack-audit-full → Run the full audit stack in parallel; consolidated report + (orchestrates all the skills below) + + Composed of: + - sap-cap-clean-core-enforce → Audit Tier-2 S/4 service availability vs released-state repo + - sap-cap-customizing-honor → Bidirectional CSV↔code parameter consistency + - sap-cap-security-rbac-matrix → OWASP/ASVS/NIST + role coherence across 4 layers + - sap-fiori-app-audit (xN) → Per-app UI/UX + frontend/backend contract chain + - sap-cap-text-polish → User-visible text + PII safety + i18n bundle gaps + +2. sap-cap-ci-gates-pattern → Lock the audit findings into CI gates that prevent regression + (bidirectional, raise-coverage, availability-drift, + convention-drift, csv-lint) +``` + +The seven CAP audit skills are designed as a single toolkit. Run `sap-cap-stack-audit-full` to dispatch everything at once, or invoke individual skills for a focused investigation. Findings flow into `sap-cap-ci-gates-pattern` so the audit converts to enforced CI gates, not one-off checks. diff --git a/skills/sap-cap-ci-gates-pattern/SKILL.md b/skills/sap-cap-ci-gates-pattern/SKILL.md new file mode 100644 index 00000000..857c4c1e --- /dev/null +++ b/skills/sap-cap-ci-gates-pattern/SKILL.md @@ -0,0 +1,409 @@ +--- +name: sap-cap-ci-gates-pattern +description: Library of five reusable CI gate patterns for SAP CAP projects — bidirectional CSV ↔ code consistency, catalog raise-coverage, released-state / API-availability drift detection, convention-matrix drift, and CSV schema lint. Each pattern is a small portable shell script with a clear contract (inputs, exit codes, output format) that plugs into GitHub Actions, GitLab CI, Jenkins, or BTP CI/CD pipelines. Use when asked to "add a CI gate", "build a drift-detection check", "enforce CSV/code parity", "prevent orphan catalog entries", "set up CI for a CAP project", or to harden a project against regressions of a structural invariant. +--- + +# SAP CAP — CI Gates Pattern Library + +This skill is a **library of reusable CI gate patterns** for SAP CAP projects, not a runner. It teaches the agent to **build** five small portable shell-based gates that catch the most common classes of regression in CAP+Fiori projects, and to wire them into a CI pipeline (GitHub Actions, GitLab CI, Jenkins, or BTP CI/CD). + +Each gate follows a deliberate contract: +- One concern per gate. +- POSIX `sh` or `bash` only; no Node/Python dependency unless the project already requires it. +- Idempotent: same repo state → same exit code. +- Exit `0` = pass, `1` = fail (CI-blocking), `2` = warning (non-blocking, prints but allows continue). +- Output: structured (TSV / JSON-lines) so humans and automation can read it. + +The skill is **descriptive** (it explains the patterns) and **generative** (in `apply` mode it writes the scripts and the workflow YAML into the project). + +## v1 Guardrails + +- **Generates scripts only.** Never edits production code, never opens PRs. +- **Idempotent generation.** Re-running `apply` overwrites the gate scripts only if `--force` is passed; otherwise it skips existing files. +- **Pattern-first, project-second.** The skill teaches the pattern; the user chooses which patterns apply to their project. Do not apply all five if some are irrelevant. +- **Cite exit codes and output format** in the generated scripts so CI logs are self-explanatory. + +## Smart Defaults (apply silently, do NOT ask) + +| Aspect | Default | Why | +| --- | --- | --- | +| Mode | `describe` (explain patterns, generate nothing) | Safer; user opts into `apply` | +| Script location | `scripts/ci/` | Common convention | +| CI provider | GitHub Actions (`.github/workflows/`) | Most common in CAP open-source | +| Shell | `bash` with `set -euo pipefail` | Portable + safe defaults | +| Exit semantics | `0` pass · `1` fail · `2` warn | Aligns with standard CI runners | +| Output format | Plain text for humans, optional `--json` flag for automation | Easier to triage in CI logs | + +## Input + +Single optional argument with format `<pattern> [mode]`: + +| Argument | Meaning | +| --- | --- | +| (empty) | Describe all five patterns; generate nothing | +| `bidirectional` · `raise-coverage` · `availability-drift` · `convention-drift` · `csv-lint` | Describe one specific pattern | +| `all apply` | Generate all five gates into `scripts/ci/` + a workflow YAML | +| `<pattern> apply` | Generate only that one pattern | + +Examples: (no arg) · `bidirectional apply` · `all apply`. + +## The Five Patterns + +### Pattern 1 — Bidirectional CSV ↔ Code Consistency + +**Intent.** When a project uses a settings table seeded via CSV (e.g. SystemParameter pattern), each parameter must have a consumer in code, and each `params.X` read in code must have a CSV seed. Otherwise the admin UI surfaces dead settings, or the code reads from a setting the admin doesn't know exists. + +**Inputs.** +- CSV file: e.g. `db/data/sap.<namespace>-Settings.csv` with a column representing the key (often the first column, e.g. `Key`). +- Code paths: `srv/` and any subset where settings are consumed. +- Reader pattern: typically `params.X`, `cfg.X`, `<helper>.get(...).<X>`, or similar — discovered from the project. + +**Algorithm.** +1. Extract CSV keys → `csv-keys.txt`. +2. Grep code for keys read from settings → `code-keys.txt`. +3. `comm -23 code-keys.txt csv-keys.txt` → **inverse orphans** (code reads, CSV missing). +4. `comm -23 csv-keys.txt code-keys.txt` → **forward orphans** (CSV seeds, no consumer). +5. Apply an allowlist (some seed keys are intentionally consumed by external systems and won't appear in code). +6. Exit `1` if either list is non-empty (minus allowlist). + +**Skeleton (`scripts/ci/check-settings-bidirectional.sh`).** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +CSV="${1:-db/data/<project>-Settings.csv}" +CODE_DIR="${2:-srv}" +KEY_COL="${3:-1}" # column index in the CSV (1-based) +ALLOWLIST="${4:-scripts/ci/settings-allowlist.txt}" + +# Extract CSV keys (skip header) +awk -F',' -v col="$KEY_COL" 'NR>1 && $col!="" {print $col}' "$CSV" | sort -u > /tmp/csv-keys.txt + +# Extract code-read keys — adapt the regex to the project's reader pattern +grep -rohE "(p|params|cfg|config)\??\.([A-Z][A-Z0-9_]+)" "$CODE_DIR" --include="*.ts" --include="*.js" 2>/dev/null \ + | grep -oE "[A-Z][A-Z0-9_]+" \ + | sort -u > /tmp/code-keys.txt + +# Optional allowlist (one key per line) +test -f "$ALLOWLIST" || : > /tmp/allowlist.txt +cat "${ALLOWLIST:-/tmp/allowlist.txt}" 2>/dev/null | sort -u > /tmp/allowlist-keys.txt || : > /tmp/allowlist-keys.txt + +INV=$(comm -23 /tmp/code-keys.txt /tmp/csv-keys.txt | comm -23 - /tmp/allowlist-keys.txt) +FWD=$(comm -23 /tmp/csv-keys.txt /tmp/code-keys.txt | comm -23 - /tmp/allowlist-keys.txt) + +INV_N=$(echo -n "$INV" | grep -c . || true) +FWD_N=$(echo -n "$FWD" | grep -c . || true) + +echo "Inverse orphans (code reads, CSV missing): $INV_N" +echo "$INV" | head -50 +echo +echo "Forward orphans (CSV seeds, code missing): $FWD_N" +echo "$FWD" | head -50 + +test "$INV_N" -eq 0 -a "$FWD_N" -eq 0 +``` + +**Tuning.** The regex on line 16 is the only project-specific part. Map it to your project's reader signature. + +### Pattern 2 — Catalog Raise-Coverage + +**Intent.** When a project has a "rule catalog" (e.g. `ProcessStepCheck` codes, validation rules, error categories), each catalog entry that is active must be **raised** from at least one place in code; otherwise the catalog is lying. + +**Inputs.** +- Catalog CSV: e.g. `db/data/sap.<namespace>-ProcessStepCheck.csv` with a column `Code` and a column `IsActive` (or equivalent). +- Code paths: `srv/`. +- Raise pattern: typically a helper like `raiseCatalogException(...)`, `raiseError(code, ...)`, or `req.reject(code, ...)`. + +**Algorithm.** +1. Extract active codes from CSV → `active-codes.txt`. +2. Grep code for `<raiseHelper>(.*CODE_X)` matches → `raised-codes.txt`. +3. `comm -23 active-codes.txt raised-codes.txt` → unraised codes. +4. Exit `1` if non-empty. + +**Skeleton.** Identical structure to Pattern 1; the regex on the grep is the only difference. Allowlist is supported (some codes are raised dynamically via configuration). + +### Pattern 3 — Released-State / API-Availability Drift + +**Intent.** SAP CAP projects often consume S/4HANA APIs that have a **release state** (C1 released, C2 sandbox, C3 deprecated). When a project pins a service catalog with an `availability` array per edition (e.g. `Public Cloud`, `Private Cloud`, `On-Premise`), drift between the local pin and the upstream `SAP/abap-atc-cr-cv-s4hc` repository (or equivalent ABAP API Release State source) must be caught. + +**Inputs.** +- Local service catalog file: e.g. `srv/integration/s4CompatibilityPolicy.js` exporting `services[]` with `{name, availability, probeObject}`. +- Upstream source: `SAP/abap-atc-cr-cv-s4hc` repository (or analogous). +- Cache directory for the upstream snapshot. + +**Algorithm.** +1. Refresh local cache of the upstream API Release State (clone or pull). +2. For each entry in the local catalog, query the upstream for the matching object + edition. +3. If upstream marks the object as **deprecated** for an edition the local catalog still claims as available, raise a finding. +4. If upstream marks the object as **available** for an edition the local catalog doesn't list, raise an informational finding (potentially missed capability). +5. Exit `1` on deprecation drift, `2` on missed-capability drift. + +**Skeleton outline.** Detailed implementation lives in [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md); this pattern is the **CI scheduling** of that audit. Typical wrapper: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +CATALOG="${1:-srv/integration/<catalog>.js}" +UPSTREAM_CACHE="${2:-.cache/abap-atc-cr-cv-s4hc}" + +# Refresh cache (idempotent) +if [ -d "$UPSTREAM_CACHE/.git" ]; then + (cd "$UPSTREAM_CACHE" && git pull --ff-only --quiet) +else + git clone --depth 1 --quiet https://github.com/SAP/abap-atc-cr-cv-s4hc "$UPSTREAM_CACHE" +fi + +# Invoke the matrix audit (delegates to sap-cap-clean-core-enforce logic) +node scripts/ci/check-availability-drift.js "$CATALOG" "$UPSTREAM_CACHE" "${3:---format=tsv}" +``` + +The `.js` companion script reads the local catalog and compares each entry against the upstream — pseudocode in [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md). + +### Pattern 4 — Convention / Matrix Drift Detection + +**Intent.** When a project codifies a convention in an ADR or matrix document (e.g. "every cap-js plugin in `package.json` must be documented in ADR 0011 with status `Adopted` / `Deferred` / `Not-applicable`"), CI must enforce the two-way binding: package change without ADR change = drift; ADR change without package change = also drift. + +**Inputs.** +- Source A: e.g. `package.json` dependencies matching a prefix (`@cap-js/*`). +- Source B: e.g. `docs/adr/0011-*.md` containing a table or YAML block enumerating the plugins. + +**Algorithm.** +1. Extract plugin names from Source A. +2. Extract plugin names from Source B. +3. Symmetric difference → drift list. +4. Exit `1` if non-empty. + +**Skeleton.** + +```bash +#!/usr/bin/env bash +set -euo pipefail + +PKG="${1:-package.json}" +ADR="${2:-docs/adr/<matrix-adr>.md}" +PREFIX="${3:-@cap-js/}" + +# From package.json: every dependency matching PREFIX +node -e "const p = require('./$PKG'); const all = {...(p.dependencies||{}), ...(p.devDependencies||{})}; Object.keys(all).filter(k => k.startsWith('$PREFIX')).forEach(k => console.log(k))" | sort -u > /tmp/pkg-list.txt + +# From ADR: every plugin name appearing as an inline-code segment matching PREFIX +grep -oE "\`$PREFIX[a-z-]+\`" "$ADR" | tr -d '`' | sort -u > /tmp/adr-list.txt + +DRIFT_PKG_ONLY=$(comm -23 /tmp/pkg-list.txt /tmp/adr-list.txt) +DRIFT_ADR_ONLY=$(comm -13 /tmp/pkg-list.txt /tmp/adr-list.txt) + +DPO=$(echo -n "$DRIFT_PKG_ONLY" | grep -c . || true) +DAO=$(echo -n "$DRIFT_ADR_ONLY" | grep -c . || true) + +echo "In package.json but missing from ADR: $DPO" +echo "$DRIFT_PKG_ONLY" +echo +echo "In ADR but missing from package.json: $DAO" +echo "$DRIFT_ADR_ONLY" + +test "$DPO" -eq 0 -a "$DAO" -eq 0 +``` + +### Pattern 5 — CSV Schema Lint + +**Intent.** Catch CSV seed corruption before it reaches deployment: missing columns, type mismatches, FK referential integrity violations, duplicate primary keys. + +**Inputs.** +- CSV files under `db/data/`. +- Schema definition: the corresponding entity in `db/schema.cds`. + +**Algorithm.** +1. For each CSV file `<entity>.csv`: + - Read the header row. + - Find the entity in `db/schema.cds`; build a column → type map. + - Verify CSV header matches entity columns (set equality). + - For each row: type-check each cell (UUID, Integer, Decimal, Date/DateTime, Boolean, String length). + - Check primary key uniqueness. + - For FK columns, verify referent exists in the referent CSV. +2. Exit `1` on any violation. + +**Skeleton outline.** This pattern usually requires Node because CSV + CDS introspection is non-trivial in pure shell. Use the project's `@sap/cds` library directly via the Node API (no shell-out to user-provided strings): + +```javascript +// scripts/ci/check-csv-lint.js +const cds = require('@sap/cds'); +const fs = require('fs'); +const path = require('path'); + +(async () => { + const csn = await cds.load(['db', 'srv']); + const csvDir = 'db/data'; + const files = fs.readdirSync(csvDir).filter(f => f.endsWith('.csv')); + let failures = 0; + + for (const f of files) { + // entity name encoded in filename: <namespace>-<EntityName>.csv + const entityName = /-([A-Z]\w+)\.csv$/.exec(f)?.[1]; + if (!entityName) continue; + const entity = Object.values(csn.definitions).find(d => d.name?.endsWith('.' + entityName) && d.kind === 'entity'); + if (!entity) { console.error('Schema not found for', f); failures++; continue; } + + const content = fs.readFileSync(path.join(csvDir, f), 'utf8').trim().split('\n'); + const header = content[0].split(','); + const expected = Object.keys(entity.elements).filter(k => !entity.elements[k].virtual); + + // Header set equality + const missing = expected.filter(k => !header.includes(k)); + const extra = header.filter(k => !expected.includes(k) && k !== 'IsActiveEntity'); + if (missing.length || extra.length) { + console.error(`[${f}] header mismatch — missing: ${missing}; extra: ${extra}`); + failures++; + } + + // Type-check rows (subset of types) + for (let i = 1; i < content.length; i++) { + const cells = content[i].split(','); + header.forEach((col, idx) => { + const def = entity.elements[col]; + if (!def) return; + const v = cells[idx]; + if (def.type === 'cds.UUID' && v && !/^[0-9a-f-]{36}$/i.test(v)) { + console.error(`[${f}:${i+1}] ${col} not a UUID: ${v}`); + failures++; + } + if (def.type === 'cds.Integer' && v && !/^-?\d+$/.test(v)) { + console.error(`[${f}:${i+1}] ${col} not an Integer: ${v}`); + failures++; + } + // ... extend for Decimal / Boolean / Date / String length + }); + } + } + + process.exit(failures > 0 ? 1 : 0); +})(); +``` + +Wrap with a bash script that invokes it and surfaces the exit code. + +## Wiring into GitHub Actions + +The generated workflow runs every gate on every PR. Use a job matrix so a single failing gate doesn't cancel the others. + +```yaml +# .github/workflows/ci-gates.yml +name: CI Gates +on: + pull_request: + push: + branches: [main] + +jobs: + gates: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + gate: + - { name: bidirectional, cmd: "bash scripts/ci/check-settings-bidirectional.sh" } + - { name: raise-coverage, cmd: "bash scripts/ci/check-catalog-raise-coverage.sh" } + - { name: availability-drift, cmd: "bash scripts/ci/check-availability-drift.sh" } + - { name: convention-drift, cmd: "bash scripts/ci/check-convention-drift.sh" } + - { name: csv-lint, cmd: "node scripts/ci/check-csv-lint.js" } + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: '22' } + - run: npm ci + - name: ${{ matrix.gate.name }} + run: ${{ matrix.gate.cmd }} +``` + +For GitLab CI, mirror the matrix as `parallel:matrix:`. For BTP CI/CD, define one task per gate referencing the same scripts. + +## Step 1: Describe mode (default) + +If `mode` is empty or `describe`: + +1. Read `package.json`, `db/data/`, `db/schema.cds`, `docs/adr/` to detect which patterns apply. +2. For each applicable pattern, print: + - Pattern name and intent + - Sources detected in the project + - Suggested script filename + - Skeleton inputs (e.g., `KEY_COL=2` if a non-default CSV layout) +3. Recommend an order: csv-lint first (fastest), bidirectional + raise-coverage second (catch most regressions), availability-drift third (slowest, needs upstream clone), convention-drift last (cheapest but project-specific). + +Do **not** write files in describe mode. + +## Step 2: Apply mode + +If `mode = apply`: + +1. Discover the inputs: + - Settings CSV: scan `db/data/` for a file whose entity in `db/schema.cds` has a `Key` or `Code` column and a `Value` column → propose as Pattern 1 source. + - Catalog CSV: scan for an entity with `Code` + `IsActive` columns → Pattern 2 source. + - S/4 catalog: scan `srv/integration/` or similar for a file exporting `services[]` with `availability` → Pattern 3 source. + - Plugin matrix ADR: scan `docs/adr/` for files referencing inline-code packages → Pattern 4 source. +2. For each pattern that has inputs: + - Write the script to `scripts/ci/`. + - Make it executable (`chmod +x`). +3. Generate or update `.github/workflows/ci-gates.yml`, preserving any existing jobs. +4. Print a summary: scripts generated, sources used, exit-code semantics, recommended allowlist files to seed. +5. Do **not** commit or push. The user reviews the generated files and decides. + +## BTP vs On-Premise Differences + +| Aspect | BTP | On-Premise | +| --- | --- | --- | +| CI runner | GitHub Actions / BTP CI/CD | Jenkins / GitLab self-hosted / Azure DevOps | +| Node availability in runner | Standard | May require image with Node pre-installed | +| Upstream API source for Pattern 3 | `SAP/abap-atc-cr-cv-s4hc` public repo | Often the same; on-prem may also use ATC results from internal SAP system | +| Workflow scheduling | Cron-friendly via `on: schedule:` | Cron jobs in scheduler | + +The gate scripts are identical across providers; only the YAML wrapper differs. + +## Error Handling + +| Symptom | Cause | Action | +| --- | --- | --- | +| Pattern 1 has no inputs | Project doesn't use a settings table | Skip pattern, mark not-applicable | +| Pattern 3 cannot reach upstream | Network restricted on runner | Cache upstream snapshot weekly via cron, point gate at the cache | +| Regex over-matches | Reader pattern misidentified | Tune the grep regex; add unit test for the gate itself | +| Allowlist not used | Legitimate orphans flagged | Seed allowlist, document the reason in `scripts/ci/<gate>-allowlist.txt` | +| Generated YAML conflicts with existing | Workflow file already present | Merge manually, do not overwrite without `--force` | + +## What This Skill Does NOT Do + +- Does **not** run the gates against the codebase (use the gate scripts directly or run the [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md)). +- Does **not** invent new patterns; the five are the canonical set for CAP projects. +- Does **not** commit or push generated files. +- Does **not** edit production code. +- Does **not** maintain the allowlists; that's a project responsibility. + +## When to Use This Skill + +- When setting up CI for a new CAP project. +- When a regression of a structural invariant was caught manually and you want to automate prevention. +- When porting CI from another stack (Jenkins → GitHub Actions) and want the equivalent gates. +- As a follow-up to one of the audit skills, to lock in the audit's findings as enforced gates. + +## When NOT to Use + +- For functional tests / integration tests (different concern; use the test framework). +- For lint / format gates (use existing tools — ESLint, Prettier, UI5 linter). +- For security scans (use the security ecosystem — CodeQL, Snyk, etc., or the [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md)). +- For performance benchmarks (different concern). + +## Follow-up + +- Pair each generated gate with the audit skill that discovers the underlying issues: + - Pattern 1 ↔ [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) + - Pattern 3 ↔ [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) + - Patterns 2 / 4 / 5 ↔ project-specific audits surfaced by [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) +- When a gate becomes noisy, examine the allowlist before relaxing the gate itself — noisy gates often mean the project's reader pattern has drifted, not that the gate is wrong. +- Schedule Pattern 3 (availability-drift) on a weekly cron, not per-PR — the upstream clone is expensive. + +## References + +- [SAP CAP — Deployment Guide](https://cap.cloud.sap/docs/guides/deployment/) +- [SAP API Release State — `SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc) +- [GitHub Actions — Workflow Syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) +- [Bash — `set -euo pipefail` Safe Defaults](https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425) +- [SAP CAP — Reference Tests and Custom Tests](https://cap.cloud.sap/docs/node.js/cds-test) diff --git a/skills/sap-cap-security-rbac-matrix/SKILL.md b/skills/sap-cap-security-rbac-matrix/SKILL.md new file mode 100644 index 00000000..d9c1d2ae --- /dev/null +++ b/skills/sap-cap-security-rbac-matrix/SKILL.md @@ -0,0 +1,441 @@ +--- +name: sap-cap-security-rbac-matrix +description: Comprehensive security audit for SAP CAP applications: multi-area parallel scanning (handlers, MCP/server, file-upload, deploy/k8s, jobs/integration, CC segregation) + role and role-collection coherence matrix (xs-security ↔ Keycloak ↔ services-auth ↔ handlers ↔ frontend ↔ SoD policy) + OWASP Top 10 / OWASP API Top 10 / ASVS / NIST CSF / CIS / SAP-SOM compliance mapping. Outputs a committable security audit report with findings ranked by confidence ≥0.8. Use when asked to "audit security", "verify RBAC coherence", "check OWASP compliance", "review CAP auth posture", or "produce a security compliance report". +--- + +# SAP CAP Security & RBAC Matrix Audit + +End-to-end security audit for enterprise SAP CAP applications: scans 6 attack-surface areas in parallel, builds a role / role-collection coherence matrix across 4 declaration layers (xs-security, Keycloak realm, CDS @restrict, handlers), and maps every finding to industry compliance frameworks (OWASP Top 10 2021, OWASP API Top 10 2023, ASVS L1-L3, NIST CSF, NIST SP 800-53, CIS K8s/Docker, SAP Secure Operations Map, GDPR, SOX 404). + +Read-only audit, idempotent, ~5-10 minute run with parallel agent dispatch. Designed for pre-deployment compliance gates and quarterly security health checks. + +## Smart Defaults (apply silently, do NOT ask) + +| Setting | Default | Rationale | +|---|---|---| +| Scope | `all` (6 attack-surface areas + role matrix) | Default to full sweep; user narrows if needed | +| Mode | `report` (read-only) | Safe by default | +| Confidence threshold | ≥0.8 | No false-positive shipping | +| Framework citation | OWASP A0X + ASVS V_a.b + at least 1 additional control (NIST / CIS / SAP-SOM / GDPR / SOX) | Defense-in-depth across multiple frameworks | +| Output | `docs/audit/<yyyy-mm-dd>-security-matrix.md` | Committable for traceability | +| Auto-fix | Safe-only: `assertCompanyCodeAccess` injection, `_sanitize*` wrap, CDS `@restrict` CC where addition, xs-security SoD bundle fix, Keycloak realm hardening | Reversible, contained | +| Target ASVS level | L2 minimum | Enterprise SAP + financial workflows posture | +| Parallel agent dispatch | 5 area agents + 1 role agent + 1 CC segregation agent | ~5-10 min total runtime | + +## Input + +Optional flags: + +- **scope** — `all` (default) | `security` (skip role matrix) | `roles` (only role coherence) | `cc-segregation` (only CC scoping audit) | `srv-only` | `auth-only` | `owasp` (only OWASP/Industry) | `compliance` (only matrix mapping) +- **mode** — `report` (default) | `fix` (safe-only auto-apply on dedicated branch) | `pending-only` (re-check previous findings) +- **canonical role list** — path to a project-specific role catalog file (auto-detected if not provided) +- **target frameworks** — comma list to restrict, e.g., `owasp-top10,asvs,nist-csf` + +## Framework Reference (authoritative) + +| Framework | Version | Scope | +|---|---|---| +| **OWASP Top 10** | 2021 | Web app generic — A01 Broken Access Control · A02 Crypto Failures · A03 Injection · A04 Insecure Design · A05 Misconfiguration · A06 Vulnerable Components · A07 ID & Auth · A08 SW & Data Integrity · A09 Logging & Monitoring · A10 SSRF | +| **OWASP API Security Top 10** | 2023 | REST/OData API — API1 BOLA · API2 Broken Auth · API3 BOPLA · API4 Resource Consumption · API5 BFLA · API6 Sensitive Business Flows · API7 SSRF · API8 Misconfig · API9 Improper Inventory · API10 Unsafe Consumption | +| **OWASP ASVS** | 4.0.3 L1-L3 | V2 Auth · V3 Session · V4 Access Control · V5 Validation · V6 Crypto · V7 Errors · V8 Data Protection · V9 Comms · V10 Code · V11 BizLogic · V12 Files · V13 API · V14 Config | +| **NIST CSF** | 2.0 | Identify · Protect · Detect · Respond · Recover | +| **NIST SP 800-53** | rev5 | AC · AU · IA · SC · SI control families | +| **CIS Kubernetes** | 1.9 | Pod security · RBAC · NetworkPolicy · Secret mgmt · Container image | +| **CIS Docker** | 1.6 | Host · Daemon · Image · Container runtime | +| **SAP Secure Operations Map** | 2024 | 12 layer: Awareness · Process · Compliance · Auth · UI · Custom Code · Roles · Audit · Sec Hardening · System Mgmt · Network · OS/DB | +| **GDPR** | EU 2016/679 | Art. 5 Data Minimization · Art. 17 Right to Erasure · Art. 25 Privacy by Design · Art. 32 Sec of Processing | +| **SOX 404** | PCAOB AS 2201 | Audit log append-only · 4-eyes SoD · Change mgmt · Access review | + +## Step 1: Pre-flight + Project Detection + +### 1a. Verify CAP project type + +```bash +test -f package.json && grep -q '"@sap/cds"' package.json +``` + +If not a CAP project, stop and inform user. + +### 1b. Detect security configuration files + +```bash +find . -maxdepth 3 -name "xs-security.json" 2>/dev/null +find . -maxdepth 4 -name "keycloak-realm.json" -o -name "*-keycloak-realm.json" 2>/dev/null +find srv -name "services-auth.cds" -o -name "*-auth.cds" 2>/dev/null +find approuter -name "xs-app.json" 2>/dev/null +``` + +Note presence/absence of each file — Steps 2 and 3 adapt accordingly. + +### 1c. Resolve canonical role catalog + +Auto-detect roles from `xs-security.json`: + +```bash +node -e "JSON.parse(require('fs').readFileSync('xs-security.json','utf8')).scopes.map(s => s.name.split('.').pop())" 2>/dev/null +``` + +If a `CLAUDE.md` or `docs/security.md` exists with a "canonical roles" section, prefer that as the authoritative list. + +### 1d. Branch creation (if mode=fix) + +```bash +git checkout -b codex/security-matrix-<scope>-$(date +%Y-%m-%d) +``` + +## Step 2: Parallel Area Audits (5 area agents) + +Dispatch 5 parallel scans, one per attack-surface area: + +### Agent S1 — Backend handlers + auth + +**Scope**: `srv/handlers/*.ts` + `srv/services-auth.cds` + `srv/*.cds` (main service) + +**Looks for** (frameworks: OWASP A01/A03/A04 · API1/API3/API5 · ASVS V4/V5): + +- **Authorization bypass**: action handler accepting `companyCode` from `req.data` without `assertCompanyCodeAccess(req, cc)` call → A01 Broken Access Control · API1 BOLA · API5 BFLA +- **SQL injection** via template strings in `cds.run` / `db.run` → A03 · ASVS V5.3.4 +- **`forUpdate` missing** in lifecycle action (TOCTOU race) → A04 Insecure Design +- **`req.reject` with `err.message` raw** instead of `rejectSafe()` → A09 · ASVS V7.4.1 (info leak) +- **CDS @restrict gap**: entity exposed without `@restrict` OR `grant: '*'` without CC scope (only SuperAdmin allowed) → API3 BOPLA · ASVS V4.2.2 +- **AuditLogEntry INSERT bypassing `_sanitizePII`** → A02 · GDPR Art. 5 · ASVS V8.3.4 +- **Mass assignment**: `req.data` whitelist not enforced (FE PATCH can write `@Core.Computed` / `LegalImmutable` fields) → API6 · ASVS V5.1.2 + +### Agent S2 — MCP + server + token + +**Scope**: `srv/mcp/*.ts` + `srv/server.ts` + `srv/jobs/CronJob*.ts` + sacAnalyticsHandler.ts + ai/MCP* if present + +**Looks for** (OWASP A02/A05/A07 · API2/API8/API10 · ASVS V2/V3/V14): + +- **Token validation without `timingSafeEqual` HMAC** → A02 · API2 · ASVS V2.4.1 (timing-attack safe compare) +- **Fail-closed gap in production** (Swagger UI mount + MCP fail-closed + OIDC skip path exact match) → A05 · API8 · ASVS V14.3.2 +- **CC whitelist enforcement** on MCP query-data (auto-inject `{ccField IN whitelist}`) → API1 · ASVS V4.2.1 +- **JWT validation** (no `alg: none`, no `kid` spoofing, issuer + audience checked) → A07 · ASVS V3.5.3 +- **Bearer token in URL/query** (logged → leak) → ASVS V2.2.5 (token in header only) + +### Agent S3 — File upload + parsing + +**Scope**: `srv/inbound/*.ts` + `srv/utils/xmlParser.ts` + `srv/extraction/**.ts` + `srv/dms/**.ts` + archive/documentHub actions + +**Looks for** (OWASP A03/A10 · API7/API10 · ASVS V12/V13): + +- **XXE injection** (xml2js / fast-xml-parser external entity processing) → A03 · ASVS V13.2.6 +- **Zip slip / path traversal** in archive extraction → A01 · ASVS V12.3.1 +- **Magic bytes bypass** (header NOT trusted, only magic bytes) → ASVS V12.1.1 +- **SSRF via Document Hub resolver** (host/protocol control) → A10 · API7 · ASVS V12.6.1 +- **Stream limit bypass** (size cap enforced BEFORE read, not after) → ASVS V12.1.3 +- **CC propagation** on streamDocument → API1 · ASVS V4.2.1 + +### Agent S4 — Deploy + k8s + secrets + +**Scope**: `Dockerfile*` + `k8s/*.yaml` + `xs-security.json` + `.github/workflows/*.yml` + `scripts/ci/*.sh` + `mta.yaml` + +**Looks for** (OWASP A05/A06/A08 · CIS K8s/Docker · NIST SC-7/SI-2): + +- **GH Actions injection** (`pull_request_target` + checkout PR head) → A08 · ASVS V14.2.1 +- **SHA pinning gap** (`actions/checkout@v4` non-SHA) → A08 · NIST SR-3 +- **Secret echo in workflow logs** → A02 · ASVS V2.10.4 +- **K8s secret mounted as env var** (HANA/S4/MCP/OIDC password) → CIS K8s 5.4.1 · ASVS V2.10.1 +- **NetworkPolicy egress ALLOW_ALL** → CIS K8s 5.3.2 · NIST SC-7 +- **Container runs as root** (no `runAsNonRoot`) → CIS K8s 5.2.6 · CIS Docker 4.1 +- **`tenant-mode: shared`** when single-tenant intended → API3 BOPLA · ASVS V4.3.1 +- **Dockerfile `FROM <image>:latest`** (image pinning by tag) → CIS Docker 4.2 + +### Agent S5 — Jobs + integration + adapters + +**Scope**: `srv/jobs/*.ts` + `srv/messaging/*.ts` + `srv/integration/**.ts` + `srv/notifications/**.ts` + `srv/audit/auditLogger.ts` + +**Looks for** (OWASP A02/A08/A09 · API2/API10 · ASVS V6/V9 · SAP-SOM L4/L8): + +- **Webhook signature missing** (Event Mesh, BPA callback, email receivers) → A08 · API10 · ASVS V14.5.3 +- **system-user role bypass** (used outside documented webhook endpoints) → API5 BFLA · ASVS V4.2.1 +- **Notification PII leak** (`_sanitizeNotificationComment` bypass on SMTP/Teams/Slack outbound) → A02 · GDPR Art. 5 · ASVS V8.3.4 +- **Adapter factory selection bypass** (SystemParameter `*_ADAPTER` writable by non-Admin) → API3 BOPLA · A05 +- **Idempotency key replay** (event format not validated) → API4 · ASVS V11.1.6 +- **SMTP TLS verification disabled** (`rejectUnauthorized: false`) → A02 · ASVS V9.1.1 +- **Audit log NOT append-only** at DB layer → A09 · SOX 404 · ASVS V7.3.2 +- **Encryption-at-rest missing** for `IsSecret=true` SystemParameter → A02 · ASVS V6.2.1 + +## Step 3: Agent O1 — OWASP/Industry Orthogonal + +SKIP if `scope=roles` / `auth-only`. + +**Scope**: `srv/server.ts`, `approuter/xs-app.json`, `srv/handlers/*.ts`, `package.json` + `package-lock.json`, `srv/utils/paramEncryption.ts`, `.env*` / `terraform/**/secrets*` + +**Categories** (cross-cutting): + +1. **Security headers** [OWASP A05 · ASVS V14.4]: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cookie SameSite+Secure+HttpOnly +2. **Cryptographic failures** [A02 · ASVS V6]: Hash algo (no MD5/SHA1), RNG (`crypto.randomBytes`), JWT signature pinned, encryption-at-rest AES-256-GCM, key derivation (PBKDF2 ≥600k iter / Argon2id) +3. **IDOR / BOLA horizontal** [A01 · API1]: action handlers receiving `id` parameter without `companyCode` filter in lookup +4. **SSRF host control** [A10 · API7 · ASVS V12.6]: destination URL parametrized + host whitelist (no `0.0.0.0`/`169.254.169.254`/`localhost`) +5. **Components & supply chain** [A06 · NIST SR-3]: `npm audit --production` critical/high residue, lockfile committed, frozen-lockfile in CI +6. **Insecure design** [A04 · ASVS V1]: abuse case missing (webhook flood, race condition, replay), trust boundary unclear +7. **ID & Auth** [A07 · ASVS V2/V3]: session fixation, cookie timeout > 8h, refresh token rotation, MFA option +8. **Logging adequacy** [A09 · NIST AU-2/AU-3 · ASVS V7]: `cds.log()` vs `console.*`, auth failure logged distinguishable, SuperAdmin trail, rate-limit hit logged +9. **Hardcoded secrets** [A02 · ASVS V2.10]: regex sweep for `(api[_-]?key|secret|password|token)\s*=\s*['"][a-zA-Z0-9]{16,}['"]` in srv/scripts/terraform +10. **API inventory & misconfig** [API9/API8]: OpenAPI spec up-to-date, debug/health endpoints behind auth, CORS `*` on sensitive endpoints + +## Step 4: Role + Role-Collection Coherence Matrix + +SKIP if `scope=security` / `cc-segregation`. + +For each canonical role identified in Step 1c, verify 4-layer declaration coherence: + +### Matrix verification + +| Layer | Source | Check | +|---|---|---| +| xs-security.json | `scopes[]` + `role-templates[]` + `role-collections[]` | Every canonical role has scope + template + collection | +| Keycloak realm | `roles.realm[]` + `groups[]` + `clients[].roleMappings` | Every canonical role has realm-role + matching group | +| services-auth.cds | `@restrict.to[]` + `@restrict.grant` | Every canonical role used in at least one `@restrict` | +| Handlers runtime | `req.user.is('<role>')` calls | Every role-name string matches canonical set | + +### Coherence findings + +| Drift type | Severity | Example | +|---|---|---| +| **Zombie role** | LOW | Role in xs-security but never in @restrict + never in handler check | +| **Orphan role check** | HIGH | Handler `req.user.is('Approver')` but no `Approver` scope in xs-security | +| **Naming drift** | HIGH | Role `Manager` in xs-security but handler checks `manager` (case mismatch) | +| **Keycloak gap** | HIGH | Role in xs-security but missing realm-role + group → cannot be assigned on-prem | +| **Collection over-bundle** | HIGH | Role collection includes mutex roles (e.g., `Approver` + `PostingOfficer` outside SOD-exempt) | + +### SoD (Segregation of Duties) policy verification + +For canonical role pairs declared as SoD-conflicting: + +```typescript +// Expected pattern in OIDCAuthStrategy / handlers: +const SOD_EXEMPT_ROLES = ['OperationalAdmin', 'SuperAdmin', 'system-user']; +const SOD_MUTEX_PAIRS = [['Approver', 'PostingOfficer'], ...]; +``` + +For each mutex pair, verify no role collection bundles both (unless in `SOD_EXEMPT_ROLES`). + +## Step 5: CC Segregation Audit + +SKIP if `scope=roles` / `auth-only` / `security`. + +For multi-tenant CAP apps where each tenant has its own `CompanyCode`: + +### 5a. Entity scope audit (services-auth.cds) + +For every entity with `CompanyCode` column in schema: + +```bash +# Find entities with CompanyCode field +grep -nE "CompanyCode\s*:\s*String" db/schema.cds 2>/dev/null + +# Verify their @restrict has CC where +grep -nE "@restrict|@grant" srv/services-auth.cds | grep -B1 -A2 "<EntityName>" +``` + +Expected: `where: "CompanyCode = \$user.attr.CompanyCode"` for every non-SuperAdmin grant. + +### 5b. Handler audit + +For every action handler accepting `req.data.companyCode`: + +```bash +grep -nE "req\.data\.companyCode" srv/handlers/*.ts 2>/dev/null +``` + +Each call must be followed (within a few lines) by `assertCompanyCodeAccess(req, ...)`. If not → finding HIGH (cross-CC mutation possible). + +### 5c. Repository query audit + +For repository functions: + +```bash +grep -rnE "(SELECT|UPDATE|DELETE).*from\s*\(" srv/repositories/ 2>/dev/null +``` + +Must include `companyCode` in WHERE clause when entity has CC column. Repository function signatures should expose `companyCode` parameter (not inferred from request context, to avoid leaks). + +### 5d. Cross-CC leak via association + +For entities with `Association to <ParentEntity>`: + +```bash +grep -rnE "Association to" db/schema.cds 2>/dev/null +``` + +Verify parent entity is also CC-scoped — otherwise a child query may leak parent data from other CC. + +## Step 6: Compliance Matrix Mapping + +For each finding from Steps 2-5, map to control framework. + +### Per-finding mapping table + +| ID | Severity | OWASP T10 | OWASP API | ASVS | NIST 800-53 | CIS | SAP SOM | GDPR | SOX | +|---|---|---|---|---|---|---|---|---|---| + +### Coverage rollup + +| Framework | Findings covered | Status | +|---|---|---| +| OWASP A01-A10 | N total | ✅ / ⚠️ / 🔴 | +| OWASP API1-API10 | N | ✅ / ⚠️ / 🔴 | +| ASVS L1 | N covered / total | ✅ | +| ASVS L2 | N covered / total | ✅ | +| ASVS L3 | N covered / total | ⚠️ | +| NIST CSF function | N control | — | +| CIS K8s/Docker | N control | — | +| SAP SOM 12 layer | N / 12 | — | + +### Gap analysis (controls NOT yet verified) + +Honest assessment: list framework areas the audit did NOT cover (e.g., ASVS V12.4 anti-virus integration, NIST AC-17 Remote Access). + +## Step 7: Output Report + +Save markdown to `docs/audit/<yyyy-mm-dd>-security-matrix.md`: + +```markdown +# Security Matrix Audit — <scope> — <yyyy-mm-dd> + +## Pre-flight +- Branch / sha / scope / mode + +## Discovery +- Security config files detected +- Canonical roles identified: <N> +- Mode dispatched: <area agents + role agent + CC agent> + +## Security Review (per area) +| Area | Status | Findings | Frameworks | + +## Role Definition Matrix +(N canonical × 4 layers) + +## Role Collection Matrix +(M collections × role inclusion × SoD conflicts) + +## CC Segregation Matrix +(Entity × @restrict CC where × handler guard × repository CC filter) + +## Findings (confidence ≥0.8) +### [HIGH] CATEGORY: file:line +- Description +- Exploit / Impact +- Framework mapping: OWASP A0X · API_Y · ASVS V_a.b · NIST CONTROL · CIS X.Y · SAP-SOM L_n +- Confidence: 0.X +- Fix: <recommendation> + +## Compliance Matrix +(per-finding + coverage rollup + gap analysis) + +## Checks Passed (verified clean) +<list> + +## Fix Plan / Fix Applied (mode=fix) +| Severity | Item | Effort | Auto-fixable | + +## Re-verification +- Cadence: monthly / quarterly / pre-deployment +- Command: `sap-cap-security-rbac-matrix` (this skill) +``` + +## Step 8: Apply Fixes (only when `mode=fix`) + +### Safe auto-applicable + +1. **`assertCompanyCodeAccess` injection** in handler that accepts `companyCode` from request (helper must already exist in `srv/utils/authGuards.ts`) +2. **`_sanitize*` wrap** on PII / notification output (helper must already exist) +3. **CDS `@restrict` CC where addition** in `services-auth.cds` for entities with CC column (CDS-only, additive) +4. **xs-security.json SoD bundle fix** — remove conflicting role from over-bundled collection +5. **Keycloak realm hardening** — add token TTL defaults, add missing realm-role for declared scope + +### NOT auto-applied (manual decision required) + +- Refactor handler signature +- Schema change (db/migrations) +- Action availability matrix change +- Cross-cutting adapter factory refactor +- Rename collection in xs-security (breaking change for existing bindings) +- Remove legacy role (architectural decision) + +### Verification after fix + +```bash +git diff --stat +npx cds compile srv app --service OrchestratorService --to edmx > /dev/null +node -e "JSON.parse(require('fs').readFileSync('xs-security.json','utf8'))" # valid JSON +``` + +## BTP vs On-Premise Differences + +| Aspect | BTP Cloud Foundry | Kyma | On-Premise | +|---|---|---|---| +| Auth | XSUAA | XSUAA + IAS | Keycloak / IAS / custom | +| Role mapping | xs-security.json + IAS groups | Same | Keycloak realm + groups | +| Token lifecycle | XSUAA-managed | Same | Keycloak-managed (TTL must be pinned) | +| Audit log | BTP Audit Log Service | Same | Custom adapter | +| Network isolation | Cloud Connector | NetworkPolicy + Connectivity Proxy | Firewall + VLAN | + +## Error Handling + +| Error | Cause | Fix | +|---|---|---| +| No `xs-security.json` found | Project uses different auth scheme | Skill skips xs-security checks, focuses on services-auth.cds + handlers | +| No canonical role catalog | Project lacks a centralized role definition | Auto-extract roles from xs-security.json scopes; warn user about role drift risk | +| Keycloak realm not found | BTP-only deployment | Skip Keycloak matrix check; flag if on-prem deployment is planned | +| Grep returns too many "matches" in handlers sweep | Permissive patterns | Tighten patterns; use `--scope srv/handlers/specific` for focused audit | +| `assertCompanyCodeAccess` helper not found | Project uses different CC guard pattern | Ask user for the helper function name + adjust check | +| Audit log entity not detected | Custom audit table name | Ask user to specify; default search pattern is `AuditLogEntry` / `audit_log` | + +## What This Skill Does NOT Do + +- **No penetration testing** — static audit; doesn't execute exploits +- **No runtime instrumentation** — analyzes source code, not running app +- **No SQL injection runtime test** — flags suspicious patterns, doesn't execute +- **No dependency CVE deep-scan** — basic `npm audit` only; for deep analysis use Snyk / Dependabot +- **No social engineering / phishing audit** — out of code-level scope +- **No third-party service security** (XSUAA / IAS internals) — assumes BTP managed services are secure +- **No compliance certification** — provides evidence; certification (SOC 2, ISO 27001) requires auditor sign-off + +## When to Use This Skill + +- **Pre-deployment gate** — quarterly compliance check before production release +- **Pre-acquisition audit** — assessing 3rd-party CAP app security posture +- **Post-incident review** — after security event, verify scope + identify similar gaps +- **Customer compliance request** — SOC 2 / ISO 27001 / GDPR evidence +- **Role refactor validation** — after adding/removing roles, verify coherence +- **OWASP/ASVS readiness** — pre-ASVS Level 2 verification audit + +## When NOT to Use This Skill + +- **Greenfield project before first deploy** — too early; come back after first iteration with concrete attack surface +- **Pure ABAP / non-CAP project** — use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) instead +- **Single-user / personal project** — overkill; multi-tenant patterns don't apply +- **Real-time penetration test** — use specialized pen-test tooling + +## Follow-up + +After this skill produces the audit: + +- **HIGH findings**: fix immediately (auto-apply where safe, manual review otherwise) +- **MEDIUM findings**: track in pending list, fix in next sprint +- **LOW findings**: document in `docs/security-backlog.md`, address opportunistically +- **Role drift**: align across 4 layers (xs-security ↔ Keycloak ↔ services-auth ↔ handlers) +- **CC segregation gap**: prioritize — cross-CC leaks are GDPR / SOX critical +- **Re-run quarterly** — cadence depends on release velocity; weekly during active feature work + +Related skills: + +- [sap-cap-clean-core-enforce](../sap-cap-clean-core-enforce/SKILL.md) — S/4 API Clean Core compliance (complementary) +- [sap-cap-customizing-honor](../sap-cap-customizing-honor/SKILL.md) — customizing coverage audit (complementary) +- [migrate-custom-code](../migrate-custom-code/SKILL.md) — ABAP-side ATC fixes (companion for SAP custom code) + +## References + +- [OWASP Top 10 2021](https://owasp.org/Top10/) +- [OWASP API Security Top 10 2023](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) +- [OWASP ASVS 4.0.3](https://github.com/OWASP/ASVS/tree/v4.0.3) +- [NIST CSF 2.0](https://www.nist.gov/cyberframework) +- [NIST SP 800-53 rev5](https://csrc.nist.gov/projects/risk-management/sp800-53-controls) +- [CIS Kubernetes Benchmark 1.9](https://www.cisecurity.org/benchmark/kubernetes) +- [CIS Docker Benchmark 1.6](https://www.cisecurity.org/benchmark/docker) +- [SAP Secure Operations Map](https://help.sap.com/docs/secure-operations-map) +- [CAP `@restrict` + XSUAA documentation](https://cap.cloud.sap/docs/guides/authorization) diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md new file mode 100644 index 00000000..f1c82483 --- /dev/null +++ b/skills/sap-cap-stack-audit-full/SKILL.md @@ -0,0 +1,371 @@ +--- +name: sap-cap-stack-audit-full +description: Orchestrator audit that runs the full SAP CAP + Fiori Elements + BTP stack of checks in parallel — UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded-customizing sweep, test suite, plus optional specialized agent reviews (UI5 code quality, CAP performance, project architecture, security, deployment readiness) — and consolidates everything into a single deduplicated report. Use when asked to "run a full audit", "audit the whole stack", "pre-release check", "production readiness audit", "do a comprehensive SAP audit", or to dispatch multiple specialized audits in one shot. +--- + +# SAP CAP Stack — Full Audit Orchestrator + +Run **every applicable check** on a SAP CAP + Fiori Elements + BTP project in **six phases**, parallelize the work aggressively, deduplicate findings, and emit one consolidated report. The skill is **read-only**: it never modifies files and never opens PRs. Its purpose is situational awareness before a release, a hand-off, or a critical merge. + +The skill orchestrates: +- **Static analysis tools** (UI5 linter, manifest schema, CDS compile, TypeScript) +- **Test suite** scoped to the project's existing configuration +- **Specialized audit skills** in this repository ([`sap-cap-clean-core-enforce`](../sap-cap-clean-core-enforce/SKILL.md), [`sap-cap-customizing-honor`](../sap-cap-customizing-honor/SKILL.md), [`sap-cap-security-rbac-matrix`](../sap-cap-security-rbac-matrix/SKILL.md), [`sap-fiori-app-audit`](../sap-fiori-app-audit/SKILL.md), [`sap-cap-text-polish`](../sap-cap-text-polish/SKILL.md)) +- **MCP tooling** when available (Fiori Tools, SAP Docs, UI5 Tooling, CAP/cds-mcp) + +It does **not** invent new checks; it composes existing ones. + +## v1 Guardrails + +- **Idempotent and read-only.** No file modification, no commits, no PRs. +- **Timeouts.** Each sub-audit caps at 10 min wall-clock; on timeout the result is marked `TIMEOUT` and the run continues. +- **Parallelism over speed.** Phases 2 and 3 fan out aggressively (one message, many tool calls). Phase 4 and 5 run sequentially because they depend on Phase 2/3 outputs. +- **Deduplication.** If two sub-audits report the same finding (same `file:line`, same category), keep the description with the most evidence and link to the other audit. +- **Cite `file:line`.** Every finding must have a clickable evidence pointer. + +## Smart Defaults (apply silently, do NOT ask) + +| Aspect | Default | Why | +| --- | --- | --- | +| Mode | Full audit (all phases) | The point of this skill is comprehensive coverage | +| Output language | English (Italian if project requires) | Match project convention | +| Output destination | `docs/audit/<yyyy-mm-dd>-stack-audit.md` | Stable filename convention | +| Test scope | Project's `npm test` script with `--runInBand` if jest, or fallback to project's existing CI test command | Do not invent a new test invocation | +| Agent budget | Max 6 specialized agents in parallel | Keep cost predictable | +| Skip flag | `quick` skips agent reviews (Phase 3); CLI static checks only | ~2 min run instead of ~10 min | + +## Input + +Single optional argument: + +| Argument | Behavior | +| --- | --- | +| (empty) | Full audit of the whole project | +| `ui5` | Skip CAP/BTP-specific phases; only UI5/Fiori checks | +| `cap` | Skip UI5 phases; only CAP backend | +| `btp` | Only BTP best practices + deployment readiness + connectivity | +| `quick` | All phases EXCEPT Phase 3 (agent reviews). ~2 min | +| `<app-name>` or `<sub-folder>` | Filter to a specific app or directory | + +Examples: (no arg) → full · `ui5` · `cap` · `quick` · `manager-ui` · `srv/handlers`. + +## Step 1: Pre-flight + +Always runs, ~30 s. + +```bash +git status --short && git log -1 --oneline +git branch --show-current +``` + +Detect project shape: + +```bash +# CAP signature +test -f srv/server.ts || test -f srv/server.js || test -f srv/index.ts || ls srv/*.cds 2>/dev/null | head -1 + +# Fiori apps detection (multiple layouts supported) +APPS=$(ls -d app/*/webapp 2>/dev/null | wc -l) +test "$APPS" -eq 0 && APPS=$(ls -d apps/*/webapp 2>/dev/null | wc -l) +echo "Detected $APPS Fiori app(s)" + +# CDS compile sanity (entry-point auto-discovery) +SRV=$(grep -lE "^service\s+\w+" srv/*.cds | head -1) +test -n "$SRV" && npx cds compile srv app --service "$(grep -oE '^service\s+\w+' "$SRV" | head -1 | awk '{print $2}')" --to edmx > /tmp/audit-svc.edmx 2>&1 && echo "CDS OK" || echo "CDS FAIL" +``` + +Output of Phase 1: branch SHA, dirty files, project signature (CAP yes/no, Fiori apps count, CDS compile status, primary service name). + +If CDS compile fails, **stop the audit** and emit a single-finding report — no point auditing on top of a broken model. + +## Step 2: Static analysis (parallel) + +All commands in Phase 2 are independent; dispatch them via **multiple Bash tool calls in a single message**. + +### 2a — UI5 Linter per app + +```bash +for app in $(ls -d app/*/webapp 2>/dev/null | cut -d/ -f2); do + dir="app/$app" + result=$(cd "$dir" && npx -y @ui5/linter 2>&1 | grep -E "[0-9]+ problems" | tail -1) + echo "$app: $result" +done +``` + +### 2b — Manifest schema validation + +Prefer MCP tool when available: + +``` +mcp__plugin_sapui5_ui5-tooling__run_manifest_validation app/<each>/webapp/manifest.json +``` + +Fallback: JSON Schema validation via `ajv` or similar against the manifest schema bundled in `@sap-ux/manifest-validation-tool` if installed. + +### 2c — Fiori app discovery + +``` +mcp__plugin_sap-fiori-tools_fiori-tools__list_fiori_apps (searchPath = cwd) +``` + +Record anomalies: duplicate app IDs, non-standard `appPath`, `odataVersion` mismatch with the backend service. + +### 2d — Test suite + +```bash +# Discover the test runner +if grep -q '"jest"' package.json && grep -q '"test":' package.json; then + npm test 2>&1 | tail -10 +elif test -f vitest.config.ts; then + npx vitest run 2>&1 | tail -10 +elif grep -q '"mocha"' package.json; then + npm test 2>&1 | tail -10 +fi +``` + +Skip in `quick` mode. + +### 2e — TypeScript typecheck per app and srv + +```bash +# Backend +test -f srv/tsconfig.json && npx -p typescript tsc --noEmit -p srv/tsconfig.json 2>&1 | tail -5 + +# Each app +for app in $(ls -d app/*/ 2>/dev/null); do + test -f "$app/tsconfig.json" && (cd "$app" && npx -p typescript tsc --noEmit 2>&1 | head -5) +done +``` + +### 2f — Hardcoded customizing sweep + +A quick regex pass to surface obvious hardcoded business decisions. The deep audit lives in [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md); this is a fast cross-check. + +```bash +grep -rnE ">=\s*[0-9]{2,}|<=\s*[0-9]{2,}|setTimeout\([^,]+,\s*[0-9]{4,}" srv/ --include="*.ts" --include="*.js" 2>/dev/null \ + | grep -vE "\.test\.|//|^.*\*|HTTP|status:|\.length|substring" \ + | head -20 +``` + +## Step 3: Specialized audits (parallel agents) + +Skip entirely in `quick` mode. Otherwise dispatch the agents below **in parallel** (one message, multiple Agent / Skill invocations). + +Pick agents based on scope: + +| When | Skill / Agent | Purpose | +| --- | --- | --- | +| Always (if CAP) | [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) | Clean Core Level A compliance audit | +| Always (if customizing pattern detected) | [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) | Bidirectional CSV ↔ code audit | +| Always | [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) | OWASP / ASVS / NIST / RBAC matrix | +| Per Fiori app (scope = `ui5` or full) | [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) `<app>` | Per-app UI/UX + frontend/backend contract | +| Optional polish round | [`../sap-cap-text-polish/SKILL.md`](../sap-cap-text-polish/SKILL.md) `dry-run` | User-visible text quality | +| If MCP `sap-btp-best-practices` exists | (project-specific agent) | BTP destination, XSUAA, audit log, resilience | +| Pre-deploy | (project-specific deployment-readiness agent) | mta.yaml / Kyma manifests / Dockerfile / health endpoints | + +Each agent runs **read-only** and reports back ≤500 words. If an agent times out or is unavailable, mark it `TOOL UNAVAILABLE` and move on. + +Detection heuristics for whether to invoke an optional agent: + +```bash +# Customizing pattern detected? +test -f "db/data/$(ls db/data/ 2>/dev/null | grep -iE 'systemparam|setting|config' | head -1)" && echo "customizing detected" + +# Clean Core (Tier-2 S/4 proxies) detected? +grep -lE "@cds\.external|extend service.*S4|cds\.connect\.to\(['\"]S/4" srv/ -r --include="*.cds" --include="*.ts" 2>/dev/null | head -1 + +# BTP deployment artifacts? +test -f mta.yaml || ls k8s/*.yaml 2>/dev/null | head -1 || test -f Dockerfile +``` + +## Step 4: CAP-specific deep checks + +Skip if scope = `ui5`. + +### 4a — CDS model integrity + +Use the `cds-mcp` MCP server if installed: + +``` +mcp__plugin_cds-mcp_cds-mcp__search_model query="list all entities" +mcp__plugin_cds-mcp_cds-mcp__search_model query="list all services" +``` + +Fallback: `npx cds compile srv app --to json > /tmp/model.json` and inspect entities/services from JSON. + +### 4b — Profile / configuration audit + +```bash +# Discover all CDS profiles +grep -nE "\[(production|onprem|development|live|mocked|k8s|hybrid)" .cdsrc*.json package.json 2>/dev/null | head -20 +``` + +For each profile detected, check: +- Does it set `requires.auth`? +- Does it set `requires.db.kind`? +- Are remote bindings referenced (`requires.<name>.kind: odata-v[24]` + `credentials.destination`)? + +### 4c — Build dry-run + +```bash +grep -q '"build"' package.json && npm run build 2>&1 | tail -20 +test -f mta.yaml && which mbt && mbt build --mode=verbose 2>&1 | tail -20 || echo "mbt not available" +``` + +## Step 5: Best-practice cross-check + +Invoke (via Skill tool) any general best-practice skill present in the user's environment to cross-validate Phase 2/3 findings. Examples (only if available — do not invent): + +- A BTP developer-guide skill +- A BTP cloud-logging best-practice skill +- A BTP connectivity / destination skill +- A CAP deployment-checklist skill + +For each, extract the **DO / DON'T** highlights and compare against Phase 2/3 findings. Anything that violates a DO/DON'T is upgraded to `PRINCIPLE MISMATCH` and added to the high-priority section of the final report. + +## Step 6: Optional formal code review + +Run **only if** the current branch is not `main`/`master` and has a non-trivial diff: + +```bash +BR=$(git rev-parse --abbrev-ref HEAD) +test "$BR" != "main" -a "$BR" != "master" && git diff --stat origin/main..."$BR" | tail -1 +``` + +If applicable, invoke a code-review skill / agent (project-specific) with scope "all changes on current branch vs main". Otherwise, write "No active feature branch — code-review skipped". + +## Output — consolidated report + +Always written to `docs/audit/<yyyy-mm-dd>-stack-audit.md`. Final structure: + +```markdown +# SAP Stack Audit — <yyyy-mm-dd> — branch <name> + +## Summary +- Overall grade: **A | B | C | D** (based on count + severity of findings) +- Critical: N +- High: N | Medium: N | Low: N +- Build status: OK / FAIL +- Tests: passed / total + +## Infrastructure +- CDS compile: OK / FAIL +- TypeScript (per scope): … +- Test suite: … +- Hardcoded residue: N findings + +## UI5 / Fiori (per app) +| App | Linter | Manifest | TypeScript | Notes | +| --- | --- | --- | --- | --- | + +## CAP Backend +- Model: … +- Service layer: … +- Performance flags: … + +## BTP / Deploy +- … + +## Critical (P1) +1. … + +## High (P2) +1. … + +## Quick Wins (P3) +- … + +## Sub-audit reports +- Clean Core: [link] +- Customizing: [link] +- Security RBAC: [link] +- Fiori App (per app): [link x N] +- Text Polish: [link] + +## Tools unavailable +- … + +## Raw findings (deduplicated) +[per phase, citable file:line] +``` + +### Severity rollup + +Grade calculation (default; adjust to project's conventions if `CLAUDE.md` specifies): + +| Grade | Trigger | +| --- | --- | +| A | 0 Critical · 0 High · ≤5 Medium | +| B | 0 Critical · ≤2 High | +| C | 0 Critical · 3-5 High | +| D | ≥1 Critical OR ≥6 High | + +### Deduplication + +Two findings are duplicates when **all** of these match: +- Same file path +- Same line range (or overlapping by ±2 lines) +- Same root cause category + +Merge: keep the description with more evidence; cross-link the other audit's reference. + +## BTP vs On-Premise Differences + +| Aspect | BTP | On-Premise | +| --- | --- | --- | +| Test suite | Often `npm test` with mocked profile | May require live S/4 connectivity (`live` profile) | +| Deployment dry-run | `mbt build` or `kubectl apply --dry-run=client` | Less standardized; may need manual smoke test | +| MCP tooling availability | Generally rich | Limited; expect more `TOOL UNAVAILABLE` markers | +| Connectivity check | Destination service + XSUAA | SAP Cloud Connector / Reverse Proxy | + +The orchestrator logic is identical; expect more `TOOL UNAVAILABLE` flags on-premise. + +## Error Handling + +| Symptom | Likely cause | Action | +| --- | --- | --- | +| CDS compile fails in Phase 1 | Schema error | Stop the audit, emit single-finding report | +| Test suite cannot be invoked | No `test` script / unsupported runner | Mark Phase 2d "SKIPPED (no test runner)" and continue | +| Agent times out (Phase 3) | Long-running sub-audit | Mark `TIMEOUT`, attach partial output, continue | +| MCP tool unavailable | Plugin not installed | Mark `TOOL UNAVAILABLE`, fall back to non-MCP method if any | +| Two agents report the same finding | Expected | Deduplicate per the rule above | +| Test suite produces flaky results | Single-writer SQLite, in-memory DB locks | Note in report; do not retry | + +## What This Skill Does NOT Do + +- Does **not** modify any file. +- Does **not** open PRs. +- Does **not** run destructive commands. +- Does **not** redesign architecture (use a design skill). +- Does **not** invent new checks; only composes existing skills/tools. +- Does **not** download remote artifacts beyond what the project already references. + +## When to Use This Skill + +- Before a major release / cut-over. +- When taking over an unfamiliar project, to get a baseline. +- Before a Clean Core / production-readiness review meeting. +- As a sanity check after a large refactor or merge. +- When preparing a status report for stakeholders. + +## When NOT to Use + +- For a single targeted question (use the specific skill directly). +- When the project is mid-broken state — fix the breakage first. +- For continuous CI execution (too expensive; use [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) instead). +- For learning the codebase from scratch (use a code-exploration skill). + +## Follow-up + +- The consolidated report points to each sub-audit's report. Open the relevant one for deep findings. +- Findings tagged `PRINCIPLE MISMATCH` should be triaged first — they violate established best-practice conventions. +- Findings tagged `TOOL UNAVAILABLE` indicate gaps in the local tooling, not in the project. +- Critical (P1) findings should block release; High (P2) should have owners assigned; Medium (P3) belongs in the backlog. + +## References + +- [SAP CAP — Production-Readiness Guide](https://cap.cloud.sap/docs/guides/deployment/) +- [SAP UI5 — Linter](https://github.com/SAP/ui5-linter) +- [SAP Fiori Tools — Manifest Validation](https://help.sap.com/docs/SAP_FIORI_tools) +- [SAP BTP — Reference Architecture](https://help.sap.com/docs/btp/sap-btp-neo-environment/reference-architectures) +- [OWASP — Application Security Verification Standard (ASVS)](https://owasp.org/www-project-application-security-verification-standard/) diff --git a/skills/sap-cap-text-polish/SKILL.md b/skills/sap-cap-text-polish/SKILL.md new file mode 100644 index 00000000..af90f5d5 --- /dev/null +++ b/skills/sap-cap-text-polish/SKILL.md @@ -0,0 +1,383 @@ +--- +name: sap-cap-text-polish +description: Audit and rewrite all user-visible text across a SAP CAP + Fiori Elements project — backend reject/throw messages, helper rejects, frontend toasts/dialogs/notifications, i18n bundles, CDS labels, CodeList descriptions, and CSV master-data text — to a consistent professional tone. Detects ten anti-patterns (colloquial, accusatory, tech-jargon, fragmented, mixed-language, missing ICU placeholders, PII leak, etc.) and applies only safe additive rewrites on a dedicated branch. Use when asked to "polish UI text", "improve error messages", "review user-visible messages", "fix tone of notifications", "audit i18n bundle", or to raise the language quality of a CAP application before a release. +--- + +# SAP CAP Text Polish + +Audit every piece of **user-visible text** produced by a CAP + Fiori Elements V4 application and rewrite the ones that look unprofessional, unclear, accusatory, or leak technical detail. Output is a structured before/after report; in `fix` mode the skill applies only **safe additive** rewrites (typos, ICU placeholders, missing fallbacks, PII masking, missing i18n keys). Semantic rewrites of legal/policy text and CodeList seed changes are always flagged for human review, never applied. + +The skill is **language-agnostic for detection** but **language-aware for rewriting**: it reads the project's primary locale from `CLAUDE.md`/`AGENTS.md` or from the i18n bundle filename pattern (`i18n_de.properties`, `i18n_it.properties`, ...) and rewrites in the same locale, preserving any non-primary locale variants. + +## v1 Guardrails + +- **Read-only by default.** `fix` is opt-in and limited to additive edits. +- **Never rewrite legal text.** GDPR notices, ToS, consent dialogs are flagged with `LEGAL_REVIEW_REQUIRED`, never edited. +- **Never change semantics.** Polishing is a form-level operation; if the original message is logically wrong, file it as `CONTENT_BUG` for human review. +- **Never rename i18n keys** that are already referenced from CDS annotations. Adding a new key is safe; renaming may break the annotation lookup. +- **PII must be masked** before any user-visible context. The skill detects unmasked IBAN, fiscal codes, VAT numbers, email addresses and either masks them via the project's existing sanitize helper or proposes the helper call as a fix. +- **Cite `file:line`** on every finding. + +## Smart Defaults (apply silently, do NOT ask) + +| Aspect | Default | Why | +| --- | --- | --- | +| Mode | `dry-run` (report only) | Safer; user opts into `fix` | +| Tone profile | `formal` | Enterprise SAP audience; `friendly` opt-in via argument | +| Primary locale | Detected from i18n filename pattern or project doc | Avoid asking; project state is authoritative | +| Output destination | `docs/audit/<yyyy-mm-dd>-text-polish.md` | Committed for traceability | +| Branch name | `audit/text-polish-<scope>-<yyyy-mm-dd>` | One audit per branch | +| Tone profile rules | Impersonal voice · active voice · action-oriented closure · canonical locale · ≤120 char errors · ≤250 char dialog body · ≤80 char toast · first-word + proper-noun capitalization · ICU placeholders always | Codified in this skill | +| i18n key naming | `snake_case` with category prefix (`msg_`, `dlg_`, `act_`, `lbl_`, `tooltip_`, `err_`) | Match common CAP convention | +| Diff cap per file | ≤50 lines | Keep fixes reviewable | + +## Input + +Single argument with format `<scope> [mode] [tone:<profile>]`: + +| Argument | Meaning | +| --- | --- | +| `<scope>` | `all` (default), `<app-name>` (e.g. `manager-ui`), `srv-only` (backend reject/throw), `i18n-only` (only `webapp/i18n/i18n*.properties`) | +| `mode` | `dry-run` (default, no edits) · `fix` (apply safe rewrites on a branch) | +| `tone:formal` | Formal enterprise tone (default) | +| `tone:friendly` | Friendly-but-professional tone (allows one exclamation, slightly warmer wording) | + +Examples: `all`, `manager-ui fix`, `srv-only dry-run tone:friendly`, `i18n-only fix`. + +## Step 1: Discover text sources + +The skill scans **eight categories** of user-visible text. For each category it produces a list of `{file, line, original_text, classification, suggested_fix}` rows. + +### 1a — Backend reject messages + +```bash +grep -rnE "req\.reject\([0-9]+\s*,\s*['\"`]" srv/ --include="*.ts" --include="*.js" +grep -rnE "req\.error\([0-9]+\s*,\s*['\"`]" srv/ --include="*.ts" --include="*.js" +``` + +### 1b — Backend throws with user-visible message + +```bash +grep -rnE "throw new (Error|CdsError|RequestError)\(['\"`][A-Za-z]" srv/ +``` + +Distinguish: +- **Internal-only** (caught by a top-level error handler and translated) → leave alone, but flag the throw site if the message itself is non-professional. +- **User-visible** (escapes to the OData response) → audit. + +### 1c — Centralized reject helper + +```bash +# Discover the project's centralized reject helper (varies by project) +grep -rnE "rejectSafe\(|safeReject\(|rejectWith\(" srv/ --include="*.ts" --include="*.js" | head -5 +# Then scan all call sites +grep -rnE "<discovered-helper>\([^,]+,\s*[0-9]+,\s*['\"`][^'\"`]+['\"`]" srv/ +``` + +### 1d — Frontend notification helpers + +```bash +grep -rnE "_t\([^,]+,\s*['\"]\w+['\"],\s*['\"][^'\"]+['\"]" app/ +grep -rnE "MessageBox\.(show|confirm|error|warning|information)\(['\"`]" app/ +grep -rnE "MessageToast\.show\(['\"`]" app/ +grep -rnE "req\.notify\(['\"`]" srv/ +``` + +### 1e — i18n bundles + +```bash +ls app/*/webapp/i18n/i18n*.properties +ls app/*/i18n/*.properties +``` + +Read each bundle; one row per key. + +### 1f — CDS labels and titles + +```bash +grep -rnE "@title:\s*['\"`]|@Common\.Label:\s*['\"`]|@Core\.Description:\s*['\"`]" db/ srv/ app/ +``` + +Distinguish hardcoded string literals from `{i18n>key}` bindings. Only the former are in scope here. + +### 1g — CodeList descriptions + +```bash +ls db/data/*_texts.csv 2>/dev/null +ls db/data/sap.*-*.csv 2>/dev/null +``` + +For each CodeList CSV with a `descr` or `name` column, list rows where the description matches an anti-pattern. + +### 1h — CSV master-data labels + +```bash +# Discover CSV files with label/description columns +for csv in db/data/*.csv; do + head -1 "$csv" | grep -qE "(name|label|description|descr)" && echo "$csv" +done +``` + +## Step 2: Classify every text + +For every text found, classify it as one of: + +| Classification | Meaning | Action in `fix` mode | +| --- | --- | --- | +| **OK** | Already professional | No change | +| **POLISH** | Minor refinement (punctuation, capitalization, article) | Auto-apply | +| **REWRITE** | Substantial reword needed | Propose, require review (do not auto-apply unless it's a clear typo) | +| **PII_RISK** | Contains sensitive data unmasked | Auto-apply masking helper call | +| **TECH_LEAK** | Technical detail exposed (stack trace, error code raw, SQL error) | Auto-apply mask + downgrade to operational message; log technical via `LOG.error` | +| **LEGAL_REVIEW_REQUIRED** | Privacy / consent / ToS text | Never edit; flag only | +| **CONTENT_BUG** | Message is logically wrong (says A when code does B) | Never edit; flag for human review | + +## Step 3: Detect the ten anti-patterns + +Apply these heuristics to every text. A row may match multiple anti-patterns; classify by the highest severity. + +| # | Anti-pattern | Pattern | Severity | +| --- | --- | --- | --- | +| 1 | **Colloquial / Telegram** | "oops", "uh", "kk", "ok dai", smiley faces in non-marketing contexts | POLISH | +| 2 | **Accusatory tone** | "you did wrong", "you can't", "you must" (second person imperative) | REWRITE | +| 3 | **Unexplained tech jargon** | "404", "null pointer", "SQL error", raw stack class names, server URLs | TECH_LEAK | +| 4 | **Fragmented / incomplete** | "Error." with no context, "Failed", "Invalid" without operand | REWRITE | +| 5 | **All-caps / multiple exclamations** | `[A-Z]{5,}`, `!{2,}`, `\?{2,}` | POLISH | +| 6 | **Unexpanded acronyms** | BP, CC, SDI, FE, PO etc. used without first-occurrence expansion | POLISH | +| 7 | **Mixed locale** | EN words inside an IT/DE/etc. message (e.g. "Errore: invalid input") | REWRITE | +| 8 | **Missing ICU placeholders** | Concatenation with `+` or template literals that should be `{0}` / `{0,number,#,##0.00}` | POLISH (auto-apply) | +| 9 | **Inconsistent punctuation** | Missing final period in complete sentence, redundant punctuation | POLISH | +| 10 | **PII leak** | Unmasked IBAN, fiscal code, VAT number, full email | PII_RISK (auto-apply mask) | + +Detection regex examples: + +```bash +# Anti-pattern 5: all-caps or multiple exclamations +grep -rnE "[A-Z]{5,}|!{2,}|\?{2,}" app/*/webapp/i18n/ + +# Anti-pattern 8: concatenation that should be ICU +grep -rnE "['\"`][^'\"`]+['\"`]\s*\+\s*\w+\s*\+\s*['\"`]" srv/ app/ + +# Anti-pattern 10: PII +grep -rnE "IT[0-9]{2}[A-Z][0-9]{10}[0-9A-Z]{12}" srv/ app/ # IBAN IT +grep -rnE "[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]" srv/ app/ # IT fiscal code +grep -rnE "\b[\w.-]+@[\w.-]+\.\w+\b" srv/ app/ | grep -v "_sanitize\|@example\|@test" +``` + +## Step 4: Rewrite per tone profile + +For each row classified `POLISH` or `REWRITE`, propose a rewrite that: + +1. **Preserves intent.** The user must understand what happened and what to do next. +2. **Applies tone profile.** `formal` is the default: + - Impersonal voice ("The operation cannot be completed") not second person ("You can't do this"). + - Active voice, direct ("Retry in a few minutes"). + - Action-oriented closure where applicable (give a concrete next step). + - First-word capitalization only; locale-canonical punctuation. + - Length caps: ≤120 char errors, ≤250 char dialog body, ≤80 char toast. +3. **Preserves placeholders.** Existing `{0}` / `{0,number,...}` / `{0,date,...}` survive; concatenations are converted to placeholders. +4. **Generates i18n key if missing.** If the text is inline (`_t(view, "key", "fallback")` with key absent from bundle), propose the new key in `snake_case` with category prefix. +5. **Localizes only the primary locale.** Other locales are noted as "needs translation"; the skill does not invent translations. + +### Before / after examples + +Backend error: +```diff +- req.reject(409, 'Stato fattura non valido per questa azione'); ++ req.reject(409, "L'operazione non è disponibile per lo stato corrente della fattura ({0}). Verificare la fase di processo prima di riprovare."); +``` + +Frontend toast: +```diff +- MessageToast.show("Ok!"); ++ MessageToast.show(_t(this, "msg_action_completed", "Operazione completata.")); +``` + +Dialog confirmation: +```diff +- title: "Sei sicuro?", +- message: "Vuoi davvero eliminare?" ++ title: _t(this, "dlg_confirmDelete_title", "Conferma eliminazione"), ++ message: _t(this, "dlg_confirmDelete_msg", "L'eliminazione non è reversibile. Continuare?") +``` + +i18n bundle entry: +```diff +- err_invalid_input=Errore: input invalido!!! ++ err_invalid_input=Il valore inserito non è valido. Controllare il formato e riprovare. +``` + +PII leak fix: +```diff +- LOG.warn(`Notifica fallita per ${user.email}`); ++ LOG.warn(`Notifica fallita per ${_sanitizePII(user.email)}`); +``` + +ICU placeholder fix: +```diff +- MessageToast.show("Hai " + count + " messaggi"); ++ MessageToast.show(_t(this, "msg_unread_count", "Hai {0} messaggi", [count])); ++ # bundle entry: ++ msg_unread_count=Hai {0} messaggi +``` + +## Step 5: Output report + +Write to `docs/audit/<yyyy-mm-dd>-text-polish.md`: + +```markdown +# Text Polish Report — <scope> — <yyyy-mm-dd> + +## Summary +- Total scanned: <N> sources +- OK: <N> | POLISH: <N> | REWRITE: <N> | PII_RISK: <N> | TECH_LEAK: <N> | LEGAL_REVIEW: <N> | CONTENT_BUG: <N> + +## PII / Tech Leak (urgent) +| File:line | Original | Issue | Suggested fix | +| --- | --- | --- | --- | + +## REWRITE (substantial) +| File:line | Original | Rewritten | i18n key | Note | +| --- | --- | --- | --- | --- | + +## POLISH (refinement) +| File:line | Before | After | Type | +| --- | --- | --- | --- | + +## Bundle gaps (i18n) +| Key | Primary locale | Other locales | Used in | +| --- | --- | --- | --- | + +## Stats +- Most polished file: <path> +- Tone consistency score: <0-100>% +- ICU placeholder coverage: <N/M> + +## LEGAL_REVIEW (do not edit) +| File:line | Text | Reason | +| --- | --- | --- | + +## CONTENT_BUG (human review) +| File:line | Text | Suspected logic mismatch | +| --- | --- | --- | +``` + +## Step 6: Safe fix (mode = `fix` only) + +Apply **only** these safe edits: + +✅ Allowed: +- Typo / capitalization fix in i18n bundle. +- Add ICU placeholder where concatenation existed; bundle key gets the new placeholder syntax. +- Mask PII with the project's `_sanitizePII` / `_sanitize*` helper. If no helper exists, propose adding one but do not edit logs yet. +- Add missing fallback in `_t(view, key, fallback)` calls. +- Add missing i18n key (bundle entry creation). +- Add final period where the sentence is complete and missing it. +- Normalize multiple exclamations / question marks down to one. +- Lowercase all-caps non-acronym words. + +❌ Forbidden (always defer to human): +- Substantial REWRITE. +- Rewrite legal / privacy / consent text. +- Rename an i18n key already referenced from CDS annotations. +- Full translation across locales (the skill does primary-locale rewrites only). +- Modify CodeList CSV seed values (downstream migrations / sap-common conventions may depend on them). +- Rewrite stack-trace or technical log strings going to stdout/cloud-logging (those are operator-visible, different audience). +- Touch text inside test fixtures (`*.test.ts`, `__fixtures__/`). + +### Verification after fixes + +```bash +# CSV lint (if project has one) +npm run lint:csv 2>&1 | tail -10 || true + +# CDS compile sanity +npx cds compile srv app > /dev/null && echo "CDS OK" + +# TS typecheck scoped to apps touched +for app in $(git diff --name-only HEAD~1 -- 'app/*/webapp/' | cut -d/ -f1-2 | sort -u); do + test -f "$app/tsconfig.json" && (cd "$app" && npm run ts-typecheck 2>&1 | tail -5) +done + +# Scoped jest if backend reject messages touched +git diff --name-only HEAD~1 -- 'srv/' | grep -E '\.(ts|js)$' | xargs -I {} npx jest --findRelatedTests {} --runInBand 2>&1 | tail -10 +``` + +## Step 7: Commit + branch (mode = `fix` only) + +```bash +git checkout -b "audit/text-polish-<scope>-$(date +%Y-%m-%d)" +git add -A +git commit -m "fix(text-polish): <scope> — <N> messages reformulated [skip ci] + +- <category-A>: <N> fixes +- <category-B>: <N> fixes +- PII masked: <N> sites +- ICU placeholders added: <N> +- Bundle keys added: <N> + +Report: docs/audit/<yyyy-mm-dd>-text-polish.md +" +git push -u origin HEAD +``` + +Then optionally open a PR via `gh pr create`. Never push to `main` directly. + +## BTP vs On-Premise Differences + +| Aspect | BTP (CF / Kyma) | On-Premise | +| --- | --- | --- | +| Operator-visible log target | BTP Cloud Logging / Kyma Loki — JSON structured | NetWeaver SLG1 / file system | +| User-visible message channel | OData fault + UI5 MessageBox | Same | +| PII helper convention | Often `_sanitizePII` co-located with audit logger | Often inline regex, harder to discover | +| i18n bundle path | `webapp/i18n/i18n.properties` | Same | + +The audit logic is identical; only the discovery of the PII helper varies. + +## Error Handling + +| Symptom | Likely cause | Action | +| --- | --- | --- | +| Primary locale cannot be inferred | No `CLAUDE.md` hint and ambiguous bundle filenames | Pick English by default, mark report with "locale assumption: en" | +| PII helper not found | Project lacks centralized sanitize | Flag the gap, propose adding the helper, do not edit logs yet | +| Diff > 50 lines in a single file | Bug in detection or over-eager rewriter | Stop, emit "diff cap exceeded" finding, defer to manual review | +| CSV change requested | Out of scope for safe-fix mode | Flag in report, never auto-apply | +| Test fixture touched | Should never happen | Revert that file's changes, log it as a guardrail violation | + +## What This Skill Does NOT Do + +- Does **not** rewrite legal text (privacy, ToS, consent). +- Does **not** change message semantics. Form-level only. +- Does **not** rename keys already referenced by CDS annotations. +- Does **not** translate across locales (only rewrites the primary locale; non-primary locales are flagged). +- Does **not** modify CodeList CSV seeds. +- Does **not** modify test fixtures. + +## When to Use This Skill + +- Before a release, to raise overall language quality. +- After a translation/localization phase, to verify the primary locale stays consistent. +- After adding a new app, to bring its tone in line with the rest of the suite. +- When a stakeholder complains "the app sounds amateur". +- As a PII safety net before going live with audit logging. + +## When NOT to Use + +- For functional / logic bug fixing (use a code-review skill). +- For full localization to a new language (use a translation tool / professional translator). +- For marketing copy (different audience, different tone profile). +- For real-time / per-keystroke validation messages (separate UX concern, may need product input). + +## Follow-up + +- Pair with [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) — Step 2g of that audit produces a list of hardcoded strings; this skill fixes them. +- Pair with [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) — its PII findings hand off to this skill's PII_RISK class. +- Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to gate the regression of PII leaks and missing ICU placeholders in CI. + +## References + +- [SAP UI5 — `ResourceModel` and `_t` Helper Pattern](https://sapui5.hana.ondemand.com/sdk/#/topic/91f217ce374d4dec8a4b08c8e4c0b3a4) +- [ICU MessageFormat — Placeholder Syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages/) +- [SAP CAP — Localized Messages](https://cap.cloud.sap/docs/guides/i18n) +- [SAP Fiori Design Guidelines — Writing](https://experience.sap.com/fiori-design-web/writing/) +- [OWASP — Sensitive Data Exposure](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/) diff --git a/skills/sap-fiori-app-audit/SKILL.md b/skills/sap-fiori-app-audit/SKILL.md new file mode 100644 index 00000000..92030c19 --- /dev/null +++ b/skills/sap-fiori-app-audit/SKILL.md @@ -0,0 +1,359 @@ +--- +name: sap-fiori-app-audit +description: Operational audit of a single SAP Fiori Elements V4 application — verifies user journey, frontend/backend contract coherence, manifest + annotations + EDMX alignment, i18n coverage, action availability, draft behavior, and applies only safe quick-win fixes on a dedicated branch. Use when asked to "audit Fiori app X", "verify Fiori Elements app", "check the manager/governance/billing app", "review the UI/UX of a Fiori app", "find action gaps in Fiori app", or to validate that a Fiori Elements V4 app is production-ready. +--- + +# SAP Fiori App Audit + +Audit one Fiori Elements V4 app end-to-end. The audit covers the **operational journey** (filter → list → detail → action → refresh) and the **frontend/backend contract** (UI flag → CDS annotation → `@restrict` grant → handler implementation), then optionally applies **safe quick wins** on a dedicated branch. + +The skill targets projects with a typical CAP + Fiori Elements V4 layout: a CAP service on the backend exposing entities and bound actions, and one or more Fiori Elements V4 apps under `app/<app-name>/webapp/` (or `apps/<app-name>/`), with `manifest.json`, `webapp/ext/` extensions, `webapp/i18n/` bundles, and CDS annotations split across `app/annotations*.cds`. + +## v1 Guardrails + +- **Single app per invocation.** `all` is supported but emits N reports (one per app), not a merged one. +- **Read-only by default.** `fix` is opt-in; applies only safe additive UX fixes. +- **Never touch dirty files**, unless they're inside the requested scope and the user has explicitly authorized. +- **Branch-isolated fixes.** Each audit-fix run creates `audit/fiori-<app>-<yyyy-mm-dd>`. +- **Cite `file:line`** on every finding. + +## Smart Defaults (apply silently, do NOT ask) + +| Aspect | Default | Why | +| --- | --- | --- | +| Mode | `report` | Safer default; user opts into `fix` | +| Output language | English (Italian if `CLAUDE.md`/`AGENTS.md` requires it) | Match the project's primary language | +| App-discovery roots | `app/`, `apps/`, `webapp/` | Most common layouts | +| Service introspection | `npx cds compile srv app --service <Service> --to edmx` | Single source of truth for action/property availability at runtime | +| UI5 version | The version declared in `manifest.json:sap.platform.cf` or `manifest.json:sap.ui5.dependencies.minUI5Version` | Don't bump it as a side effect | +| Test scope | Only files touched by the audit-fix branch | Keep run time under 30s | + +## Input + +Single argument with format `<app-name-or-scope> [mode]`: + +| Argument | Meaning | +| --- | --- | +| `<app-name>` | App directory name (e.g. `manager-ui`, `governance`, `billing`). The skill resolves to `app/<app-name>` or `apps/<app-name>` | +| `all` | Iterate every Fiori app found under the app roots | +| `mode` (optional) | `report` (default) · `fix` (apply safe quick wins) · `pending-only` (only verify existing pending findings) | + +Examples: `manager-ui`, `governance fix`, `all pending-only`. + +## Step 1: Pre-flight + +### 1a — Resolve the app + +```bash +# Resolve target directory +for root in app apps; do + test -d "$root/<app-name>" && echo "Found: $root/<app-name>" && break +done +``` + +If the app cannot be resolved, list all apps found and stop. + +### 1b — Git state + +```bash +git status --short +git branch --show-current +git log -1 --oneline +``` + +Classify dirty files: +- **Inside scope** → treat as in-flight work, do not overwrite in `fix` mode. +- **Outside scope** → ignore. + +### 1c — Identify the file map + +For the chosen app, locate: + +| Artifact | Typical location | +| --- | --- | +| Manifest | `app/<app>/webapp/manifest.json` | +| Extensions / custom controllers | `app/<app>/webapp/ext/**/*.{ts,js,xml}` | +| i18n bundles | `app/<app>/webapp/i18n/i18n*.properties` | +| Annotations (global) | `app/annotations.cds` | +| Annotations (per-app) | `app/annotations/<app>.cds` if it exists | +| Backend service | `srv/*Srv.cds` (look for `@(path:'...')` matching the manifest `dataSources` URI) | +| Action handlers | `srv/handlers/*.ts` referenced by the service | +| Auth declarations | `srv/services-auth.cds`, `xs-security.json` | + +### 1d — Pending findings (if registry exists) + +If the project keeps an audit-pending registry (e.g. `docs/audit/APP_AUDIT_PENDING.md`): + +1. Read every entry whose scope matches the chosen app and status is `Pending` or `Recheck`. +2. For each, re-verify the evidence against the current code AND against the **runtime EDMX**: + +```bash +npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx +``` + +Source-level fix without EDMX evidence is `Partially Fixed`, not `Closed`. + +3. Classify outcomes: `Closed`, `Still Open`, `Partially Fixed`, `Superseded` (with reason). + +If `pending-only` mode, stop here and emit the verification table. + +## Step 2: End-to-end user journey + +Walk the canonical journey of the app and audit each station. Document gaps as you go. + +### 2a — Entry point (List Report / landing) + +- Does the manifest declare a landing page with sensible defaults (`navigation.list.detail.routing`, `initialLoad`)? +- Are filter variants persisted (`flexEnabled: true` + `supportedLocales`)? +- Is `liveMode: true` enabled only on **small** datasets (CodeLists, master data)? On large entities it triggers a `$batch` per keystroke — performance regression. + +### 2b — Filter bar and variants + +- For every filter field that points to master data (FK to CodeList / Categories / etc.), is there a `@Common.ValueList` annotation? Free-text input on a master-data field is a P2 finding. +- For required filters, is `@UI.SelectionFields` aligned with backend `@assert.range` constraints? +- Validate `@Common.ValueListWithFixedValues` is **not duplicated** on the same field — MDC will crash with "Invalid property definition". + +### 2c — ObjectPage navigation + +- Is the routing target reachable? `routing.routes` must declare every ObjectPage referenced by the List Report. +- Does the ObjectPage have a meaningful HeaderInfo (Title / TypeName / Description)? Missing HeaderInfo → "Object" generic title. +- For draft-enabled entities (`@odata.draft.enabled: true`): + - Is the active/inactive transition handled in the controller's `before('EDIT', ...)`? + - Are computed flags re-evaluated on draft activation (otherwise stale `Can*` flags persist into the active row)? + +### 2d — Sections and Facets + +- Audit every `@UI.Facets[].$Type` is one of `UI.ReferenceFacet`, `UI.CollectionFacet`, `UI.ReferenceURLFacet`. +- Detect duplicate Facet IDs — Fiori Elements generates auto-Facets for HeaderInfo; custom Facets with the same ID will collide. +- For each table Facet (`UI.LineItem` on a composition target), verify the navigation property is reachable from the parent entity. + +### 2e — Primary and secondary actions + +For every action exposed via `UI.Identification` or `UI.LineItem`: + +| Check | Pattern | +| --- | --- | +| Backend action exists | `grep -nE "action <name>" srv/*Srv.cds` | +| `@Core.OperationAvailable` references a flag | `Can<name>` flag is computed by a handler | +| Flag is NOT `@UI.Hidden` | FE v4 with `autoExpandSelect: true` skips hidden properties in `$select` → flag undefined → button stuck disabled | +| `@restrict` grants the action to expected roles | `srv/services-auth.cds` | +| Handler implementation present | `srv/handlers/*.ts` | + +### 2f — Refresh after action + +- Does the action use `@Common.SideEffects` to refresh affected fields/sections? +- Does the handler return the **full entity** (no `.columns()` filter)? Returning a partial entity breaks edit-mode refresh in FE v4. +- For batched/list actions, are `TargetProperties` and `TargetEntities` declared so FE knows what to invalidate? + +### 2g — Error handling, empty states, i18n + +- Backend errors: action handlers should use a centralized reject helper (e.g. `rejectSafe`) — never leak `err.message` to the client. +- Empty states: does the table have a `noData` text in i18n bundle? +- i18n bundle present in `webapp/i18n/i18n.properties` + at least one localized variant? Hardcoded user-visible text in TS/XML is a P2 finding. +- Manifest declares `i18n` model under `sap.ui5.models` with `bundleName` matching the actual file path? + +### 2h — Responsive / mobile + +- Does the table declare `condensedTableLayout: true`? +- Are column widths or visibility tuned with `@UI.Importance` (`#High` / `#Medium` / `#Low`)? +- For long text columns, is `wrap` or `maxLines` set to avoid overflow? + +## Step 3: Frontend ↔ backend contract chain + +For every primary action exposed by the app, trace the full chain: + +``` +Manifest / annotation / fragment + → flag Can* / CanUse* + → READ enrichment or projection CDS + → @restrict in services-auth.cds + → action handler backend + → SideEffects / refresh UI + → test exists or test missing +``` + +Compile findings into a table: + +| Action | Surface | Flag | Backend grant | Handler | Refresh | Test | Gap | +| --- | --- | --- | --- | --- | --- | --- | --- | + +Common gaps to record: + +1. **UX-positive-false**: UI shows action, backend rejects with 403/409. Always P1 if blocks the only path forward, else P2. +2. **UX-negative-false**: UI hides action that the role would be granted to invoke. +3. **Flag computed with state-only logic, grant is user-aware** (or vice versa). +4. **Company-code / tenant scope mismatch** between UI query, enrichment, and handler. +5. **Draft vs active behavior divergence** for the same action. + +## Step 4: Automated checks (scoped) + +Run only the targets relevant to the scope. + +```bash +# CDS compile and EDMX dump +npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx + +# TypeScript typecheck (if app has tsconfig.json) +test -f "app/<app>/tsconfig.json" && (cd "app/<app>" && npm run ts-typecheck 2>&1 | tail -20) + +# UI5 build (if app has build script) +test -f "app/<app>/package.json" && grep -q '"build"' "app/<app>/package.json" && (cd "app/<app>" && npm run build 2>&1 | tail -10) + +# UI5 linter (if installed) +which @ui5/linter || npx -y @ui5/linter --version +(cd "app/<app>" && npx -y @ui5/linter 2>&1 | tail -20) + +# Manifest validation (via UI5 MCP if available, fallback to JSON Schema) +# mcp__plugin_sapui5_ui5-tooling__run_manifest_validation app/<app>/webapp/manifest.json +``` + +If a tool is not available, record it as "TOOL UNAVAILABLE" and proceed — never silently skip. + +## Step 5: Quick wins (mode = `fix` only) + +A finding is a quick win when **all** of these hold: + +- Local impact, reversible. +- Touches ≤3 application files. +- No DB migration, no contract OData change, no functional policy decision. +- Scoped test/compile available to verify. +- Does not require real SAP/BTP credentials. + +Apply these: + +✅ Allowed: +- Annotation moved into the correct file (per-app vs global). +- `@Core.OperationAvailable` added (referring to an **already-computed** `Can*` flag). +- `@Common.SideEffects` added (when navigation target already exists). +- Fragment custom-handler short dotted path → fully-qualified extension name. +- i18n key added in `webapp/i18n/i18n*.properties` (NOT renamed). +- `@Common.ValueList` added for a master-data field on the filter bar (NOT on the edit form — edit-form value list may require cascading-filter context). +- Manifest binding fix when the target flag already exists. +- Contract test added for an action handler that has none. + +❌ Forbidden: +- Process redesign. +- State machine change. +- Role / SoD change. +- Schema or migration. +- New OData contract that hasn't been agreed. +- Ambiguous security policy decision. +- Wide refactoring. + +## Step 6: Output and verification + +### 6a — Report + +```markdown +# Fiori App Audit — <app> — <yyyy-mm-dd> + +## Pending verification +| ID | Status | Evidence | Action | +| --- | --- | --- | --- | + +## Applied fixes (mode=fix) +| Area | File:line | Description | Test | +| --- | --- | --- | --- | + +## New findings +1. [P1] Title + - File: … + - Impact: … + - Evidence: … + - Required action: … + - Quick win: yes/no + +## PR +- Branch: +- Commit: +- PR URL: + +## Verifications +- CDS compile: PASS/FAIL +- Typecheck/build: PASS/FAIL +- Scoped tests: PASS/FAIL +- Browser check: performed / skipped (reason) + +## Residual risk +- … +``` + +### 6b — Verification + +After fixes: + +```bash +# Recompile +npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx + +# Scoped TS check +test -f "app/<app>/tsconfig.json" && (cd "app/<app>" && npm run ts-typecheck) + +# Scoped tests (only files touched) +npx jest <files> --runInBand +``` + +If the project has a UI dev-server, attempt to start it and verify in a browser **only if the user explicitly requested a visual check** — otherwise record "browser check skipped (not requested)". + +## BTP vs On-Premise Differences + +| Aspect | BTP (CF / Kyma) | On-Premise | +| --- | --- | --- | +| Manifest `sap.platform` | `cf` or `kyma` | typically empty or `abap` | +| Auth claims source | XSUAA / IAS | Keycloak / NetWeaver IdP | +| Approuter path rewriting | Required (CSRF, CORS) | Often absent (same-origin) | +| FLP shell | Standalone with `init.ts` registering `ShellUIService` | Real FLP shell from on-prem launchpad | +| UI5 version source | `https://ui5.sap.com/<version>/` | `/sap/public/bc/ui5_ui5/` | + +The audit logic is identical; expect different manifest sap.platform values and slightly different bootstrap. + +## Error Handling + +| Symptom | Likely cause | Action | +| --- | --- | --- | +| App directory not resolved | Wrong scope name | Print discovered apps and stop | +| `npx cds compile` fails | Schema error or missing model | Don't proceed with EDMX-dependent checks; record P1 and stop | +| Tests fail before fixes | Pre-existing breakage | Record baseline, don't attempt to fix | +| Tests fail after fixes | Audit introduced a regression | Revert the offending fix, escalate to manual review | +| Manifest validation skipped (MCP unavailable) | Tooling not installed | Fall back to JSON Schema check | + +## What This Skill Does NOT Do + +- Does **not** redesign the user journey. +- Does **not** change the state machine. +- Does **not** modify backend grants or auth boundaries. +- Does **not** modify CodeList CSV seeds. +- Does **not** translate or rewrite user-facing text (use `sap-cap-text-polish`). +- Does **not** audit the role/scope layer in depth (use `sap-cap-security-rbac-matrix`). +- Does **not** run the full repository test suite. + +## When to Use This Skill + +- Before merging a Fiori app PR. +- When a user reports "button is missing" or "button does nothing". +- After a UI5 version bump, to verify nothing regressed. +- Before quarterly releases, as a regression-prevention audit. +- When validating pending audit findings (`pending-only` mode). + +## When NOT to Use + +- For greenfield design of a new Fiori app (use `convert-ui5-to-fiori-elements` or design skills). +- For backend-only changes (use a CAP code review skill). +- For pure performance tuning (different skill). +- For cross-app navigation strategy review — this is single-app focused. + +## Follow-up + +- Pair with [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) to validate role/scope layer surfaced by Step 3. +- Pair with [`../sap-cap-text-polish/SKILL.md`](../sap-cap-text-polish/SKILL.md) to clean up i18n gaps surfaced by Step 2g. +- Pair with [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) when the app consumes Tier-2 S/4 proxies. +- Pair with [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) when the audit reveals patterns that should be migrated to Fiori Elements V4. +- Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to add CI gates that prevent regressions of the high-severity findings. + +## References + +- [SAP Fiori Elements V4 — Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) +- [SAP CAP — Fiori Service Annotations](https://cap.cloud.sap/docs/advanced/fiori) +- [OData V4 — Common Annotations](http://docs.oasis-open.org/odata/odata-vocabularies/v4.0/cs01/vocabularies/Org.OData.Capabilities.V1.md) +- [SAP UI5 — `flexEnabled` and Variant Management](https://sapui5.hana.ondemand.com/sdk/#/topic/465f01dcf1cd49b08230e7d3b53b29ed) +- [SAP UI5 — `@Core.OperationAvailable` and Action Visibility](https://sapui5.hana.ondemand.com/sdk/#/topic/cbcb1f3b9c6b4d24aaf5db9447eafa92) From 3721a647b7b02d74d67befafdc034e922e9a6476 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:08:55 +0200 Subject: [PATCH 05/18] feat(skills): add Battle-Tested Patterns knowledge base + companion plugin sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new master skill that distills institutional knowledge — ~60 production- discovered patterns and gotchas across eight categories — into a navigable reference catalog, and cross-links it from the five operational audit skills. New master skill: - sap-cap-fiori-battle-tested-patterns (~620 lines): UI5/FE V4 traps, CAP/ TypeScript pitfalls, BTP/Kyma deployment lessons, security defense-in-depth, customizing-driven patterns, lifecycle/process discipline, post-commit events /messaging, ecosystem plugin landscape. Each pattern: symptom + root cause + generic remedy. Internally anchored so other skills can deep-link by pattern number (e.g. "matches pattern 1.2 — @UI.Hidden on OperationAvailable"). Enrichment of the five Wave 2-3 operational audit skills: - "Battle-Tested Patterns Referenced" section: each skill names the 3-12 patterns most relevant to its audit and explains the connection. - "Recommended Companion Plugins" section: tailored list of external skills / MCP servers that complement the audit (sap-cap-capire, sapui5, sap-fiori- tools, sap-btp-*, sap-docs, context7, playwright). Cross-link to Category 8 of the master skill for the full ecosystem map. README update: master skill added to the SAP CAP Enterprise Audit (Preview) table; status note clarifies the toolkit is now eight skills and the Recommended Companion Plugins convention. Rationale: prior to this commit the operational audit skills carried only their immediate guidance; the institutional knowledge from production deployments (which patterns recur, which gotchas burn teams) lived in private project lore. The master skill makes that knowledge generic, reviewable, and linkable. The companion plugin sections make the ecosystem dependencies explicit — downstream users know which external skills to install for the full experience. All content is 100% generic (no project / domain references). Discovery-driven where applicable; degrades gracefully when the documented preconditions are absent. [skip ci] --- skills/README.md | 3 +- skills/sap-cap-ci-gates-pattern/SKILL.md | 22 + .../SKILL.md | 776 ++++++++++++++++++ skills/sap-cap-security-rbac-matrix/SKILL.md | 26 + skills/sap-cap-stack-audit-full/SKILL.md | 35 + skills/sap-cap-text-polish/SKILL.md | 21 + skills/sap-fiori-app-audit/SKILL.md | 30 + 7 files changed, 912 insertions(+), 1 deletion(-) create mode 100644 skills/sap-cap-fiori-battle-tested-patterns/SKILL.md diff --git a/skills/README.md b/skills/README.md index 1cdf24c3..6d0fae40 100644 --- a/skills/README.md +++ b/skills/README.md @@ -113,8 +113,9 @@ End-to-end audit and compliance verification for SAP CAP applications deployed o | [sap-cap-text-polish](sap-cap-text-polish/SKILL.md) | Audit and rewrite user-visible text (backend reject/throw, helper rejects, frontend toasts/dialogs, i18n bundles, CDS labels, CodeList descriptions). Detects ten anti-patterns including PII leak. Locale-aware, tone-profile-driven, additive safe rewrites only | Pre-release polish; PII safety net for audit logging; after localization phase | | [sap-cap-stack-audit-full](sap-cap-stack-audit-full/SKILL.md) | Orchestrator that runs the full audit stack in parallel — UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded-customizing sweep, test suite + the specialized audit skills above — and consolidates findings into a single deduplicated report | Pre-release situational awareness; project hand-off baseline; after large refactors | | [sap-cap-ci-gates-pattern](sap-cap-ci-gates-pattern/SKILL.md) | Library of five reusable CI gate patterns (bidirectional CSV↔code, catalog raise-coverage, API-availability drift, convention-matrix drift, CSV schema lint). Generates portable shell scripts + GitHub Actions / GitLab / Jenkins workflow YAML | Setting up CI for a new CAP project; locking in audit findings as enforced gates | +| [sap-cap-fiori-battle-tested-patterns](sap-cap-fiori-battle-tested-patterns/SKILL.md) | Knowledge base of ~60 production-distilled patterns and gotchas across eight categories (UI5/FE V4 traps, CAP/TypeScript pitfalls, BTP/Kyma deployment, security defense-in-depth, customizing patterns, lifecycle discipline, post-commit events/messaging, ecosystem plugin landscape). Cross-linked by the operational audit skills | As a reference when reviewing best practices, diagnosing bugs, onboarding to CAP+Fiori, or auditing — invoke directly or via the cross-links from other CAP audit skills | -> **Status**: Preview / Work-in-progress. The seven skills above form the **SAP CAP Enterprise Audit toolkit** delivered in two waves — Wave 1 (Clean Core + Customizing) in PR #279, Waves 2-3 (the remaining five) in the current PR. They are designed to chain together: see the *Typical Workflow* sections below for `sap-cap-stack-audit-full` and `sap-cap-ci-gates-pattern` as composition entry points. +> **Status**: Preview / Work-in-progress. The eight skills above form the **SAP CAP Enterprise Audit toolkit** delivered in two waves — Wave 1 (Clean Core + Customizing) in PR #279, Waves 2-3 (the remaining six including the battle-tested-patterns knowledge base) in the current PR. They are designed to chain together: see the *Typical Workflow* sections below for `sap-cap-stack-audit-full` and `sap-cap-ci-gates-pattern` as composition entry points. Each skill includes a **"Recommended Companion Plugins"** section that lists external skills (`sap-cap-capire`, `sapui5`, `sap-btp-*`, `sap-docs`, `context7`, `playwright`, …) that complement it in a real CAP+Fiori+BTP deployment. ### Clean Core & Custom Code Retirement diff --git a/skills/sap-cap-ci-gates-pattern/SKILL.md b/skills/sap-cap-ci-gates-pattern/SKILL.md index 857c4c1e..d43ee211 100644 --- a/skills/sap-cap-ci-gates-pattern/SKILL.md +++ b/skills/sap-cap-ci-gates-pattern/SKILL.md @@ -400,6 +400,28 @@ The gate scripts are identical across providers; only the YAML wrapper differs. - When a gate becomes noisy, examine the allowlist before relaxing the gate itself — noisy gates often mean the project's reader pattern has drifted, not that the gate is wrong. - Schedule Pattern 3 (availability-drift) on a weekly cron, not per-PR — the upstream clone is expensive. +## Battle-Tested Patterns Referenced + +This skill encodes patterns from [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) as enforced CI gates. The patterns each gate locks in: + +- **5.5 Bidirectional CSV ↔ code consistency** ↔ Gate Pattern 1. +- **6.4 / 6.5 Exception auto-dispatch ordering / Orphan workflow cleanup** ↔ Gate Pattern 2 (catalog raise-coverage prevents dead catalog entries that would break auto-dispatch). +- **3.1 / 3.10 `cds run` does NOT mount UI5 apps** + Clean Core Level A compliance ↔ Gate Pattern 3 (availability drift catches consumed Tier-2 services that lose released state). +- **2.8 `cap-js` plugin matrix discipline** ↔ Gate Pattern 4 (convention/matrix drift between `package.json` and ADR doc). +- **5.4 Master-data references must be value-list-bound** ↔ Gate Pattern 5 partial (CSV schema lint with FK referential integrity). + +A noisy gate (false positives) usually signals a **drift in the project's reader pattern**, not a gate bug. Tune the regex in Pattern 1's skeleton (line 16 of `check-settings-bidirectional.sh`) before relaxing the gate. + +## Recommended Companion Plugins + +| Plugin / Skill | Why for CI gate generation | +|---|---| +| `sap-cap-capire` | Pattern 5 (CSV schema lint) uses `cds.load` to introspect the model; capire docs cover edge cases | +| `sap-docs` | Pattern 3 (availability drift) cross-references `SAP/abap-atc-cr-cv-s4hc` and Help Portal Communication Scenario docs | +| `context7` | GitHub Actions workflow syntax, bash safe-defaults, ICU MessageFormat (when CSV-derived strings carry placeholders) | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. + ## References - [SAP CAP — Deployment Guide](https://cap.cloud.sap/docs/guides/deployment/) diff --git a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md new file mode 100644 index 00000000..5a42ac79 --- /dev/null +++ b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md @@ -0,0 +1,776 @@ +--- +name: sap-cap-fiori-battle-tested-patterns +description: Knowledge base of battle-tested patterns and gotchas for SAP CAP + Fiori Elements V4 + BTP (Cloud Foundry / Kyma) projects — distilled from production deployments. Eight categories covering UI5/FE V4 traps, CAP/TypeScript pitfalls, Kyma deployment lessons, security defense-in-depth, customizing-driven patterns, lifecycle/process discipline, post-commit events/messaging, and the companion-plugin ecosystem landscape. Use when asked to "review CAP best practices", "diagnose Fiori Elements bug", "fix CAP runtime issue", "harden deployment", "review draft behavior", "audit my CAP project", or as a knowledge reference linked from other skills. Not a runnable skill — it is a curated reference catalog. +--- + +# SAP CAP + Fiori Elements V4 — Battle-Tested Patterns + +A reference catalog of patterns and gotchas that have surfaced repeatedly across production SAP CAP + Fiori Elements V4 + BTP deployments. Each entry distills a real-world failure mode into a generic pattern: **symptom** observed by users or operators, **root cause** in the framework / runtime / deployment layer, and a **remedy** that is portable across CAP projects. + +This skill is **not a runner** — it never executes commands. It is a curated reference invoked either directly (when the user asks for best practices) or cross-linked from operational skills like [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md), [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md), [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md), and similar. + +Patterns are organized into **eight categories**. Each category lists the most load-bearing patterns first; lighter-weight items follow. Where multiple frameworks expose the same gotcha (e.g. `@UI.Hidden` interaction with `@Core.OperationAvailable`), the entry points to the framework documentation rather than reproducing it. + +## How to read this skill + +- **As a checklist**: skim the section titles, identify any pattern that matches the current bug / question, jump to the remedy. +- **As a reference linked from another skill**: each pattern is internally addressable (anchor links work). Other skills cite specific patterns by anchor. +- **As a teaching aid**: patterns are written so a developer new to CAP + Fiori Elements V4 can read them as a sequence and absorb the institutional knowledge. + +The catalog is opinionated about defaults. Where SAP documentation lists multiple options, this skill picks one **proven in production** and notes the alternative briefly. + +--- + +## Category 1 — UI5 / Fiori Elements V4 Traps + +### 1.1 — Pin the UI5 minor version explicitly + +**Symptom.** A Fiori Elements V4 app that worked yesterday now renders a blank shell or throws `Component-preload.js 404` after the public CDN rolled forward. + +**Root cause.** Loader URL `https://ui5.sap.com/resources/sap-ui-core.js` (no version) follows the upstream LTS pointer; minor version bumps occasionally break MDC controls (1.136.x MDC Table requires `webapp/changes/{flexibility-bundle,changes-bundle}.json` files; absent → 404 cascade). + +**Remedy.** Pin a specific minor in `manifest.json` and the bootstrap script: +```html +<script id="sap-ui-bootstrap" src="https://ui5.sap.com/1.136.16/resources/sap-ui-core.js" …></script> +``` +Bump only after a smoke test in a non-prod environment. If MDC Table is used, ship empty `flexibility-bundle.json` and `changes-bundle.json` under `webapp/changes/` to prevent 404 cascades. + +### 1.2 — `@UI.Hidden` on a `@Core.OperationAvailable` operand silently disables the button + +**Symptom.** A header action button is permanently disabled even though the role grants it and the user is in the right state. + +**Root cause.** FE V4 with `autoExpandSelect: true` (the default) skips `@UI.Hidden` properties from the `$select` projection. If `@Core.OperationAvailable: SomeFlag` references a `Can*` flag that is also annotated `@UI.Hidden`, the property arrives `undefined` and the action evaluator falls back to "disabled". + +**Remedy.** On computed flags backing `OperationAvailable`, use only `@Core.Computed` — **never** `@UI.Hidden`. Expose them as regular fields (FE V4 won't render them in any UI because no annotation references them as visible). + +### 1.3 — Actions returning `Entity` must NOT use `.columns(...)` + +**Symptom.** After invoking a bound action that creates / promotes / completes a draft, the ObjectPage exits edit mode unexpectedly or shows stale data. + +**Root cause.** When a draft-aware bound action returns the entity but with a projection like `.columns('field1', 'field2')`, the resulting partial entity confuses the FE V4 client cache — it thinks the row was replaced by a thinner version and bails on the edit flow. + +**Remedy.** Return the **full** entity from draft-aware actions: +```javascript +return cds.tx(req).run(SELECT.one.from(EntityName).where({ id })); +``` +Use `@Common.SideEffects: { TargetProperties: […], TargetEntities: […] }` to scope the refresh client-side; don't shrink the action's return shape. + +### 1.4 — `liveMode: true` triggers a `$batch` per keystroke on large entities + +**Symptom.** The filter bar lags or the backend gets hammered when a user types in a filter field on a List Report. + +**Root cause.** `liveMode: true` on a SmartFilterBar / FilterField fires the filter on every keystroke. Acceptable for CodeLists / master data (~tens of rows); catastrophic for transactional entities (~millions). + +**Remedy.** Default to `liveMode: false` on transactional entities. Reserve `liveMode: true` for CodeList-backed dropdowns and small master-data lookups. + +### 1.5 — Composition vs Association for audit / log child entities + +**Symptom.** Saving a draft of a transactional entity fails with `*_drafts_pkey` unique-key violation on an audit / log child entity (`AuditLogEntry`, `ProcessStepExecution`, `BPResolutionCheck`, etc.). + +**Root cause.** CAP copies **Composition** children into the draft. When the parent already has many audit rows, the draft copy duplicates their primary keys → `_drafts_pkey` collision. Compositions also impose cascade-delete semantics, which audit / log tables should never inherit (legal retention). + +**Remedy.** Model audit / log children as **Association**, not Composition. They are not part of the editable graph; they are read-only references with their own retention rules. + +### 1.6 — `i18n>{model>keyPath}` dynamic binding does not work + +**Symptom.** A label or text appears blank or shows the raw key path (`{i18n>some.dynamic.key}`). + +**Root cause.** Double binding (`{i18n>{model>x}}`) is not resolvable at compile time and most UI5 binding parsers refuse it. + +**Remedy.** Resolve the key in the controller `onInit()` and populate the model with literal strings: +```typescript +const i18n = this.getView().getModel('i18n').getResourceBundle(); +this.getView().getModel('view').setProperty('/computedLabel', i18n.getText(keyFromModel)); +``` + +### 1.7 — Custom Facets must not collide with HeaderInfo auto-Facet IDs + +**Symptom.** ObjectPage edit mode shows an unexpected section called "Header" or "General", or your custom Facet is replaced by an auto-generated one. + +**Root cause.** FE V4 edit mode auto-generates a Facet from `UI.HeaderInfo`. Giving a custom Facet the same `ID` causes a silent merge. + +**Remedy.** Prefix custom Facet IDs with the entity name and the section purpose, e.g. `Invoices_Workflow_Timeline`. Never reuse `Header`, `General`, `Main`. + +### 1.8 — `@Common.ValueListWithFixedValues` duplicated crashes MDC + +**Symptom.** Filter bar refuses to render with "Invalid property definition" in MDC. + +**Root cause.** Annotating the same field with `@Common.ValueListWithFixedValues: true` from two sources (e.g. the schema and an annotation file) registers two value lists; MDC chokes on the duplicate. + +**Remedy.** Single source of truth. Decide whether the value list lives on the schema (`db/schema.cds`) or in the annotations layer (`app/annotations/*.cds`) — and stick to it. + +### 1.9 — `manifest.json` must declare the i18n model explicitly + +**Symptom.** Texts annotated `{i18n>label_*}` in CDS render blank in the UI. + +**Root cause.** FE V4 doesn't infer the i18n bundle path. The `manifest.json` must declare: +```json +"sap.ui5": { + "models": { + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { "bundleName": "<namespace>.i18n.i18n" } + } + } +} +``` + +**Remedy.** Verify `bundleName` matches the actual file path under `webapp/i18n/`. Misaligned bundle paths → silent blank texts. + +### 1.10 — `@odata.draft.enabled` master data: choose per case, not per default + +**Symptom.** `lock_drafts` pollution and ETag conflicts on a CodeList that is rarely edited. + +**Root cause.** Blanket `@odata.draft.enabled: true` on pure read-mostly CodeLists adds friction without UX benefit. Conversely, transactional master data with multiple fields under governance benefits from the draft activate/cancel pattern. + +**Remedy.** Per-entity decision: +- **No draft** for read-mostly CodeLists / DocumentTypes (admin edits are rare, single-field). +- **Draft** for transactional master data with multi-field edits under governance. + +### 1.11 — Status fields: use `sap.common.CodeList`, not `String enum` + +**Symptom.** A status column in a table displays raw codes (`'PENDING_APPROVAL'`) instead of friendly labels. + +**Root cause.** `String(N) @assert.range enum { … }` does not generate a value list; FE V4 has no source to map code → label. + +**Remedy.** Model status as a `CodeList` entity: +```cds +entity ProcessingStatuses : sap.common.CodeList { + key code : String(20); +} +``` +Seed it via CSV. For tables, expose a `CASE`-derived computed field with `@Common.Text` + `@Common.TextArrangement: #TextOnly`. + +### 1.12 — Italian "Edit" appears as "Elabora" — never name custom actions "Edit" + +**Symptom.** The standard FE V4 Edit button conflicts with a custom action also called "Edit", or appears in unexpected positions. + +**Root cause.** FE V4 reserves the verb "Edit" for the draft-edit transition. Adding a custom action with the same label confuses the rendering pipeline. + +**Remedy.** Name custom actions explicitly: `Approve`, `Reject`, `Resolve`, `Cancel`. Never `Edit`, `Submit` (reserved by draft semantics), `Activate` (FE V4 internal). + +--- + +## Category 2 — CAP / TypeScript Pitfalls + +### 2.1 — `cds.tx(async tx => …)` autonomous deadlocks SQLite single-writer + +**Symptom.** Lifecycle E2E tests time out non-deterministically on SQLite; production rarely sees it but staging on a small Postgres might. + +**Root cause.** `cds.tx(async tx => …)` opens a separate transaction. On SQLite (single-writer) or under high contention, if the outer handler already holds a write lock, the inner autonomous tx waits forever. + +**Remedy.** Inside a handler, reuse the request tx: `cds.tx(req)`. Reserve autonomous tx (`cds.tx(async tx => …)`) for truly independent work (background jobs, post-commit side effects). For audit on Postgres / HANA where deadlock cost is lower, use `cds.connect.to('db')` and direct `db.run(INSERT…)`. + +### 2.2 — `forUpdate()` before lifecycle UPDATE + +**Symptom.** Concurrent lifecycle actions race; one of them silently overwrites the other's transition. + +**Root cause.** A `SELECT` that precedes an `UPDATE` in a lifecycle action does not lock the row. Under concurrent requests, both reads see `state=A`, both decide to advance to `state=B`, both write. + +**Remedy.** When the read informs the write, use `.forUpdate()`: +```javascript +const row = await tx.run(SELECT.one.from(Entity).where({ id }).forUpdate()); +// decide state transition based on `row` +await tx.run(UPDATE(Entity).set({ status: 'B' }).where({ id })); +``` + +### 2.3 — Side effects in `req.on('succeeded', …)`, not in the request handler + +**Symptom.** Action fails near the end, but the outbound notification / S/4 push has already fired. + +**Root cause.** A successful Cloud Application Programming handler may still roll back if a downstream handler errors. Side effects performed inside the handler get partially committed. + +**Remedy.** Use the request's lifecycle hook: +```javascript +req.on('succeeded', () => emitEvent(…)); +req.on('succeeded', () => sendNotification(…)); +``` +Inside the hook, the tx is already committed. Side effects are fire-and-forget; failure should `LOG.warn` only, never escalate to the user. + +### 2.4 — `cds.log('module-name')` everywhere; never `console.*` + +**Symptom.** Logs in production are noisy and unfilterable; correlation IDs are missing. + +**Root cause.** `console.log` bypasses the CAP logging facade. No log level, no module tag, no correlation. + +**Remedy.** Every module: +```typescript +const LOG = cds.log('module-name'); +LOG.info({ correlationId, action: 'foo' }, 'descriptive message'); +``` +Enables filtering by module, integrates with Cloud Logging / Loki, supports structured metadata. + +### 2.5 — Centralized reject helper (`rejectSafe`) — never expose `err.message` + +**Symptom.** A 500 response leaks internal error text including SQL fragments or stack traces. + +**Root cause.** `req.reject(500, err.message)` propagates the raw exception. SQL injection details, internal hostnames, table names all reach the client. + +**Remedy.** Build one helper: +```typescript +export function rejectSafe(req: cds.Request, code: number, userMsg: string, err: unknown, log: cds.Log) { + log.error({ err, code }, userMsg); + req.reject(code, userMsg); // userMsg is curated, no `err.message` +} +``` +All handlers route errors through this helper. Internal detail goes to the log; user gets a sanitized message. + +### 2.6 — TypeScript strict rolled out folder-by-folder, not repo-wide + +**Symptom.** A "strictify the project" PR explodes into thousands of type errors and never lands. + +**Root cause.** Strict mode is binary in `tsconfig.json`. Flipping it for the whole repo dumps decades of legacy types at once. + +**Remedy.** Per-folder strict tsconfig: +```json +// srv/tsconfig.strict.json +{ + "extends": "../tsconfig.json", + "compilerOptions": { "strict": true }, + "include": ["handlers/**/*.ts", "utils/**/*.ts"] +} +``` +Add `npm run typecheck:strict`. Promote folders one at a time, lock them in CI when ready. + +### 2.7 — `xs-security.json` tenant-mode: `dedicated` is the default; switch to shared deliberately + +**Symptom.** A multi-customer audit reveals tenant data crossing customer boundaries. + +**Root cause.** `"tenant-mode": "shared"` opens multi-tenancy at the XSUAA layer but doesn't automatically enforce tenant isolation in the data layer. Audit log writes, autonomous tx, and `cds.tx({tenant})` calls each need review. + +**Remedy.** Default to `"tenant-mode": "dedicated"` (single-tenant). Multi-customer = multi-deployment. Migration to shared multi-tenancy is a deliberate, planned exercise: schema-routed audit log, tenant-aware autonomous tx, separate review. + +### 2.8 — `cap-js` plugin matrix discipline + +**Symptom.** New plugins drift into `package.json` without ADR / matrix discussion; the runtime acquires unaudited side effects. + +**Root cause.** `cap-js/*` plugins are powerful but each one extends the runtime (e.g. `@cap-js/audit-logging` registers a BTP-emit layer; `@cap-js/change-tracking` hooks every update). Adding them silently leaves the team unaware of the new coupling. + +**Remedy.** Maintain a matrix doc (an ADR is sufficient) listing every `@cap-js/*` plugin: `Adopted` / `Deferred` / `Not-applicable`. CI gate verifies that `package.json` ↔ matrix doc match (see [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection) Pattern 4). + +--- + +## Category 3 — BTP / Kyma Deployment Lessons + +### 3.1 — `cds run` does NOT mount UI5 apps + +**Symptom.** Production deployment serves the OData service but the UI5 apps return 404. + +**Root cause.** `cds-plugin-ui5` (which mounts apps under `cds watch`) is dev-only. `cds run` (production) doesn't include it. + +**Remedy.** In production, serve UI bundles via the approuter: +- Build UI ZIPs (`ui5 build --all`). +- Embed under `approuter/static/<app>/` in the Docker image. +- Approuter routes `/<app>/*` → static files. +- OR use `@sap/html5-app-repo` (BTP CF only; not Kyma if free-tier). + +### 3.2 — BTP Free Plan is CF-only + +**Symptom.** Trying to bind `html5-apps-repo` or `postgresql-db` (free) from a Kyma deployment returns 401 or refuses to provision. + +**Root cause.** Free-tier service brokers expose private endpoints reachable only from BTP CF, not from Kyma's cluster network. + +**Remedy.** For Kyma deployments, pay-tier or in-cluster equivalents: +- UI ZIPs embedded in approuter image (instead of `html5-apps-repo`). +- Bitnami PostgreSQL Helm chart in-cluster (instead of `postgresql-db` free). +- HANA Cloud has a public endpoint, usable from Kyma directly. + +### 3.3 — Approuter on Kyma needs session stickiness + +**Symptom.** FE V4 batch requests fail intermittently with CSRF token mismatch (403 / 419). + +**Root cause.** Kyma's default load balancer round-robins requests across approuter pods. The CSRF token issued by pod A is rejected by pod B. + +**Remedy.** Cookie-based affinity on the Kyma APIRule: +```yaml +metadata: + annotations: + nginx.ingress.kubernetes.io/affinity: "cookie" + nginx.ingress.kubernetes.io/session-cookie-name: "route" +``` +Never disable CSRF (`csrfProtection: false`) — UI5 + FE V4 depend on it. + +### 3.4 — Multi-stage Dockerfile: build needs native toolchain, runtime stays slim + +**Symptom.** Build fails on `tree-sitter-java` native compilation or runtime image is bloated with 800 MB of build dependencies. + +**Root cause.** Some `@sap/eslint-plugin-cds` transitive deps need `python3 make g++` to build native modules; the production image doesn't need them. + +**Remedy.** Multi-stage build: +```Dockerfile +FROM node:22 AS builder +RUN apk add python3 make g++ # build-only tools +COPY package*.json ./ +RUN npm ci --legacy-peer-deps +COPY . . +RUN npx cds build --production +WORKDIR /app/gen/srv +RUN npm ci --production && npm install @cap-js/sqlite @sap/cds-dk tsx + +FROM node:22-slim AS runtime +COPY --from=builder /app/gen /app/gen +WORKDIR /app/gen/srv +ENV NODE_OPTIONS='--import tsx' +CMD ["node_modules/.bin/cds-serve"] +``` + +### 3.5 — `NODE_OPTIONS='--import tsx'` for TypeScript in production + +**Symptom.** Production container starts but fails on first TypeScript handler load: "Unknown file extension: .ts". + +**Root cause.** Standard Node 22 can't load `.ts` files; the project's CAP handlers are TypeScript. + +**Remedy.** Bundle `tsx` in the runtime image and load it via `NODE_OPTIONS='--import tsx'`. The `cds-serve` binary handles the rest. + +### 3.6 — `npm prune` after `npm ci --production` is harmful + +**Symptom.** Production image is missing `@cap-js/sqlite` (test profile) or `tsx` (TypeScript loader); runtime fails. + +**Root cause.** `npm prune --production` removes anything marked `devDependencies`, but the CAP runtime needs some `devDependencies` even in production (test driver to fallback, TS loader). + +**Remedy.** Do **not** run `npm prune`. Use `npm ci --production` in the runtime stage and add back what you specifically need: `npm install @cap-js/sqlite @sap/cds-dk tsx`. + +### 3.7 — Health probes: separate `/health/Live` (no-DB) from `/health/Ready` (DB check) + +**Symptom.** Kyma keeps restarting the pod even though the app is up because the DB is briefly unreachable. + +**Root cause.** A liveness probe that hits the DB conflates "app is alive" with "DB is reachable". A flapping DB → restart loop. + +**Remedy.** Two endpoints: +- `/health/Live` → always 200 if the process is responsive. No DB. +- `/health/Ready` → 200 only if DB is reachable AND model loaded. +Map `livenessProbe` → `/health/Live`, `readinessProbe` → `/health/Ready`. + +### 3.8 — Multi-region readiness via secret rotation + +**Symptom.** Deploying to a second region requires forking the CI pipeline. + +**Root cause.** Hardcoded hostnames in workflow YAML and manifests. + +**Remedy.** Parameterize the Kyma hostname via 2 GitHub Actions secrets: `KYMA_APP_URL` (e.g. `myapp.c-xxx.kyma.ondemand.com`) + `KYMA_KUBECONFIG`. Region switch = rotate the two secrets + update DNS. Zero code change. + +### 3.9 — Image registry: prefer public GHCR for OSS, private for IP + +**Symptom.** Kyma can't pull the image; private registry credentials are rotating constantly. + +**Root cause.** Mixing the image-pull-secret rotation with the deployment rhythm. + +**Remedy.** For open-source / non-sensitive code, public GHCR. For sensitive: dedicate a service account, rotate the PAT separately from deployment, surface the rotation as a runbook item. + +### 3.10 — `cds-plugin-ui5` is dev-only; ship UI separately + +**Symptom.** "UI works in `cds watch` but breaks in `cds run`." + +**Root cause.** `cds-plugin-ui5` registers a custom UI5 sandbox under `cds watch`. `cds run` (production) doesn't load plugins of that class. + +**Remedy.** Two distinct delivery paths: +- Local dev → `cds watch` + `cds-plugin-ui5` serves apps from `app/<app>/webapp/`. +- Production → approuter serves apps from `dist/` (post-`ui5 build`) bundled into the Docker image. + +--- + +## Category 4 — Security Defense-in-Depth + +### 4.1 — Audit log append-only — three layers + +**Symptom.** A SOX / GDPR auditor demands proof that AuditLogEntry cannot be tampered with. + +**Root cause.** Single-layer enforcement (CDS `@restrict: READ only`) is bypassable by anyone with DB access. + +**Remedy.** Three layers, none of which can be the only one: +1. **CDS layer**: `grant: 'READ'` only; no `WRITE` / `UPDATE` / `DELETE` for any role except a specific anonymization job. +2. **Handler layer**: a before-handler on `AuditLogEntry` rejects any non-INSERT verb with 403 explicitly. +3. **Database layer**: DB trigger (Postgres / HANA) raises an exception on `UPDATE` or `DELETE` of `AuditLogEntry` rows, except from a designated retention process. + +Layer 1 is the contract; layer 2 enforces in the runtime; layer 3 is the safety net if a privileged code path slips past layers 1-2. + +### 4.2 — PII sanitization before audit log + +**Symptom.** A leaked DB dump exposes IBANs, fiscal codes, emails because they ended up in the audit log. + +**Root cause.** Audit log writes pass through every textual field as-is. + +**Remedy.** A central `_sanitizePII(text)` helper applied at the audit-log entry boundary. Mask IBANs (preserve last 4), fiscal codes, VAT numbers, full email addresses. Wire it into the audit logger: +```typescript +async function logAuditEntry(tx, entry) { + for (const field of TEXTUAL_FIELDS) { + entry[field] = _sanitizePII(entry[field]); + } + await tx.run(INSERT.into(AuditLogEntry).entries(entry)); +} +``` + +### 4.3 — Magic bytes verification on upload — never trust MIME header + +**Symptom.** A user uploads `evil.pdf` whose body is actually an HTML / SVG with a script payload. + +**Root cause.** The `Content-Type` header is set by the client; PDF readers / image previewers in the UI render based on the body bytes, not the header. + +**Remedy.** Read the first 8 bytes of the upload and verify the magic bytes match the claimed MIME type. For PDF: `%PDF-`. For PNG: `\x89PNG`. For JPEG: `\xFF\xD8\xFF`. Reject mismatches with 415. The check belongs in the upload handler before any persistence call. + +### 4.4 — Rate limiters per endpoint class, not global + +**Symptom.** Auth endpoint is blocked by a global rate limit while file upload is wide open. + +**Root cause.** A single global rate limit can't reflect the different risk / traffic profiles of different endpoint classes. + +**Remedy.** Per-class limits (typical baseline): +- Global: 200/min/IP. +- Write OData: 60/min/IP. +- File upload: 10/min/IP. +- Job endpoints: 30/min/IP. +- MCP / external integration: 60/min per consumer (key from token claim). +All env-tunable. Use Redis backing in multi-pod deployments so limits are global, not per-pod. + +### 4.5 — Token rotation: quarterly default; immediate on compromise + +**Symptom.** A bot token leaked into a public log six months ago is still valid. + +**Root cause.** No rotation policy means tokens live until manually rotated. + +**Remedy.** Document a quarterly rotation runbook for every long-lived token (job invocation token, MCP service token, integration token). Immediate rotation triggers: token in logs, contributor leaves the project, secret scanner alert. + +### 4.6 — OData `$expand` depth cap + per-entity `$top` cap + +**Symptom.** A malicious / careless client expands six levels deep with `$top=10000`; the DB shudders. + +**Root cause.** OData V4 defaults are permissive. + +**Remedy.** +- Enforce a global `$expand` depth limit (2-3) in a request preprocessing handler. +- Enforce per-entity `$top` defaults and caps (e.g. AuditLog: default 200, max 1000). + +### 4.7 — Multi-doc XML / multi-batch attack surface + +**Symptom.** Uploaded multi-document XML (e.g. FatturaPA lotto) brings the parser to its knees with thousands of bodies. + +**Root cause.** XML parsers happily walk every body; absent a cap, an attacker can DoS the service with a single-byte-per-body archive of millions. + +**Remedy.** Hard cap on documents per upload (`MAX_BODIES = 50` is reasonable for batch-invoice patterns). Reject excess with 413. + +--- + +## Category 5 — Customizing-Driven Patterns + +### 5.1 — SystemParameter single-source-of-truth with bounded cache + +**Symptom.** A parameter change in the admin UI takes 5-30 minutes to propagate; users complain. + +**Root cause.** Naive caching with no TTL or no invalidation bus. + +**Remedy.** Wrap reads through a `SystemParamReader.get(category, companyCode)`. Cache for 60-300 seconds (TTL). Publish an `InvalidationBus.publish(category)` event on writes that other pods listen to (Redis pub/sub or NATS). Cross-pod invalidation arrives in <1 s. + +### 5.2 — Adapter factory dual-source fallback chain + +**Symptom.** A change in the customizing UI doesn't take effect; the code is still reading from `cds.env`. + +**Root cause.** Adapter wiring reads `cds.env.X` only, bypassing the SystemParameter layer. + +**Remedy.** Canonical fallback chain inside the adapter factory: +```typescript +const params = await getSystemParamReader().get(CATEGORY, companyCode); +const value = params.MY_KEY // canonical + || cds.env.adapter?.my_key // cds.env fallback + || process.env.MY_KEY // env var fallback + || DEFAULT_VALUE; // hardcoded last-resort +``` +Mark the order in code comments. Audit ensures every adapter uses this chain (see [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md)). + +### 5.3 — Per-tenant override pattern (catalog → company override) + +**Symptom.** A check must be active for company A and disabled for company B without forking the codebase. + +**Root cause.** Single-layer config (`ProcessStepCheck` catalog) can't express per-tenant variation. + +**Remedy.** Two-level table: +1. **Catalog** (`ProcessStepCheck`): ships with the product. Read-only at runtime. +2. **Override** (`CompanyCheckOverride`): one row per `(CompanyCode, CheckCode)`. Fields override catalog defaults (`IsDisabled`, `Severity`, `min`, `max`). +Runtime resolution: override → catalog → hardcoded default. + +### 5.4 — Master-data references must be value-list-bound + +**Symptom.** A user typed `1001 ` (trailing space) in a free-text CompanyCode field; downstream lookups fail. + +**Root cause.** Free-text input on a foreign-key field bypasses referential integrity. + +**Remedy.** Every FK field that points to a master-data entity (CompanyCode, GLAccount, BusinessPartner, etc.) must have `@Common.ValueList` annotation. On the filter bar AND on the edit form. Combined with `@Common.Text` + `@Common.TextArrangement: #TextOnly` for human-readable display. + +### 5.5 — Bidirectional CSV ↔ code consistency + +**Symptom.** Admin UI surfaces a parameter that does nothing; or a code path reads from a parameter the admin doesn't know exists. + +**Root cause.** No enforced contract between CSV seed and code consumer. + +**Remedy.** A CI gate (see [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md#pattern-1--bidirectional-csv--code-consistency)) that fails the build on **inverse orphans** (code reads, CSV missing) or **forward orphans** (CSV seeds, no consumer). Allowlist for legitimate external consumers. + +--- + +## Category 6 — Lifecycle / Process Discipline + +### 6.1 — Centralized phase boundary map + +**Symptom.** Three different handlers compute "what's the next status after PENDING_APPROVAL" independently and disagree. + +**Root cause.** Status transitions implicit in each handler's logic. + +**Remedy.** Single map module (`srv/process/statusTransitions.ts`): +```typescript +export const PHASE_BOUNDARIES: Record<Status, Status> = { + NEW: 'RECEIVED', + RECEIVED: 'VALIDATED', + // … +}; +``` +Every action queries the map; tests verify exhaustiveness. + +### 6.2 — CAS marker for idempotent state advance + +**Symptom.** A user double-clicks "Post Invoice"; two S/4 posts happen. + +**Root cause.** Handler isn't idempotent. + +**Remedy.** Compare-And-Swap on a marker column: +```javascript +const updated = await tx.run( + UPDATE(Invoices).set({ PostedAt: new Date() }) + .where({ id, PostedAt: { '=': null } }) // only if not yet posted +); +if (updated === 0) { + return { alreadyPosted: true }; // someone else already did it +} +// proceed with actual post-to-S4 call +``` + +### 6.3 — Touchless handler idempotency + +**Symptom.** A scheduled job runs the same automatic step twice on retry; downstream side effects duplicate. + +**Root cause.** Steps marked `IsAutomatic=true` are invoked by a job runner that may retry on transient failure. + +**Remedy.** Every touchless handler (resolveBP, checkDuplicate, validateFiscal, matchPO, etc.) must be idempotent: no double INSERT, no double S/4 call, no double notification. Use CAS markers (`6.2`) or guard with explicit "already processed" checks at the top. + +### 6.4 — Exception auto-dispatch ordering + +**Symptom.** Audit log shows the dispatch event before the raise event, confusing forensic review. + +**Root cause.** Auto-dispatcher fires before the audit logger commits. + +**Remedy.** Strict order in the raise handler: +1. `logAuditEntry(…)` — write audit row. +2. `autoDispatch(…)` — only after audit. +3. Return. +This way the audit log captures the pre-dispatch state. + +### 6.5 — Orphan workflow cleanup when exception closes via reclassify + +**Symptom.** A workflow item is still PENDING for an exception that the system already closed via reclassification. + +**Root cause.** Exception closure path doesn't cascade to dependent workflow items. + +**Remedy.** Helper `cancelOrphanWorkflows(tx, closedExceptionIds)` that: +1. Holds the lock acquired upstream (`forUpdate`). +2. UPDATE InvoiceWorkflowItem SET state='CANCELLED' WHERE exception_id IN closedExceptionIds AND state='PENDING'. +3. Logs each cancellation to the audit log. + +### 6.6 — Auto-release pattern (no `forUpdate`, reuses caller tx) + +**Symptom.** Auto-release of a payment block triggers a SELECT that races with the user's release. + +**Root cause.** Naive implementation opens its own tx + forUpdate, contending with the caller. + +**Remedy.** Reuse the caller's tx. Use a CAS marker (`SYSTEM_AUTO`) to ensure the auto-release fires only once. Document the exception clearly — this pattern is the **only** safe place to skip `forUpdate`, and it relies on CAS idempotency. + +--- + +## Category 7 — Events / Messaging Post-Commit Patterns + +### 7.1 — Declarative event service with typed payloads + +**Symptom.** Six different files emit "InvoiceApproved" with three different payload shapes. + +**Root cause.** No declarative contract for events. + +**Remedy.** Declare events in a service: +```cds +service NOVAEvents @(path: '/odata/v4/events') { + event InvoiceApproved : { eDocumentGuid: UUID; companyCode: String; approver: String; approvedAt: Timestamp }; + // … +} +``` +Emit via `cds.connect.to('NOVAEvents').emit('InvoiceApproved', payload)`. AsyncAPI auto-generated from the declaration. + +### 7.2 — Post-commit fire-and-forget emit + +**Symptom.** A successful approve action gets blocked because the event broker is slow. + +**Root cause.** Synchronous emit inside the handler couples user-facing latency to broker SLA. + +**Remedy.** Emit from `req.on('succeeded')`: +```javascript +req.on('succeeded', async () => { + try { await emitNovaEvent('InvoiceApproved', payload); } + catch (err) { LOG.warn({ err }, 'emit failed'); } +}); +``` +The user-facing tx commits; emit failure logs but doesn't propagate. + +### 7.3 — Connection cache as `Promise<Service>`, not `Service` + +**Symptom.** First concurrent requests race; the service connects N times. + +**Root cause.** Cache pattern `let cached: Service | null; if (!cached) cached = await cds.connect.to('X')` allows multiple awaits to all see `null` and initialize. + +**Remedy.** Cache the promise: +```typescript +let cached: Promise<Service> | null = null; +function getEvents(): Promise<Service> { + if (!cached) cached = cds.connect.to('NOVAEvents'); + return cached; +} +``` +First call starts the connect; all concurrent calls await the same promise. + +### 7.4 — Idempotency key cross-emit + +**Symptom.** A retry emit during a transient broker outage produces a duplicate event downstream. + +**Root cause.** Consumer can't tell "this is a retry of the same event" from "this is a new event". + +**Remedy.** Compose a deterministic `idempotencyKey` per (eventType, entityId, companyCode) and include it in the payload metadata. Consumers dedupe on the key. Replay paths (e.g. outbox redelivery) reuse the same key. + +### 7.5 — Outbox replay mirror as detective control + +**Symptom.** Broker was down for 4 hours; some events never made it. + +**Root cause.** No replay path for emissions that failed silently. + +**Remedy.** Mirror emit events into `cds.outbox.Messages` (or a project-specific table). A periodic job re-emits unacknowledged entries. Distinguish first-emit from replay-emit in the metrics (`nova_events_emit_total` vs `nova_events_replay_total`). + +--- + +## Category 8 — Ecosystem Plugin Landscape + +This category is not gotchas — it's the map of **companion plugins / skills** that a CAP + Fiori Elements + BTP project benefits from. Each entry lists what it provides; the consuming skill's "Recommended Companion Plugins" section will reference these by name. + +### 8.1 — SAP CAP Capire (`sap-cap-capire`) + +CAP runtime knowledge base. Covers CDS modeling, service handlers, draft semantics, authentication / authorization, deployment profiles, multitenancy. Reference for any CAP-side question that goes deeper than this skill's snapshot. + +### 8.2 — SAP UI5 (`sapui5`, `sap-fiori-tools`) + +UI5 API explorer, control library reference, Fiori Tools scaffolding. Look up control APIs, manifest schema, annotation reference. Mandatory companion when working on Fiori Elements V4 apps. + +### 8.3 — SAP BTP Cloud Platform (`sap-btp-cloud-platform`) + +BTP service map (auth, persistence, connectivity, eventing). Use when designing a new BTP-side deployment or troubleshooting service bindings. + +### 8.4 — SAP BTP Connectivity (`sap-btp-connectivity`) + +Destination service, Cloud Connector, on-prem connectivity, principal propagation. Use when configuring S/4HANA destinations (BTP CF) or troubleshooting Tier-2 proxy auth. + +### 8.5 — SAP BTP Integration Suite (`sap-btp-integration-suite`) + +iFlow design, Open Connectors, API Management. Use when the project consumes Integration Suite iFlows or exposes APIs via API Management. + +### 8.6 — SAP BTP Cloud Logging (`sap-btp-cloud-logging`) + +OpenTelemetry integration, Kibana / Cloud Logging dashboards. Use when wiring up production observability. + +### 8.7 — SAP BTP Job Scheduling (`sap-btp-job-scheduling`) + +BTP Job Scheduler service for cron-style jobs. Alternative / complement to Kyma CronJob CRDs. + +### 8.8 — SAP BTP Audit Log (`sap-btp-audit-log`) + +BTP Audit Log Service. Adopt as a destination for the local AuditLogEntry events; never replace the local audit log (defense-in-depth Category 4.1). + +### 8.9 — SAP BTP Master Data Integration (`sap-btp-master-data-integration`) + +MDI for BP / Product / Cost-Object distribution across systems. Use when the project needs master-data sync to S/4 / SuccessFactors / etc. + +### 8.10 — SAP CAP Best Practices (`sap-cap-best-practices`) + +Curated CAP patterns from SAP engineering. Use as a cross-check for Category 2 patterns when in doubt. + +### 8.11 — SAP PCE Expert (`sap-pce-expert`) + +S/4HANA Private Cloud Edition / RISE specifics. Use when the project consumes S/4HANA PCE / RISE (Tier-2 proxies have different host patterns and Communication Scenarios). + +### 8.12 — SAP Fiori Tools (`sap-fiori-tools`) + +Fiori Tools MCP: manifest validation, Fiori app discovery, page-template scaffolding. Use during Fiori Elements V4 setup or migration. + +### 8.13 — SAP CAP MCP (`sap-cap-capire` MCP server / `@sap/cds-mcp`) + +CDS model search, doc lookup. Use during exploration of an unfamiliar CAP project. + +### 8.14 — SAP Docs / SAP Notes (`sap-docs`, `sap-note-search`) + +Help portal, Notes search, `sap_search_objects` for Clean Core checks. Mandatory companion for any S/4HANA Tier-2 proxy work. + +### 8.15 — Context7 (`context7`) + +Generic library documentation lookup. Use for non-SAP dependencies (Node libraries, npm packages). + +### 8.16 — Playwright MCP (`playwright`) + +Browser automation for UI tests. Use during Fiori app smoke tests or visual regression checks. + +--- + +## How to Use This Skill + +### As a direct reference + +When the user asks "what's the best practice for X in CAP / Fiori Elements?", search this catalog for the pattern, cite it by anchor, and reference the companion skill if a deeper / operational answer is needed. + +### As a cross-link target + +Other skills in this repository cross-link to specific patterns by anchor: +- [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) refers to Category 1 for FE V4 traps. +- [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) refers to Category 4 for defense-in-depth. +- [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) refers to Category 5 for customizing patterns. +- [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) refers to Category 3.8 for multi-region deployment. +- [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) acts as orchestrator and consults this catalog when consolidating findings. + +### As a teaching index + +A developer new to CAP + Fiori Elements V4 can read Categories 1 → 8 in order. The categories progress from frontend → backend → deployment → cross-cutting concerns, mirroring the layers of a typical CAP project. + +## When NOT to use + +- For project-specific advice that depends on domain semantics (the patterns here are deliberately generic). +- For runnable diagnostics (use the operational skills like [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md)). +- As a replacement for SAP official documentation — this catalog distills production lessons, but the authoritative spec lives in the SAP help portal. + +## Recommended Companion Plugins + +Install whichever of these are available in your environment. Each is referenced by Category 8 above; consult the relevant entry when working in that area. + +| Plugin / Skill | npm / vercel-labs install | Domain | +|---|---|---| +| `sap-cap-capire` | `npx skills add SAP/sap-cap-capire` | CAP runtime knowledge base | +| `sapui5` | `npx skills add SAP/sap-ui5` | UI5 API explorer | +| `sap-fiori-tools` | `npx skills add SAP/sap-fiori-tools` | Fiori Elements scaffolding & validation | +| `sap-btp-cloud-platform` | `npx skills add SAP/sap-btp-cloud-platform` | BTP services map | +| `sap-btp-connectivity` | `npx skills add SAP/sap-btp-connectivity` | Destinations, Cloud Connector | +| `sap-btp-integration-suite` | `npx skills add SAP/sap-btp-integration-suite` | iFlow, API Management | +| `sap-btp-cloud-logging` | `npx skills add SAP/sap-btp-cloud-logging` | Observability | +| `sap-btp-job-scheduling` | `npx skills add SAP/sap-btp-job-scheduling` | Job Scheduler | +| `sap-btp-audit-log` | `npx skills add SAP/sap-btp-audit-log` | BTP Audit Log Service | +| `sap-btp-master-data-integration` | `npx skills add SAP/sap-btp-mdi` | MDI | +| `sap-cap-best-practices` | `npx skills add SAP/sap-cap-best-practices` | CAP curated patterns | +| `sap-pce-expert` | `npx skills add SAP/sap-pce-expert` | S/4 PCE / RISE specifics | +| `sap-docs` | `npx skills add SAP/sap-docs` | Help portal, Notes search | + +The exact plugin namespace (`SAP/...` vs `vercel-labs/...` vs `<community>/...`) depends on the package registry your tooling uses. Adapt the install commands to your environment. + +## References + +- [SAP CAP — Capire (Official Docs)](https://cap.cloud.sap/docs/) +- [SAP Fiori Elements V4 — Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) +- [SAP BTP — Reference Architectures](https://help.sap.com/docs/btp/sap-business-technology-platform/reference-architectures) +- [SAP API Release State Repository](https://github.com/SAP/abap-atc-cr-cv-s4hc) +- [OWASP — Application Security Verification Standard (ASVS)](https://owasp.org/www-project-application-security-verification-standard/) +- [Kyma — Runtime Documentation](https://kyma-project.io/docs/) +- [SAP UI5 — `flexEnabled` and Variant Management](https://sapui5.hana.ondemand.com/sdk/#/topic/465f01dcf1cd49b08230e7d3b53b29ed) diff --git a/skills/sap-cap-security-rbac-matrix/SKILL.md b/skills/sap-cap-security-rbac-matrix/SKILL.md index d9c1d2ae..584d434d 100644 --- a/skills/sap-cap-security-rbac-matrix/SKILL.md +++ b/skills/sap-cap-security-rbac-matrix/SKILL.md @@ -428,6 +428,32 @@ Related skills: - [sap-cap-customizing-honor](../sap-cap-customizing-honor/SKILL.md) — customizing coverage audit (complementary) - [migrate-custom-code](../migrate-custom-code/SKILL.md) — ABAP-side ATC fixes (companion for SAP custom code) +## Battle-Tested Patterns Referenced + +This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md), which catalogs gotchas distilled from production deployments. The patterns most relevant to a security/RBAC audit: + +- **Category 4 (entire) — Security Defense-in-Depth**: the seven multi-layer patterns are the foundation for the audit's compliance mapping (OWASP/ASVS/NIST/CIS/SAP-SOM). +- **4.1 Audit log append-only — three layers** (CDS + handler + DB trigger): every entity with an audit log must satisfy all three. +- **4.2 PII sanitization before audit log**: cross-check that the project has a centralized `_sanitizePII` helper; if missing, flag as HIGH. +- **4.3 Magic bytes verification on upload**: cross-check file-upload handlers — never trust `Content-Type` header. +- **4.4 Rate limiters per endpoint class**: verify global + write + upload + jobs + integration limiters all exist. +- **4.5 Token rotation quarterly**: ensure a runbook exists for every long-lived token (job, MCP, integration). +- **4.6 OData `$expand` depth + `$top` cap**: verify request preprocessing enforces both. +- **2.7 `xs-security.json` tenant-mode discipline**: single-tenant `dedicated` by default; `shared` requires deliberate review of data-layer isolation. + +## Recommended Companion Plugins + +| Plugin / Skill | Why for security audits | +|---|---| +| `sap-cap-capire` | CDS `@restrict` semantics, authentication/authorization patterns reference | +| `sap-btp-audit-log` | BTP Audit Log Service integration — adopt as destination for local AuditLogEntry events | +| `sap-btp-cloud-logging` | Cloud Logging dashboards for security event correlation | +| `sap-btp-connectivity` | XSUAA scope wiring, principal propagation, destination service auth | +| `sap-docs` | SAP Notes search for security advisories on consumed S/4 APIs | +| `context7` | OWASP / ASVS / NIST reference lookup for compliance mapping rationale | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. + ## References - [OWASP Top 10 2021](https://owasp.org/Top10/) diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md index f1c82483..dbb10409 100644 --- a/skills/sap-cap-stack-audit-full/SKILL.md +++ b/skills/sap-cap-stack-audit-full/SKILL.md @@ -362,6 +362,41 @@ The orchestrator logic is identical; expect more `TOOL UNAVAILABLE` flags on-pre - Findings tagged `TOOL UNAVAILABLE` indicate gaps in the local tooling, not in the project. - Critical (P1) findings should block release; High (P2) should have owners assigned; Medium (P3) belongs in the backlog. +## Battle-Tested Patterns Referenced + +This skill orchestrates the full audit stack and consults [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) as its **gotcha catalog**. The catalog's eight categories map to the orchestrator's phases: + +- **Category 1 (UI5 / FE V4 traps)** ↔ Phase 2a-2b (UI5 linter, manifest validation) and Phase 3 ([`sap-fiori-app-audit`](../sap-fiori-app-audit/SKILL.md)). +- **Category 2 (CAP / TypeScript pitfalls)** ↔ Phase 2e (TS typecheck), Phase 4 (CAP-specific deep checks). +- **Category 3 (BTP / Kyma deployment)** ↔ Phase 3 deployment-readiness agent. +- **Category 4 (Security defense-in-depth)** ↔ Phase 3 ([`sap-cap-security-rbac-matrix`](../sap-cap-security-rbac-matrix/SKILL.md)). +- **Category 5 (Customizing-driven)** ↔ Phase 3 ([`sap-cap-customizing-honor`](../sap-cap-customizing-honor/SKILL.md)). +- **Category 6 (Lifecycle/process discipline)** ↔ Phase 4 model integrity check. +- **Category 7 (Events/messaging)** ↔ Phase 3 architecture agent. +- **Category 8 (Ecosystem plugin landscape)** ↔ Phase 5 best-practice cross-check. + +Findings from any phase that match a battle-tested pattern should be cross-referenced in the consolidated report — link to the specific pattern by anchor (e.g. "matches battle-tested pattern 1.2 — `@UI.Hidden` on `OperationAvailable`"). + +## Recommended Companion Plugins + +The orchestrator works best when **all relevant companion plugins are installed** because Phase 3 specialized agents and Phase 5 best-practice cross-check rely on them. Adapt to what's available; degrade gracefully (mark `TOOL UNAVAILABLE`) for absent ones. + +| Plugin / Skill | Used in phase | +|---|---| +| `sap-cap-capire` | Phase 4 (model integrity, profile audit) | +| `sapui5` | Phase 2a (UI5 linter), Phase 3 (FE app audit) | +| `sap-fiori-tools` | Phase 2b (manifest validation), Phase 2c (Fiori app discovery) | +| `sap-btp-cloud-platform` | Phase 3 BTP best-practice agent | +| `sap-btp-connectivity` | Phase 3 (XSUAA / destination audit) | +| `sap-btp-cloud-logging` | Phase 3 logging-pattern check | +| `sap-btp-integration-suite` | Phase 3 (if iFlow consumption detected) | +| `sap-btp-audit-log` | Phase 3 audit-logging strategy review | +| `sap-pce-expert` | Phase 4 (if S/4HANA PCE consumed) | +| `sap-docs` | Phase 1 / Phase 3 SAP Notes search | +| `context7` | Phase 2e (non-SAP dependencies) | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map and `sap-btp-*` family entries. + ## References - [SAP CAP — Production-Readiness Guide](https://cap.cloud.sap/docs/guides/deployment/) diff --git a/skills/sap-cap-text-polish/SKILL.md b/skills/sap-cap-text-polish/SKILL.md index af90f5d5..45f63e26 100644 --- a/skills/sap-cap-text-polish/SKILL.md +++ b/skills/sap-cap-text-polish/SKILL.md @@ -374,6 +374,27 @@ The audit logic is identical; only the discovery of the PII helper varies. - Pair with [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) — its PII findings hand off to this skill's PII_RISK class. - Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to gate the regression of PII leaks and missing ICU placeholders in CI. +## Battle-Tested Patterns Referenced + +This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). The patterns most relevant to text polish: + +- **1.6 `i18n>{model>keyPath}` dynamic binding does not work** — common reason for blank text findings; flag as REWRITE with controller-side resolution. +- **1.9 `manifest.json` must declare the i18n model explicitly** — bundle gap diagnosis (Step 1e). +- **2.4 `cds.log(...)` everywhere; never `console.*`** — when the polish target includes operator-visible log strings, prefer `cds.log` calls. +- **2.5 Centralized reject helper (`rejectSafe`) — never expose `err.message`** — tied to TECH_LEAK classification (Step 2). +- **4.2 PII sanitization before audit log** — direct source of PII_RISK class; flag missing `_sanitizePII` helper as gap-to-add-helper. + +## Recommended Companion Plugins + +| Plugin / Skill | Why for text polish | +|---|---| +| `sap-cap-capire` | i18n model setup, localized messages, `cds.i18n` runtime reference | +| `sapui5` | `ResourceModel`, `_t` helper, `MessageBox` / `MessageToast` API details | +| `context7` | ICU MessageFormat placeholder syntax (`{0,number,#,##0.00}`, `{0,date,medium}`) | +| `sap-docs` | SAP Fiori Design Guidelines — Writing tone conventions | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. + ## References - [SAP UI5 — `ResourceModel` and `_t` Helper Pattern](https://sapui5.hana.ondemand.com/sdk/#/topic/91f217ce374d4dec8a4b08c8e4c0b3a4) diff --git a/skills/sap-fiori-app-audit/SKILL.md b/skills/sap-fiori-app-audit/SKILL.md index 92030c19..4078be27 100644 --- a/skills/sap-fiori-app-audit/SKILL.md +++ b/skills/sap-fiori-app-audit/SKILL.md @@ -350,6 +350,36 @@ The audit logic is identical; expect different manifest sap.platform values and - Pair with [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) when the audit reveals patterns that should be migrated to Fiori Elements V4. - Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to add CI gates that prevent regressions of the high-severity findings. +## Battle-Tested Patterns Referenced + +This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). The patterns most relevant to a Fiori app audit: + +- **1.1 Pin the UI5 minor version explicitly** — Step 2a `manifest.json` check; missing pin is a P2. +- **1.2 `@UI.Hidden` on `@Core.OperationAvailable` operand silently disables buttons** — Step 2e common UX-positive-false root cause. +- **1.3 Actions returning `Entity` must NOT use `.columns(...)`** — Step 2f refresh after action. +- **1.4 `liveMode: true` triggers `$batch` per keystroke on large entities** — Step 2a/2b filter audit. +- **1.5 Composition vs Association for audit/log child entities** — Step 2c draft behavior check. +- **1.6 `i18n>{model>keyPath}` dynamic binding does not work** — Step 2g i18n coverage. +- **1.7 Custom Facets must not collide with HeaderInfo auto-Facet IDs** — Step 2d Facets audit. +- **1.8 `@Common.ValueListWithFixedValues` duplicated crashes MDC** — Step 2b filter bar audit. +- **1.9 `manifest.json` must declare the i18n model explicitly** — Step 2g. +- **1.10 `@odata.draft.enabled` master data: per-case decision** — Step 2c draft audit per entity. +- **1.11 Status fields: use `sap.common.CodeList` not `String enum`** — Step 2b for filterable status columns. +- **3.10 `cds-plugin-ui5` is dev-only; ship UI separately** — Step 4 build verification. + +## Recommended Companion Plugins + +| Plugin / Skill | Why for Fiori app audits | +|---|---| +| `sapui5` | UI5 API explorer — verify control APIs, events, properties when validating annotations | +| `sap-fiori-tools` | Manifest validation MCP tool, Fiori app discovery, page-template alignment | +| `sap-cap-capire` | CAP service annotations reference (`@cds.redirection.target`, `@Common.SideEffects`) | +| `sap-docs` | Search SAP Notes for FE V4 / UI5 issues that match the audit findings | +| `playwright` | Browser smoke test for the "did the fix actually work in the UI" verification step | +| `context7` | Look up non-SAP libraries used in `webapp/ext/` (npm packages, build tools) | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. + ## References - [SAP Fiori Elements V4 — Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) From af76c55c5f19441f1858eb3836509d99851f52c0 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:15:39 +0200 Subject: [PATCH 06/18] feat(skills): target-aware deployment patterns + Clean Core as deploy gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Master skill `sap-cap-fiori-battle-tested-patterns` Category 3 expanded with the four canonical deployment targets, ~17 new patterns, and Clean Core as a CI-blocking deployment gate. Category 3 restructure: - 3.0 Deployment target decision matrix (BTP CF / BTP Kyma / OnPrem Kyma / OnPrem CF) with column for each: when-to-choose, CDS profile, auth, DB, UI delivery, remote service binding. - 3.A BTP Cloud Foundry — 5 patterns: mta.yaml atomic deploy, xs-security scope wiring, Destination service for Tier-2 S/4 proxies, html5-apps-repo (Free plan limits), CF health check endpoints. - 3.B BTP Kyma — 7 patterns: APIRule CRD (not Ingress YAML), Service Manager bindings via secrets, CronJob CRD (not BTP Job Scheduler), Bitnami PG Helm chart for in-cluster persistence, KYMA_APP_URL + KYMA_KUBECONFIG multi-region pattern, HPA + PDB + NetworkPolicy, approuter session stickiness via nginx cookie affinity. - 3.C On-Premise Kyma — 6 patterns: cluster flavor detection (k3d/Rancher/ Gardener/OpenShift), Keycloak realm + customer IdP federation, secrets.env + envsubst per-customer config, sizing wizard (xs/s/m/l tiers), pre-flight check (Docker RAM, DNS, storage class), in-cluster service URLs (*.svc.cluster.local). - 3.D On-Premise CF — 1 pattern: legacy continuity only, EOL warning, shim list. - 3.1-3.10 cross-cutting patterns preserved. - 3.11 NEW — Clean Core Level A as deployment gate (not just audit). Cites the three authoritative sources: SAP/abap-atc-cr-cv-s4hc README, SAP API Hub (api.sap.com), project compatibility catalog. Hard-fail CI semantics. Cross-links to sap-cap-clean-core-enforce + ci-gates-pattern Pattern 3. Orchestrator `sap-cap-stack-audit-full` gets a new Step 0: - 0a Auto-detect deployment target from project state (mta.yaml, k8s manifests, CDS profile, auth indicators). - 0b If ambiguous, ask the user explicitly — never guess; the wrong target produces inapplicable advice. - 0c Persist target as $TARGET; subsequent phases dispatch differently per target (Phase 2 manifest validation, Phase 3 agent selection, Phase 4 profile filter, Phase 5 best-practice set). - Pre-flight Clean Core catalog detection added — warning emitted when absent so the dispatch of sap-cap-clean-core-enforce knows to run in discovery mode only. Rationale: prior to this commit the master skill's deployment guidance was target-agnostic, leading to advice that applied somewhere but not always (e.g. "use html5-apps-repo" → broken on Kyma free; "use APIRule" → broken on BTP CF). Making target explicit is a precondition for any actionable deployment guidance. Clean Core gate elevation: previously documented as "an audit you might want to run". Now codified as a HARD pre-deployment gate consulting three authoritative sources. This is the single most important deployment gate for CAP + S/4 projects — a non-released service that ships becomes a multi-quarter migration debt. [skip ci] --- .../SKILL.md | 307 +++++++++++++++++- skills/sap-cap-stack-audit-full/SKILL.md | 57 +++- 2 files changed, 362 insertions(+), 2 deletions(-) diff --git a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md index 5a42ac79..e79c1333 100644 --- a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md +++ b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md @@ -250,7 +250,290 @@ Add `npm run typecheck:strict`. Promote folders one at a time, lock them in CI w --- -## Category 3 — BTP / Kyma Deployment Lessons +## Category 3 — BTP / Kyma / On-Premise Deployment Lessons + +CAP + Fiori Elements V4 projects deploy across **four canonical target environments**, each with distinct constraints around authentication, persistence, UI delivery, remote service binding, and operational tooling. Patterns 3.A.* are target-specific; patterns 3.1-3.10 are cross-cutting (apply regardless of target). + +> **Always ask the deployment target first.** The auditor / orchestrator / generator skill **MUST** ask the user (or detect from project state) which of the four targets is in scope before generating manifests, recommending CDS profiles, or producing deployment artifacts. The wrong target produces unsalvageable advice (e.g. `html5-apps-repo` binding on Kyma → 401; `cds run` on a project expecting `cds watch` → blank UIs). + +### 3.0 — Deployment target decision matrix + +| Target | When to choose | CDS profile | Auth | DB | UI delivery | Remote services | +|---|---|---|---|---|---|---| +| **BTP Cloud Foundry** | Customer is fully BTP-managed, has CF entitlements, accepts BTP-managed services (Free or pay) | `production` (HANA HDI) or `production-pg` (PostgreSQL, deprecated 2026-Q4) | XSUAA | HANA HDI or BTP PostgreSQL service | `@sap/html5-app-repo` (Free plan OK) + approuter | BTP Destination service + Cloud Connector | +| **BTP Kyma** | Customer wants Kubernetes operational model, BTP-hosted but more flexible than CF, uses pay-tier or in-cluster services | `k8s` | OIDC (XSUAA or IAS via OAuth2) | PostgreSQL in-cluster (Bitnami Helm) or HANA Cloud | UI ZIPs embedded in approuter Docker image | `S4_BASE_URL` + Destination via Kyma BTP Operator | +| **On-Premise Kyma** | Customer-managed cluster (k3d local, Rancher, Gardener, OpenShift), uses customer IdP (Keycloak), needs full data sovereignty | `k8s-onprem` (Keycloak + HANA/PG) or `k8s-hana` (Kyma + HANA on-prem) | OIDC via customer IdP (Keycloak, Active Directory) | HANA on-prem or PostgreSQL on-prem | UI ZIPs embedded; ingress via NGINX or cluster-native | `*.svc.cluster.local` for in-cluster S/4 proxies + Cloud Connector for SaaS bridges | +| **On-Premise CF** | Customer runs CF on-prem (rare; SAP Cloud Foundry On-Premise is EOL) — only if existing investment | `onprem` | XSUAA on-prem | HANA on-prem | html5-apps-repo on-prem | S/4 Flex Workflow + SMTP + filesystem DMS | + +The default recommendation for a new project is **BTP Kyma** (cloud-native, pay-as-you-go, broadest customer reach). **BTP CF** when the customer mandate is "managed services only, no Kubernetes operations". **On-Premise Kyma** when the customer mandates data sovereignty or has existing Kubernetes infrastructure. **On-Premise CF** only for legacy continuity. + +### 3.A — BTP Cloud Foundry patterns + +#### 3.A.1 — Use `mta.yaml` for atomic deployment + +**Symptom.** Half-deployed state when a component fails: db deployer succeeded, srv failed, html5-apps-repo not yet wired. + +**Root cause.** Manual `cf push` per component doesn't atomically roll back on failure. + +**Remedy.** Wrap everything in `mta.yaml`: db deployer, srv module, approuter, destinations, XSUAA service binding. Use `mbt build` + `cf deploy <archive>.mtar`. Failures roll back the whole MTA. + +#### 3.A.2 — XSUAA scope wiring via `xs-security.json` + +**Symptom.** Role assignments work in dev but production users get 403 on actions that worked in test. + +**Root cause.** `xs-security.json` declares scopes; CAP handlers must `@(restrict: [{grant: '...', to: '<scope-or-role>'}])` to enforce. Mismatch between xs-security and `services-auth.cds` is silent. + +**Remedy.** Treat `xs-security.json` as the single source of truth for scope names. Match every `services-auth.cds` `to:` clause to a declared scope. Add a CI gate (see [`../sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 4) that catches drift. + +#### 3.A.3 — Destination service for S/4HANA Tier-2 proxies + +**Symptom.** S/4 proxy calls fail with 401 in production despite working in dev with `.cdsrc-private.json`. + +**Root cause.** Dev profile uses inline credentials; production must use a Destination configured in BTP cockpit + bound to the srv module via destination service. + +**Remedy.** Define one BTP Destination per S/4 system. Bind the destination service to srv. Use principal propagation (SAML / JWT bearer) when end-user identity must reach S/4; use system user (BasicAuthentication or OAuth2ClientCredentials) for batch / job calls. + +#### 3.A.4 — html5-apps-repo for UI delivery (Free plan acceptable) + +**Symptom.** Approuter image is 600 MB because it ships every UI app embedded. + +**Root cause.** On CF, you can use the managed UI repo instead of embedding. + +**Remedy.** Use `@sap/html5-app-repo` Free or pay-tier. Build UI bundles via `ui5 build`, upload via `npm run upload-html5-apps`. Approuter routes `/<app-id>/*` to the repo. Free plan has limits (5 apps, 50 MB total) — verify against your project size before committing. + +#### 3.A.5 — Cloud Foundry health checks + +**Symptom.** App restarts often even when running fine. + +**Root cause.** Default CF health check is HTTP `/`. Many CAP services return 404 on `/` (no homepage). + +**Remedy.** In `manifest.yml`: `health-check-type: http`, `health-check-http-endpoint: /health/Live`. Pair with the `/health/Ready` endpoint for the proper liveness/readiness split (cross-cutting pattern 3.7). + +### 3.B — BTP Kyma patterns + +#### 3.B.1 — APIRule (CRD) for ingress, not Ingress YAML + +**Symptom.** Direct Ingress YAML works in dev but doesn't integrate with BTP IAM / Kyma identity. + +**Root cause.** Kyma's networking pipeline uses `APIRule` (custom CRD) that wires JWT validation, CORS, rate limiting natively. + +**Remedy.** For each public service, declare an `APIRule`: +```yaml +apiVersion: gateway.kyma-project.io/v1beta1 +kind: APIRule +metadata: + name: srv +spec: + host: srv.${KYMA_APP_URL} + service: + name: srv + port: 4004 + gateway: kyma-system/kyma-gateway + rules: + - path: /.* + methods: [GET, POST, PUT, PATCH, DELETE] + accessStrategies: + - handler: jwt + config: + jwks_urls: + - https://${XSUAA_HOST}/token_keys + trusted_issuers: + - https://${XSUAA_HOST} +``` +For approuter, add `noAuth` accessStrategy on the OAuth2 callback paths. + +#### 3.B.2 — Service Manager bindings via secrets (not env) + +**Symptom.** Refactoring credentials breaks the running pod because env vars don't reload. + +**Root cause.** Kyma's BTP Service Operator creates `Secret` resources on `ServiceBinding` apply; pod mounts them as volume + env. + +**Remedy.** Use `ServiceInstance` + `ServiceBinding` CRDs. Don't hardcode credentials in `Deployment.env`; reference the Secret by name. Pod restart picks up rotated credentials automatically (with proper `restartPolicy`). + +#### 3.B.3 — CronJob CRD for scheduled jobs, not BTP Job Scheduler + +**Symptom.** BTP Job Scheduler binding fails (or costs extra) on Kyma. + +**Root cause.** BTP Job Scheduler is a CF-friendly service; Kyma-native is `CronJob` CRD. + +**Remedy.** Declare every scheduled job as a Kyma `CronJob` pointing at a job-runner endpoint: +```yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: <job-name> +spec: + schedule: "0 */6 * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: trigger + image: curlimages/curl:8.5.0 + command: ["sh", "-c", "curl -fsS -X POST -H 'X-Job-Token: $(JOB_TOKEN)' http://srv:4004/api/jobs/<job-name>"] + env: [{name: JOB_TOKEN, valueFrom: {secretKeyRef: {name: job-token, key: token}}}] + restartPolicy: OnFailure +``` +The srv module exposes job endpoints (token-protected). Cron triggers via HTTP. + +#### 3.B.4 — Bitnami PostgreSQL Helm chart for in-cluster persistence + +**Symptom.** BTP PostgreSQL Free plan refuses Kyma binding (CF-only). + +**Root cause.** Free-tier BTP services don't route into Kyma. + +**Remedy.** Install Bitnami PostgreSQL via Helm: `helm install pg bitnami/postgresql -n <ns> -f values-pg.yaml`. PVC-backed, single-instance OK for non-critical workloads; for production use Patroni Helm chart for HA. Connection details via Secret. + +#### 3.B.5 — `KYMA_APP_URL` + `KYMA_KUBECONFIG` GitHub Actions secrets for multi-region + +**Symptom.** Deploying to a second region requires forking the CI pipeline. + +**Root cause.** Hardcoded hostnames in workflow YAML and Kubernetes manifests. + +**Remedy.** Parameterize via 2 GitHub Actions secrets: `KYMA_APP_URL` (cluster-specific subdomain) + `KYMA_KUBECONFIG`. Use `envsubst` in the deployment workflow: +```yaml +- run: kubectl apply -f <(envsubst < k8s/deployment-srv.yaml) +``` +Region switch = rotate the two secrets + update DNS CNAME. Zero code change. + +#### 3.B.6 — HorizontalPodAutoscaler + PodDisruptionBudget for production + +**Symptom.** Pod evictions during cluster upgrades cause unavailability spikes. + +**Root cause.** Default `Deployment` has no HPA / PDB; cluster upgrades evict pods in parallel. + +**Remedy.** Pair every production `Deployment` with: +- `HorizontalPodAutoscaler` (min: 2, max: N, targetCPU: 70%) for capacity. +- `PodDisruptionBudget` (minAvailable: 1) so cluster upgrades drain pods serially. +- `NetworkPolicy` to restrict in-cluster traffic (default-deny + explicit allow). + +#### 3.B.7 — Approuter session stickiness via nginx cookie affinity + +**Symptom.** FE V4 batch requests fail intermittently with CSRF token mismatch (403 / 419). + +**Root cause.** Kyma's default load balancer round-robins across approuter pods; CSRF tokens are pod-local. + +**Remedy.** Cookie-based affinity on the Kyma `APIRule`: +```yaml +metadata: + annotations: + nginx.ingress.kubernetes.io/affinity: "cookie" + nginx.ingress.kubernetes.io/session-cookie-name: "route" + nginx.ingress.kubernetes.io/session-cookie-max-age: "172800" +``` +Never disable CSRF (`csrfProtection: false`) — UI5 + FE V4 depend on it. This is the same as cross-cutting Pattern 3.3 but worth re-stating in the Kyma section. + +### 3.C — On-Premise Kyma patterns + +#### 3.C.1 — Cluster flavor matters: k3d / Rancher / Gardener / OpenShift + +**Symptom.** Manifests that work on BTP Kyma fail on the customer's on-prem cluster. + +**Root cause.** Kyma CRDs (APIRule, ServiceInstance) may not be present; cluster may use Istio differently; storage class names differ. + +**Remedy.** Detect the flavor upfront: +- **k3d** (local dev): no APIRule CRD; use plain `Ingress` + nginx-ingress controller. +- **Rancher**: usually has Istio; APIRule may need install of Kyma networking module. +- **Gardener** (SAP-managed customer): Kyma module typically installed; treat like BTP Kyma. +- **OpenShift**: Routes instead of Ingress; SCC (Security Context Constraints) may block non-root containers. + +Provide two manifest sets: a "Kyma-native" (APIRule + ServiceInstance) and a "Kubernetes-vanilla" (Ingress + Secret). + +#### 3.C.2 — Customer IdP via Keycloak realm + +**Symptom.** BTP IAM (XSUAA / IAS) doesn't reach the customer's on-prem network. + +**Root cause.** On-prem deployment cannot consume BTP-managed auth services. + +**Remedy.** Provide a Keycloak realm definition (`keycloak-realm.json`) with: +- Roles matching the CAP `services-auth.cds` scopes (Viewer, Admin, etc.). +- Groups mapping to roles. +- OAuth2 client for the approuter with redirect URI = `https://<app-host>/login/callback`. +- Optional: federate with customer's Active Directory / LDAP / SAML2 IdP. + +In CAP, use `OIDCAuthStrategy` (the project's custom strategy that validates JWTs from a configurable issuer): +```yaml +# k8s-onprem profile +cds.requires.auth: + kind: jwt-auth + issuer: https://keycloak.${CUSTOMER_DOMAIN}/realms/<realm> + jwks_uri: https://keycloak.${CUSTOMER_DOMAIN}/realms/<realm>/protocol/openid-connect/certs +``` + +#### 3.C.3 — `secrets.env` + `envsubst` pattern for per-customer config + +**Symptom.** Each customer needs different DB endpoint / IdP URL / S/4 hostname; copy-paste manifests are error-prone. + +**Root cause.** Kubernetes ConfigMaps / Secrets don't templatize across deployments. + +**Remedy.** Per-customer `secrets.env` file (one source of truth), checked into customer-specific repo only. Installer script does: +```bash +set -a +source secrets.env +set +a +envsubst < k8s/onprem/configmap.yaml | kubectl apply -f - +envsubst < k8s/onprem/deployment-srv.yaml | kubectl apply -f - +``` +`secrets.env` contains: `DB_HOST`, `DB_USER`, `S4_BASE_URL`, `KEYCLOAK_REALM_URL`, `CUSTOMER_DOMAIN`, etc. + +#### 3.C.4 — Sizing wizard: customer hardware tier (xs / s / m / l) + +**Symptom.** Customer with 2 GB cluster RAM crashes the pod on first request. + +**Root cause.** Default resource requests/limits sized for cloud (4-8 GB pod RAM); on-prem dev clusters may have far less. + +**Remedy.** Document four sizing tiers in the deployment manifests with `kustomize` or Helm overlay: +| Tier | Pod RAM request | Pod CPU request | Replicas | Use case | +|---|---|---|---|---| +| **xs** | 512 Mi | 250 m | 1 | dev / proof-of-concept, ≤10 concurrent users | +| **s** | 1 Gi | 500 m | 2 | small customer, ≤50 concurrent users | +| **m** | 2 Gi | 1000 m | 3 | medium customer, ≤200 concurrent users | +| **l** | 4 Gi | 2000 m | 4+ HPA | large customer, ≤1000 concurrent users | + +For dev clusters (`xs` tier) optionally enable `LOW_MEM_MODE=true` env var that disables expensive features (less aggressive caching, smaller worker pools). **Document as DEV-ONLY** — customers in production must use the appropriate tier from the wizard. + +#### 3.C.5 — Pre-flight check before install + +**Symptom.** Installer halfway in fails because Docker / RAM / DNS / kubectl version is insufficient. + +**Root cause.** Cluster pre-conditions not verified upfront. + +**Remedy.** First step of the installer script does a comprehensive pre-flight: +```bash +# Pre-flight +check_docker_running || die "Docker not running" +check_docker_ram_gb 4 || die "Need ≥4 GB Docker RAM" +check_kubectl_version "1.28" || die "Need kubectl ≥1.28" +check_dns_resolve "$KEYCLOAK_HOST" || die "Cannot resolve Keycloak host" +check_curl_works "$S4_BASE_URL" || warn "S/4 hostname unreachable (Cloud Connector down?)" +check_storage_class_exists "$STORAGE_CLASS" || die "Storage class missing" +``` +Exit cleanly with actionable error before consuming time / state. + +#### 3.C.6 — In-cluster service URLs (`*.svc.cluster.local`) + +**Symptom.** S/4 proxy host hardcoded as a public hostname doesn't reach in-cluster S/4 proxy. + +**Root cause.** On-prem clusters may run their own S/4 OData proxy as a sidecar service. + +**Remedy.** Configure `S4_BASE_URL` (or equivalent) to use Kubernetes service DNS: `http://s4-proxy.s4-namespace.svc.cluster.local:8080`. The CAP runtime resolves it cluster-internally without exiting to public DNS. Faster, more secure, no Cloud Connector needed for in-cluster paths. + +### 3.D — On-Premise CF patterns + +#### 3.D.1 — On-prem CF is EOL — only for legacy continuity + +**Symptom.** Customer mandates CF on-prem because that's what they invested in 2018. + +**Root cause.** SAP Cloud Foundry On-Premise reached end-of-maintenance; community equivalents (CF Open Source) lack the SAP integration shims. + +**Remedy.** Surface this as a strategic finding to the customer. If continuity is non-negotiable, the `onprem` profile bundles: +- XSUAA on-prem (legacy installation). +- HANA on-prem. +- S/4 Flex Workflow (instead of BPA). +- SMTP relay (instead of BTP notifications). +- Filesystem DMS (instead of cloud-based document storage). +Document each shim's substitution explicitly in the project README so the customer is aware of the divergence from the BTP feature set. ### 3.1 — `cds run` does NOT mount UI5 apps @@ -367,6 +650,28 @@ Map `livenessProbe` → `/health/Live`, `readinessProbe` → `/health/Ready`. - Local dev → `cds watch` + `cds-plugin-ui5` serves apps from `app/<app>/webapp/`. - Production → approuter serves apps from `dist/` (post-`ui5 build`) bundled into the Docker image. +### 3.11 — Clean Core Level A as **deployment gate**, not just an audit + +**Symptom.** Production deployment succeeds; weeks later a consumed S/4HANA Communication Scenario is deprecated; the next quarterly upgrade window breaks the app silently. + +**Root cause.** Clean Core compliance was audited once at design time and not re-verified pre-deployment. The audit's findings live in a markdown report that nobody reads on the day of deploy. + +**Remedy.** Make Clean Core Level A verification a **CI gate that blocks deployment**, not a passive report. Three authoritative sources MUST be consulted: + +1. **The ABAP API Release State repository** (`SAP/abap-atc-cr-cv-s4hc`): https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md. This repo's `*.json` files are the **source of truth** for whether an ABAP object (CDS view, BAPI, RFC FM) is released — and for which edition (Public Cloud / Private Cloud / On-Premise). Pin a `git submodule` or scheduled-clone snapshot of this repo into the project; re-pull weekly via cron. + +2. **The SAP API Hub** (https://api.sap.com/): authoritative for **OData service availability per edition** (Communication Scenarios, packages, lifecycle states). Cross-check every `cds.connect.to(<remote-service>)` against the API Hub; record the edition matrix in the project's compatibility catalog. + +3. **The project's compatibility catalog** (e.g. `srv/integration/s4CompatibilityPolicy.js`): the project's *declared* edition × service matrix with `availability[]` and `probeObject` fields. The CI gate verifies this catalog matches sources 1 + 2; any drift is a HARD FAIL (cannot ship). + +Use [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) to discover consumption, build the matrix, detect drift. Use [`../sap-cap-ci-gates-pattern/SKILL.md#pattern-3--released-state--api-availability-drift-detection`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 3 to enforce the gate on every PR. + +Compliance contract: +- **Level A** (Released APIs only): every consumed Tier-2 S/4 service is in the released catalog for ALL deployment-target editions. Zero RFC/BAPI/SQL-direct usage. Zero modifications to standard tables. Zero user-exits. +- **Drift detected** ⇒ CI fails. Choices: (a) remove the consumption, (b) replace with the released equivalent, (c) accept and document a Level B/C/D exception (rare, needs sign-off). + +This is the **single most important** deployment gate for CAP + S/4 projects. A non-released service that lands in production becomes a multi-quarter migration debt the next time SAP revises the API surface. + --- ## Category 4 — Security Defense-in-Depth diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md index dbb10409..102d9c9a 100644 --- a/skills/sap-cap-stack-audit-full/SKILL.md +++ b/skills/sap-cap-stack-audit-full/SKILL.md @@ -49,6 +49,57 @@ Single optional argument: Examples: (no arg) → full · `ui5` · `cap` · `quick` · `manager-ui` · `srv/handlers`. +## Step 0: Ask the deployment target + +**Before any other audit step**, the orchestrator MUST identify the deployment target. Findings, recommendations and manifest checks differ substantially between targets; running the audit without knowing the target produces inapplicable advice. + +### 0a — Try to auto-detect from project state + +```bash +# BTP CF signature +test -f mta.yaml && echo "Detected: BTP Cloud Foundry (mta.yaml present)" + +# BTP Kyma signature +ls k8s/*.yaml 2>/dev/null | head -1 && echo "Detected: BTP Kyma or On-Prem Kyma (k8s/ manifests present)" + +# Discriminator within Kyma flavors +grep -lE "kyma-project\.io/v1beta1|gateway\.kyma-project\.io" k8s/*.yaml 2>/dev/null | head -1 && \ + echo " → APIRule CRD used: likely BTP Kyma or Gardener/Rancher with Kyma module" +grep -lE "ingress\.kubernetes\.io|networking\.k8s\.io" k8s/*.yaml 2>/dev/null | head -1 && \ + echo " → vanilla Ingress used: likely on-prem k3d / OpenShift / vanilla Kubernetes" + +# CDS profile clues (.cdsrc.json or package.json) +grep -oE '"(production|production-pg|k8s|k8s-hana|k8s-onprem|onprem)"' .cdsrc*.json package.json 2>/dev/null | sort -u + +# Auth indicator +test -f xs-security.json && echo " → XSUAA (BTP CF or On-Prem CF)" +ls k8s/onprem/keycloak-realm.json k8s/keycloak/*.json 2>/dev/null && echo " → Keycloak (On-Prem Kyma)" +``` + +### 0b — Ask the user when ambiguous + +If auto-detection is inconclusive or yields multiple candidates, ask the user explicitly: + +> **Which deployment target are we auditing?** +> +> 1. **BTP Cloud Foundry** — XSUAA + HANA HDI + html5-apps-repo + mta.yaml +> 2. **BTP Kyma** — XSUAA/IAS via OIDC + PostgreSQL in-cluster or HANA Cloud + APIRule CRD + Kyma BTP Operator +> 3. **On-Premise Kyma** — Customer IdP (Keycloak) + HANA on-prem or PostgreSQL on-prem + cluster flavor (k3d / Rancher / Gardener / OpenShift) + `*.svc.cluster.local` remotes +> 4. **On-Premise CF** — legacy continuity only — XSUAA on-prem + S/4 Flex Workflow + SMTP + filesystem DMS + +Do **not** guess. The audit's per-target Phase 3 dispatches differ; getting this wrong wastes the agent budget. + +### 0c — Persist the target for downstream phases + +Record the chosen target as `$TARGET` (one of `btp-cf`, `btp-kyma`, `onprem-kyma`, `onprem-cf`). Every subsequent phase consults `$TARGET`: + +| Phase | What changes with target | +|---|---| +| Phase 2 static checks | UI5 linter / manifest validation always; on Kyma also check APIRule / NetworkPolicy / HPA / PDB | +| Phase 3 specialized agents | BTP CF → BTP best-practices + deployment-readiness against mta.yaml; Kyma → Kyma readiness + APIRule audit + Bitnami PG verification; On-prem → Keycloak realm coherence + sizing tier match | +| Phase 4 CAP-specific | Profile discovery filters to relevant profiles (drop CF-only profiles if target is Kyma, etc.) | +| Phase 5 best-practice cross-check | Different DO/DON'T sets cited (e.g. Free Plan CF-only warning is irrelevant on a Kyma audit if customer is pay-tier) | + ## Step 1: Pre-flight Always runs, ~30 s. @@ -72,9 +123,13 @@ echo "Detected $APPS Fiori app(s)" # CDS compile sanity (entry-point auto-discovery) SRV=$(grep -lE "^service\s+\w+" srv/*.cds | head -1) test -n "$SRV" && npx cds compile srv app --service "$(grep -oE '^service\s+\w+' "$SRV" | head -1 | awk '{print $2}')" --to edmx > /tmp/audit-svc.edmx 2>&1 && echo "CDS OK" || echo "CDS FAIL" + +# Clean Core Level A pre-flight (always; this is a deployment gate, not optional) +test -f srv/integration/s4*Policy* || \ + echo "WARN: no S/4 compatibility catalog found — Clean Core Level A audit will run discovery-mode only (see Step 3 dispatch of sap-cap-clean-core-enforce)" ``` -Output of Phase 1: branch SHA, dirty files, project signature (CAP yes/no, Fiori apps count, CDS compile status, primary service name). +Output of Phase 1: branch SHA, dirty files, project signature (CAP yes/no, Fiori apps count, CDS compile status, primary service name), **deployment target** chosen in Step 0, Clean Core catalog presence flag. If CDS compile fails, **stop the audit** and emit a single-finding report — no point auditing on top of a broken model. From 0946c1d091d75eba69d0bec46365f5d76257cc82 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:18:20 +0200 Subject: [PATCH 07/18] feat(skills): Clean Core enforcement mode + dual authoritative sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strengthens `sap-cap-clean-core-enforce` from a read-only audit into a proper deployment gate. Step 4 (Matrix verification) — now consults two authoritative sources in priority order: 1. SAP/abap-atc-cr-cv-s4hc — the ABAP API Release State repository (JSON files per object class). Authoritative for ABAP object classification (released / Clean Core level / per-edition availability). Already queried via mcp-sap-docs. 2. api.sap.com — the SAP API Hub. Authoritative for OData service lifecycle (released / sandbox / deprecated), Communication Scenario membership, version history, deprecation timeline. Queried via mcp-sap-docs:sap_search_objects OR scraped via Apify Website Content Crawler into a refreshable offline cache. 3. Project's own compatibility catalog (e.g. srv/integration/s4CompatibilityPolicy.js) — authoritative only for the *declared* claim, which the audit verifies against sources 1 + 2. When sources 1 and 2 disagree: API Hub wins for OData service questions; abap-atc repo wins for raw ABAP object questions. Resolution rationale is recorded in the report. New Step 9 — Enforcement mode (`--enforce` flag): | Mode | Exit on HIGH | Exit on MED | Exit on LOW | Use case | |------------------|--------------|-------------|-------------|----------| | report (default) | 0 | 0 | 0 | Audit | | --apply | 0 | 0 | 0 | Audit+fix| | --enforce | 1 | 0 (warn) | 0 (info) | CI gate | | --enforce --strict | 1 | 1 | 0 | Tightest | HIGH severity criteria (gate-blocking): - Consumed service NOT released for the deployment target's edition. - Consumed service deprecated with sunset inside the release window. - Catalog declares availability that MCP-verification refutes (OVER_DECLARED drift). - Direct DB-table or RFC/BAPI consumption against non-released artifact. CI integration patterns (GitHub Actions YAML example) included; pairs with sap-cap-ci-gates-pattern Pattern 3 for the full wiring (per-PR fast feedback + per-deploy safety net). Sources refresh discipline: weekly cron pulls fresh abap-atc snapshot; weekly Apify scrape of api.sap.com package pages. Stale (>7d) cache is treated as a finding. Drift caused by SAP-side changes is flagged as "upstream drift" with a 90-day window to remediate. Side-by-side extension fallback: when --enforce blocks on a genuinely needed non-released service, the finding is reclassified as a "side-by-side extension candidate" — the consumption belongs in a separate extension service on BTP (or out of ABAP entirely), not in the CAP service that ships through the gate. Frontmatter description updated to surface the enforcement semantics in discovery patterns ("enforce Clean Core in CI", "block deployment if non-released API is consumed"). References section reorganized: primary authoritative sources called out explicitly (abap-atc-cr-cv-s4hc README pinned link + api.sap.com), methodology references kept, related skills cross-linked (sap-cap-ci-gates-pattern Pattern 3 + battle-tested-patterns §3.11). [skip ci] --- skills/sap-cap-clean-core-enforce/SKILL.md | 90 ++++++++++++++++++++-- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/skills/sap-cap-clean-core-enforce/SKILL.md b/skills/sap-cap-clean-core-enforce/SKILL.md index 0d1c9573..58b60e8f 100644 --- a/skills/sap-cap-clean-core-enforce/SKILL.md +++ b/skills/sap-cap-clean-core-enforce/SKILL.md @@ -1,15 +1,15 @@ --- name: sap-cap-clean-core-enforce -description: Discovery-driven Clean Core Level A enforcement audit for SAP CAP + S/4HANA projects. Scans `cds.connect.to()` runtime calls + `@cds.external` services, identifies SAP-released probe objects, builds an availability matrix (Public Cloud × Private Cloud × On-Premise) via `mcp-sap-docs`, detects catalog drift versus the project's declared compatibility policy, and suggests SAP-released replacements for non-released references. Use when asked to "verify Clean Core Level A compliance", "audit S/4 API usage", "check which S/4 services we consume are released", "are we Clean Core compliant on BTP", or "build a Clean Core compliance matrix". +description: Discovery-driven Clean Core Level A enforcement gate for SAP CAP + S/4HANA projects. Scans `cds.connect.to()` runtime calls + `@cds.external` services, identifies SAP-released probe objects, builds an availability matrix (Public Cloud × Private Cloud × On-Premise) by consulting two authoritative sources (the SAP API release-state repository `SAP/abap-atc-cr-cv-s4hc` + the SAP API Hub at api.sap.com), detects catalog drift versus the project's declared compatibility policy, and supports an `--enforce` mode that fails CI on HIGH findings to block deployment of non-released consumption. Use when asked to "verify Clean Core Level A compliance", "audit S/4 API usage", "check which S/4 services we consume are released", "enforce Clean Core in CI", "build a Clean Core compliance matrix", or "block deployment if non-released API is consumed". --- # SAP CAP Clean Core Level A Enforcement -Discovery-driven Clean Core Level A compliance audit for SAP CAP + S/4HANA projects. This skill **scans your CAP codebase for S/4 API consumption**, **verifies each service against the SAP API release-state repository** ([`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc)) on all three editions (Public Cloud, Private Cloud / RISE, On-Premise), and produces a **compliance matrix** that can be checked into the repo as compliance evidence. +Discovery-driven Clean Core Level A enforcement for SAP CAP + S/4HANA projects. This skill **scans your CAP codebase for S/4 API consumption**, **verifies each service against two authoritative sources** — the SAP API release-state repository ([`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md)) and the [SAP API Hub](https://api.sap.com/) — on all three editions (Public Cloud, Private Cloud / RISE, On-Premise), and produces a **compliance matrix** that can be checked into the repo as compliance evidence. With `--enforce`, the matrix becomes a **CI-blocking deployment gate**. -Unlike [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) which classifies **ABAP custom code on the SAP side** into Levels A-D, this skill classifies **the BTP CAP application's outbound S/4 API consumption** — useful when the CAP app is the consumer and the question is "are all S/4 APIs we call actually released?". +Unlike [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) which classifies **ABAP custom code on the SAP side** into Levels A-D, this skill classifies **the BTP CAP application's outbound S/4 API consumption** — useful when the CAP app is the consumer and the question is "are all S/4 APIs we call actually released, on all editions we deploy to?". -Read-only audit, idempotent, ~5 minute run, zero side-effects on either the source SAP system or the target CAP project (unless `--apply` mode is selected). +Default mode is **`report`** (read-only audit, idempotent, ~5 minute run). Opt into **`--apply`** for safe additive catalog corrections, or **`--enforce`** for CI-gating semantics (non-zero exit on HIGH findings). ## Smart Defaults (apply silently, do NOT ask) @@ -159,6 +159,12 @@ The skill continues without crashing — untraceable services are flagged but no ## Step 4: Matrix MCP-Verified (3 editions × N services) +The matrix verification consults **three authoritative sources** in priority order. Each cell of the matrix `(service × edition)` is filled from the first source that returns a definitive answer: + +1. **`SAP/abap-atc-cr-cv-s4hc`** — the [ABAP API Release State repository](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md). JSON files per object class; updated by SAP product teams. **Authoritative for**: whether an ABAP object (CDS view, BAPI, RFC FM, table) is released-for-cloud-development and at what Clean Core Level. Queried via `mcp-sap-docs:sap_get_object_details` which wraps the repo. +2. **`api.sap.com`** — the [SAP API Hub](https://api.sap.com/). **Authoritative for**: OData service lifecycle (released / sandbox / deprecated), Communication Scenario membership, version history, deprecation timeline. Queried via `mcp-sap-docs:sap_search_objects` and `WebFetch` against the API Hub HTML pages, OR scraped offline via Apify Website Content Crawler into a refreshable cache. +3. **Project's own compatibility catalog** (e.g. `srv/integration/s4CompatibilityPolicy.js`). **Authoritative only for**: the project's *declared* availability claim — which the audit verifies against sources 1 + 2. + For each `(service, probe_object)` identified, issue **3 parallel MCP queries** — one per edition: ``` @@ -170,6 +176,8 @@ mcp-sap-docs:sap_get_object_details( ) ``` +Cross-check the abap-atc result against the API Hub for the matching Communication Scenario. When the two sources disagree, the API Hub wins for OData service questions; the abap-atc repo wins for raw ABAP object questions. Record the resolution rationale in the report. + ### Per-cell evaluation For each `(service, edition)` cell of the matrix: @@ -312,6 +320,62 @@ When user explicitly opts in: Re-run Steps 4-6 against the updated catalog to confirm zero drift remains. +## Step 9: Enforcement mode (`--enforce` flag) + +The skill defaults to `report` mode. With `--enforce`, it acts as a **CI-blocking deployment gate**: it exits with a non-zero status code on any HIGH finding, and the calling workflow refuses to deploy. + +### Contract + +| Mode | Exit on HIGH | Exit on MEDIUM | Exit on LOW | Use case | +|---|---|---|---|---| +| `report` (default) | 0 | 0 | 0 | Audit run; review the markdown report | +| `--apply` | 0 | 0 | 0 | Audit run + apply safe additive fixes | +| `--enforce` | **1** | 0 (warning) | 0 (info) | CI gate — pre-deployment block | +| `--enforce --strict` | **1** | **1** | 0 | Tightest gate — block on any finding above LOW | + +HIGH severity criteria (the gate-blocking class): +- A consumed S/4 service is **NOT released** for the deployment target's edition (per source 1 + 2 of Step 4). +- A consumed service is **deprecated** with a known sunset date inside the customer's release window. +- The catalog declares availability that MCP-verification refutes (**OVER_DECLARED** drift). +- Direct DB-table or RFC/BAPI consumption detected against a non-released artifact. + +### CI integration patterns + +Pair this skill with `sap-cap-ci-gates-pattern` Pattern 3 (API-availability drift) for the GitHub Actions / GitLab CI / Jenkins YAML wiring. The gate runs **per-PR** (fast feedback) AND **per-deploy** (final safety net before `cf deploy` / `kubectl apply`). + +GitHub Actions example: +```yaml +- name: Clean Core Level A enforcement + run: | + npx skills run sap-cap-clean-core-enforce -- \ + --enforce \ + --target ${{ inputs.deployment_target }} \ + --abap-atc-source ./.cache/abap-atc-cr-cv-s4hc \ + --report-path docs/audit/${{ github.run_id }}-clean-core.md + # CI fails here if any HIGH finding remains +``` + +### Sources refresh discipline + +The two upstream sources (`abap-atc-cr-cv-s4hc` repo + `api.sap.com`) are **mutable**. They evolve as SAP releases new objects, deprecates old ones, or revises Communication Scenarios. + +| Source | Refresh cadence | How | +|---|---|---| +| `abap-atc-cr-cv-s4hc` | weekly | Cron job: `git -C .cache/abap-atc-cr-cv-s4hc pull --ff-only` | +| `api.sap.com` snapshots | weekly (or per-PR if cheap) | Apify Website Content Crawler against the API Hub package pages — store output as JSON cache. Re-run when "stale" (>7 days) | + +A weekly cron in CI surfaces drift caused by SAP-side changes, not by the project's own commits. File these as "upstream drift" findings with a 90-day window to remediate (per SAP's typical deprecation timeline). + +### Side-by-side extension fallback + +When `--enforce` blocks the build on a non-released service that the project genuinely needs: + +1. Surface the finding as a **side-by-side extension candidate** — the consumption should move out of the CAP service into a separate extension service on BTP (or out of ABAP into BTP entirely). +2. Cross-link to the broader Clean Core return path documented in [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#311--clean-core-level-a-as-deployment-gate-not-just-an-audit`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). +3. Optionally invoke a custom-code refactor skill (e.g. a future `sap-erp-clean-core-refactor`) to plan the migration. + +The gate is **not** about preventing all custom logic — it's about preventing **non-released custom logic from coupling to the deployment pipeline**. Custom logic that legitimately needs to live on BTP belongs in a side-by-side extension, not in the CAP service itself. + ## BTP vs On-Premise Differences | Aspect | BTP target (Public Cloud) | Private Cloud / RISE | On-Premise | @@ -379,8 +443,20 @@ Related skills: ## References -- [SAP API Release State repository](https://github.com/SAP/abap-atc-cr-cv-s4hc) — authoritative source for object classifications -- [Clean Core principles](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) -- [SAP API Business Hub](https://api.sap.com) +**Primary authoritative sources** (Step 4 verification consults both): + +- [SAP API Release State repository (`SAP/abap-atc-cr-cv-s4hc`)](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — JSON-encoded ABAP object classifications by edition; pin / submodule into the project and refresh weekly. +- [SAP API Hub (`api.sap.com`)](https://api.sap.com/) — OData service lifecycle, Communication Scenario membership, deprecation timeline. Scrape via Apify Website Content Crawler or query via `mcp-sap-docs:sap_search_objects` for offline matrix construction. + +**Methodology references**: + +- [Clean Core principles (BTP docs)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) - [CAP `cds.connect.to` documentation](https://cap.cloud.sap/docs/node.js/cds-connect) - [SAP Communication Management (S/4HANA Cloud)](https://help.sap.com/docs/SAP_S4HANA_CLOUD/0f69f8fb28ac4bf48d2b57b9637e81fa/community-management.html) +- [SAP Clean Core extensibility guide (developers.sap.com)](https://developers.sap.com/topics/clean-core.html) +- [SAP Custom Code Migration Guide for S/4HANA](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) + +**Related skills (companion gates)**: + +- [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 3 — wire the `--enforce` mode into a per-PR + per-deploy CI gate. +- [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#311--clean-core-level-a-as-deployment-gate-not-just-an-audit`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) — broader context on Clean Core as a deployment gate. From dfb97231c71cf9ca2143d10e75dae146627a73d6 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:32:00 +0200 Subject: [PATCH 08/18] feat(skills): add sap-erp-clean-core-refactor (JIT, no pre-built KB) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plans the refactor of custom ABAP / ERP / S/4HANA code back to Clean Core compliance, with documentation lookups happening just-in-time only when a specific finding needs them — no centralized pre-crawled knowledge base. Why JIT instead of pre-crawl: - Volume: help.sap.com alone is ~50k pages; full crawl produces gigabytes that 99% of users never read. - Cost: weekly Apify crawl of 18 sources ~ EUR 400-780/year. No clear funding model for OSS centralized infra; nobody wants to maintain it. - Staleness: even a weekly crawl is N days behind; SAP releases new APIs monthly. Day-7 snapshot is already outdated for time-sensitive decisions. The skill ships two files: - SKILL.md: the protocol. Step-by-step from ARC-1 discovery through sap-clean-core-atc classification to JIT lookup to per-object refactor plan. Three modes: discover, plan (default), execute. Bounded per- finding lookup budget (5 Apify pages + 1 git + 1 MCP). Budget exhaust surfaces as research-required, never as a guess. - SOURCES.md: the curated catalog of 23 authoritative SAP documentation sources organized in 4 tiers: * Tier 1 (8 entries) - git-cloneable, free, weekly pull: abap-atc-cr-cv- s4hc + 6 SAP-samples repos + cloud-sdk. * Tier 2 (15 entries) - JIT Apify, user pays at lookup time: api.sap.com (puppeteer, SPA), help.sap.com family (website-content-crawler), cap. cloud.sap, sapui5.hana.ondemand.com, developers.sap.com, community (recent-90d), blogs (tag-filtered), Fiori design, Discovery Center, learning.sap.com / openSAP. * Tier 3 (3 entries) - manual consultation (auth-gated, S-user required): launchpad-support, me.sap.com, support.sap.com. * Tier 4 (2 entries) - MCP-server-backed when installed: mcp-sap-docs, context7. Cost model: per-finding ~5 lookups x EUR 0.005-0.02 = EUR 0.025-0.10 per finding. Typical customer refactor of 50-200 findings = EUR 0.5-5 total, charged to user's own Apify account. Cache TTL 30d for stable docs, 7d for community / blogs. Within TTL, repeat lookups are free. The skill complements: - sap-clean-core-atc (classification - delegate) - sap-unused-code (dead-code identification - delegate) - migrate-custom-code (ATC fix patterns - sibling) - modernize-abap-to-btp-cap (side-by-side scaffold generator - handoff) - sap-cap-fiori-battle-tested-patterns Category 3 (target deployment patterns for the side-by-side - reference) README.md updated with a new "SAP ERP - Clean Core Return + Side-by-Side Refactor (Preview)" section between Analyzing/Understanding and Clean Core/Custom Code Retirement sections. This PR is structurally independent of #279 (Wave 1 audit) and #280 (Wave 2-3 audit). Cross-links resolve once #280 lands (battle-tested- patterns Category 3); until then they degrade gracefully to a "see also" hint. [skip ci] --- skills/README.md | 10 + skills/sap-erp-clean-core-refactor/SKILL.md | 375 ++++++++++++++++++ skills/sap-erp-clean-core-refactor/SOURCES.md | 84 ++++ 3 files changed, 469 insertions(+) create mode 100644 skills/sap-erp-clean-core-refactor/SKILL.md create mode 100644 skills/sap-erp-clean-core-refactor/SOURCES.md diff --git a/skills/README.md b/skills/README.md index 689acb01..05a2ee50 100644 --- a/skills/README.md +++ b/skills/README.md @@ -100,6 +100,16 @@ Both skills produce the same RAP artifact stack. The difference is how they get | [migrate-custom-code](migrate-custom-code/SKILL.md) | Runs ATC readiness checks, groups findings by priority, and generates replacement code | Preparing custom code for S/4HANA migration or ABAP Cloud readiness | | [sap-object-documenter](sap-object-documenter/SKILL.md) | Batch-documents many custom objects at once — purpose, style (Classic/Modern/Mixed), dependencies — as Markdown | Onboarding packages, handoffs, seeding a repo wiki (vs. explain-abap-code which is single-object interactive) | +### SAP ERP — Clean Core Return + Side-by-Side Refactor (Preview) + +Plans and executes the refactor of custom ABAP / ERP / S/4HANA code back to Clean Core compliance — discovery via ARC-1, classification via `sap-clean-core-atc`, **just-in-time documentation lookup** against authoritative SAP sources (no pre-crawled KB), and hand-off to `modernize-abap-to-btp-cap` for side-by-side extension scaffolds. + +| Skill | What it does | When to use | +|---|---|---| +| [sap-erp-clean-core-refactor](sap-erp-clean-core-refactor/SKILL.md) | Inventories Z/Y custom code via ARC-1, classifies Clean Core Level A/B/C/D, consults authoritative SAP sources just-in-time (git-clone of `abap-atc-cr-cv-s4hc` + curated `SAP-samples` + `cloud-sdk`; Apify on-demand for `api.sap.com` / `help.sap.com` / `developers.sap.com` / `cap.cloud.sap` / community / blogs), and emits a per-object refactor plan with rewrite-in-place / extract-to-side-by-side / keep-at-B decisions. Apify lookups bounded per finding (~5 pages); user pays for own Apify account; results cached locally for 30 days | Planning a Clean Core compliance program; pre / post-S/4HANA migration cleanup; quarterly governance review | + +The skill ships **two files**: [`SKILL.md`](sap-erp-clean-core-refactor/SKILL.md) (the protocol) + [`SOURCES.md`](sap-erp-clean-core-refactor/SOURCES.md) (the curated catalog of 23 authoritative SAP sources organized in 4 tiers — Tier 1 git-cloneable, Tier 2 JIT Apify, Tier 3 manual-consultation auth-gated, Tier 4 MCP-server-backed). **No centralized KB is shipped**; documentation lookups happen on-demand within a bounded per-finding budget, charged to the user's own Apify account at ~€0.005-0.02 per lookup (typical refactor: €0.50-€5 total). + ### Clean Core & Custom Code Retirement | Skill | What it does | When to use | diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md new file mode 100644 index 00000000..673e080f --- /dev/null +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -0,0 +1,375 @@ +--- +name: sap-erp-clean-core-refactor +description: Plans and executes a custom-code refactor of an SAP ABAP / ERP / S/4HANA system to return to Clean Core compliance — inventories every Z/Y object via the ARC-1 MCP server, classifies each one against Clean Core Levels A/B/C/D (delegating to `sap-clean-core-atc`), then performs **just-in-time** documentation lookups on a curated list of authoritative SAP sources (Apify on-demand for HTTP/SPA pages, git-clone for static repositories, MCP servers when available) to decide, per object, between an **in-place rewrite to released APIs** or an **extraction to a side-by-side BTP extension** (handing off the scaffold to `modernize-abap-to-btp-cap`). No pre-crawled knowledge base — content is queried only when a specific finding needs it, results cached per-finding under `.cache/`. Use when asked to "refactor custom code to Clean Core", "plan side-by-side extensions", "Clean Core return on ERP", "remove non-released API consumption", "extract Z code to BTP", or to produce a documented migration plan with effort estimates and dependency ordering. +--- + +# SAP ERP — Clean Core Return + Side-by-Side Refactor + +Three convictions drive this skill: + +1. **"Clean Core" is not "ABAP migration"**. The goal is to reclassify custom code where it lives — rewrite to released APIs where possible, extract to BTP where it doesn't fit — without conflating it with a system upgrade. +2. **Documentation is queried just-in-time, not pre-crawled**. A pre-built knowledge base is expensive (Apify costs €400-780/year for weekly full crawl of 18 sources), stale by day 7, and produces gigabytes of content most of which is never read. Instead: maintain a **list** of authoritative SAP sources; query each one **only when a specific finding needs it**; cache the response locally for 30 days. +3. **The user pays for their own lookups**. Apify costs are per-invocation (~€0.005-0.02 per page). For a typical customer refactor (50-200 lookups), total cost is **€0.50-€5** charged to the user's own Apify account — not to a centralized infrastructure that no one wants to maintain. + +This skill does NOT ship a pre-built KB. It ships: +- A **curated source catalog** ([`./SOURCES.md`](./SOURCES.md)) listing the 18 authoritative SAP documentation sources. +- A **JIT lookup protocol** (Step 4 below) describing how to consult each source via Apify / git / MCP / WebFetch. +- A **per-finding cache** under `.cache/sap-clean-core/<hash>/` so repeat lookups within 30 days return instantly. + +## v1 Guardrails + +- **Three modes, in increasing risk order**: `discover` (read-only inventory), `plan` (inventory + JIT-driven refactor plan), `execute` (apply in-place rewrites + scaffold BTP extensions). +- **Never modify the ERP system without `execute` mode** and explicit user authorization. ABAP-side changes route through ARC-1 with version-aware writes. +- **Never invent released-API claims**. Every "rewrite to API X" suggestion must cite the source consulted (URL + retrieval timestamp + cached snapshot path). +- **Side-by-side scaffolds delegated**, not generated inline. The skill produces the **plan**; [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md) does the actual CAP generation. +- **Lookups are bounded**. Per-finding budget: max 5 Apify pages + 1 git lookup + 1 MCP query. If the per-finding budget is exhausted without an answer, surface a "research-required" item to the user, do not proceed with a guess. + +## Smart Defaults (apply silently, do NOT ask) + +| Aspect | Default | Why | +| --- | --- | --- | +| Mode | `plan` | Most users invoke this skill expecting a plan, not execution | +| Package scope | Top-level Z* package + recursive sub-packages | Customer-owned namespaces; SAP-standard untouched | +| Source system | The system bound to the ARC-1 MCP server | Single source of truth for discovery | +| Per-finding lookup budget | 5 Apify pages + 1 git lookup + 1 MCP query | Bounded cost; surface unresolved findings instead of unbounded research | +| Cache TTL | 30 days (stable docs), 7 days (community / blogs) | Balance freshness vs cost; user can `--force-refresh` to invalidate | +| Cache location | `.cache/sap-clean-core/<topic-hash>/` (gitignored) | Per-project local; not committed | +| Output destination | `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md` | Committed; serves as the decision record | +| Target Clean Core level | `A` | Most restrictive; works for all cloud targets | +| Side-by-side target | Asked once at session start | Reuses Step 0 of [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | + +## Input + +Single argument with format `<package-or-object> [mode] [--target=<deployment-target>]`: + +| Argument | Meaning | +|---|---| +| `<package>` | Top-level customer package (e.g. `ZFI`, `ZHR`, `Y_CUSTOM`) — recursively scoped | +| `<object>` | Single object focus mode (e.g. `ZCL_INVOICE_HANDLER`, `ZFM_GET_BP_DATA`) | +| `mode` | `discover` · `plan` (default) · `execute` | +| `--target=` | `btp-cf` · `btp-kyma` · `onprem-kyma` · `onprem-cf` (drives side-by-side target choice) | +| `--force-refresh` | Bypass the local cache; re-query the source even if cached < 30 days | +| `--budget=N` | Override the default per-finding Apify lookup budget (default 5 pages) | + +Examples: +- `ZFI plan --target=btp-kyma` — typical first call. +- `ZCL_INVOICE_HANDLER plan` — single-object focus. +- `ZFI execute --target=btp-kyma` — apply the plan (requires confirmation per object). +- `ZFI plan --force-refresh` — re-query every source even if cached. + +## Step 1: Pre-flight + +### 1a — Verify ARC-1 MCP server connectivity + +The skill cannot proceed without an authenticated ARC-1 connection to the source SAP system. Probe via a known-cheap call: + +``` +SAPSearch(searchType="package_lookup", source="active", query="<package>") +``` + +If the call fails, stop with a clear error and direct the user to the ARC-1 setup docs. + +### 1b — Verify Apify MCP availability (or offer manual mode) + +JIT lookups against HTTP/SPA sources need Apify. Probe for the Apify MCP server: + +``` +mcp__apify__search-apify-docs(query="website content crawler") +``` + +If unavailable, the skill degrades to **manual mode**: it produces a plan with explicit "consult this URL manually" pointers, requires the user to paste back the relevant doc snippets. Slower but no Apify dependency. + +### 1c — Resolve the side-by-side deployment target + +The plan's "extract to BTP" suggestions differ per target (BTP CF vs Kyma vs on-prem). If `--target` wasn't passed, ask the user once (same dialog as [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) Step 0). + +Record the target as `$TARGET`. The plan's side-by-side suggestions consult the matching section of [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-3--btp--kyma--on-premise-deployment-lessons`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). + +### 1d — Initialize the local cache + +```bash +mkdir -p .cache/sap-clean-core +grep -q "^\.cache/$" .gitignore 2>/dev/null || echo ".cache/" >> .gitignore +``` + +Cache entries are gitignored. Each entry is `.cache/sap-clean-core/<sha256-of-topic>/<source-id>-<yyyy-mm-dd>.md` so the same topic queried twice in the same week reads from disk. + +## Step 2: Inventory via ARC-1 MCP + +Discover every customer-owned object in scope. + +### 2a — Enumerate packages + +``` +SAPSearch(searchType="package_tree", source="active", root="<package>") +``` + +### 2b — Enumerate objects per package + +``` +SAPSearch(searchType="tadir_lookup", source="active", filter={pgmid: 'R3TR', devclass: '<pkg>'}) +``` + +Collect object_type, object_name, package, last_changed_at, size_loc into an inventory TSV. + +### 2c — Filter by namespace ownership + +Keep only `Z*`, `Y*`, and `/<customer-namespace>/*` prefixes. + +### 2d — Detect inactive / orphaned objects + +Cross-check with [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) when available. Mark each row with `usage_status`: `ACTIVE` / `UNUSED` / `Z_ONLY`. + +## Step 3: Classification via `sap-clean-core-atc` + +Delegate to the existing [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md). Receive back per-object Clean Core Level + ATC findings + top-finding categories. + +Augment the inventory with `clean_core_level`, `atc_findings_count`, `top_findings_category`. + +## Step 4: JIT documentation lookup per non-A finding + +For each object not classified `A` (or `UNUSED`), perform a bounded just-in-time research pass. The goal: enough evidence to decide between **rewrite-in-place** / **side-by-side** / **keep-at-B**. + +### 4a — Cache-first lookup + +Compute the **topic hash**: `sha256("<finding-category>:<key-object>:<key-context>")`. Examples: +- `sha256("non-released-api:BAPI_INCOMINGINVOICE_CREATE1:supplier-invoice")` +- `sha256("direct-db-access:VBAK:sales-order-header")` + +Look in `.cache/sap-clean-core/<topic-hash>/` for entries dated within the TTL window (30d / 7d). Use them if present. + +### 4b — JIT lookup protocol (cache-miss path) + +Consult sources in this order, stopping as soon as evidence is sufficient (bounded by `--budget`): + +**1. Git-cloned authoritative sources** (free, fast): + +| Source | Query method | When to use | +|---|---|---| +| `SAP/abap-atc-cr-cv-s4hc` | local grep against JSON files | Always for any ABAP object: classify Level + edition availability | +| `SAP-samples/*` (curated) | local grep against `*.md` / `*.cds` / `*.ts` | When looking for side-by-side patterns in a known domain | +| `SAP/cloud-sdk` (docs only) | local grep against `docs-md/` | When the plan involves Cloud SDK consumption from BTP | + +These are git-clone-able weekly without cost; the project's installer ships a script that clones them under `.cache/git/` on first use and `git pull --ff-only` on subsequent runs. + +**2. MCP-server-backed lookup** (free, fast, may not be installed): + +| MCP server | When to use | +|---|---| +| `mcp-sap-docs` | Help portal search, SAP Notes, Communication Scenario lookup | +| `context7` | Non-SAP library docs (rare in this skill) | + +**3. JIT Apify lookup** (per-page cost, user pays): + +| Source | Apify actor | Per-page cost (est.) | When to use | +|---|---|---|---| +| `api.sap.com` (specific service page) | `apify/puppeteer-scraper` | ~€0.01 | OData service lifecycle — released? deprecated? CS membership? | +| `help.sap.com` (specific topic page) | `apify/website-content-crawler` | ~€0.005 | Clean Core principles, extensibility guidance | +| `developers.sap.com/tutorials/<X>` | `apify/website-content-crawler` | ~€0.005 | A concrete how-to for a specific extension pattern | +| `cap.cloud.sap/docs/<X>` | `apify/website-content-crawler` | ~€0.005 | CAP scaffold patterns for the side-by-side option | +| `community.sap.com` (Q&A search) | `apify/website-content-crawler` | ~€0.01 | When stuck on a specific symptom; query the recent-90d archive | +| `blogs.sap.com` (tag-filtered) | `apify/website-content-crawler` | ~€0.01 | Architecture essays on a specific side-by-side pattern | + +**4. WebFetch fallback** (free for simple HTML; not for SPAs): + +If neither MCP nor Apify is available, use the agent's WebFetch primitive against simple-HTML SAP pages. Fails on api.sap.com (React SPA); works on help.sap.com. + +### 4c — Cite + cache + +For every consulted source, record: +- URL +- retrieval timestamp +- snippet relevant to the finding +- the **decision the snippet supports**: rewrite_in_place / extract_to_side_by_side / keep_at_level_b + +Persist as `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md`. The plan in Step 5 cites these paths. + +### 4d — Budget exhaustion handling + +If the per-finding lookup budget runs out without a definitive answer, mark the finding as **research-required** in the plan. Do **not** guess. The plan's "Research backlog" section flags these for human investigation. + +## Step 5: Refactor plan emission + +Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: + +```markdown +# Clean Core Return + Side-by-Side Refactor Plan — <yyyy-mm-dd> + +## Target deployment +- Side-by-side framework: SAP CAP (Node.js + TypeScript) +- Side-by-side runtime: <btp-cf | btp-kyma | onprem-kyma | onprem-cf> +- Lookup budget: 5 Apify pages + 1 git lookup + 1 MCP query per finding (default) +- Apify lookups consumed: <N> (estimated cost: €<X>) +- Cache hits: <N> (no cost; reused from prior runs) + +## Inventory summary +- Total Z/Y objects in scope: <N> +- Active: <N> | Unused: <N> +- Level A: <N> | Level B: <N> | Level C: <N> | Level D: <N> + +## Refactor plan per object +[Per-object decision: rewrite_in_place / extract_to_side_by_side / keep_at_level_b / remove_unused + with effort estimate + risk + source citations] + +## Side-by-side extension catalog +[For every "extract" outcome, the proposed CAP extension scaffold spec] + +## Sequencing +1. Remove unused (parallel): <N> objects +2. Document Level B keep-as-is (parallel): <N> objects +3. Rewrite in-place — phase 1 (low-risk): <N> objects +4. Rewrite in-place — phase 2 (medium-risk): <N> objects +5. Side-by-side extraction — per-extension PR cadence: <N> extensions + +## Effort estimate +- Total: <hours> across <count> objects +- Critical path: <hours> +- Quick wins (unused + documentation): <hours> + +## Research backlog (budget-exhausted findings) +[Items where the lookup budget was insufficient; flag for human research] + +## Source citations +[Path → URL → retrieval timestamp for every cache entry that fed a decision] +``` + +## Step 6: Execute mode (opt-in) + +`mode = execute` enables real changes. The skill processes objects one at a time, asks confirmation per object, and routes each to: + +### 6a — Rewrite in-place (Outcome 1) + +Delegate to ARC-1 MCP: +``` +SAPWrite(action="update", object_type="CLAS", object_name="ZCL_X", source="<rewritten-source>") +SAPActivate(scope="object", object_type="CLAS", object_name="ZCL_X") +SAPLint(action="run_atc", scope="object", object_name="ZCL_X", target_level="A") +``` + +Roll back via SAPGit if ATC regresses. + +### 6b — Side-by-side scaffold (Outcome 2) + +Delegate to [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). The source ABAP object is **not yet removed** — both coexist until QA confirms parity. Optionally annotate the ABAP source as deprecated. + +### 6c — Document Level B keep-as-is (Outcome 3) + +Attach SKTD documentation via ARC-1 explaining the eligible-Level-B rationale. Update ATC exemption configuration if the project uses one. + +## Step 7: Verification + +After any execute action: +- ATC regression check (`SAPLint`). +- Cross-check against the audit catalog used by [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. +- Compare ATC counts before/after; any net regression aborts the execute loop and surfaces a finding. + +## BTP vs On-Premise Differences + +The decision tree is the same; what differs is the **side-by-side target framework choice**: + +| Target | Side-by-side framework | Storage | Eventing | +|---|---|---|---| +| BTP CF | CAP Node.js / TypeScript | HANA HDI | BTP Event Mesh | +| BTP Kyma | CAP Node.js / TypeScript | PostgreSQL in-cluster or HANA Cloud | BTP Event Mesh or Kyma-native NATS | +| On-Premise Kyma | CAP Node.js / TypeScript | HANA on-prem or PostgreSQL on-prem | Kyma-native NATS | +| On-Premise CF | CAP Node.js / TypeScript (legacy) | HANA on-prem | SMTP / file-poll | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-3--btp--kyma--on-premise-deployment-lessons`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) Category 3 for per-target deployment patterns. + +## Cost model + +The skill is designed so total Apify cost for a typical refactor is **€0.50-€5** charged to the **user's own Apify account**, not to a centralized infrastructure. + +| Item | Estimated cost | +|---|---| +| Discovery (ARC-1 only) | €0 | +| Classification (ATC + git-clone) | €0 | +| Per-finding JIT Apify (5 pages × ~€0.01) | €0.05 | +| Refactor of 50 findings | ~€2.50 | +| Refactor of 200 findings | ~€10 | +| Re-running the plan within 30 days (cache hit) | €0 | + +For projects with no Apify budget, the skill operates in **manual mode**: it emits the plan with `consult URL X manually` pointers; the user pastes back doc snippets; the skill incorporates them. Slower, but zero cost. + +## Error Handling + +| Symptom | Likely cause | Action | +| --- | --- | --- | +| ARC-1 MCP not reachable | MCP server not started / not authenticated | Stop; print setup instructions | +| Apify MCP not available | User hasn't installed it | Degrade to manual mode; emit URL pointers, prompt for paste-back | +| Apify rate-limited | User's account hit per-second limit | Backoff with jitter; warn on persistent throttling | +| ATC regression after execute | Rewrite introduced new findings | Roll back via SAPGit; surface diff | +| Side-by-side scaffold generation fails | `modernize-abap-to-btp-cap` errored | Capture error; plan unchanged; flag for retry | +| Budget exhausted on a finding | Source landscape doesn't cover this case | Mark research-required; do NOT guess | +| Cache poisoned (stale snapshot) | Source page changed without TTL expiry | User invokes `--force-refresh` | + +## What This Skill Does NOT Do + +- Does **not** plan a system upgrade. Use SAP Activate methodology for that. +- Does **not** decide whether the customer should adopt BTP — strategic decision. +- Does **not** invent SAP knowledge — every claim is source-cited. +- Does **not** modify SAP-standard code (only `Z*` / `Y*` / customer namespace). +- Does **not** generate the side-by-side CAP scaffold inline — delegates to `modernize-abap-to-btp-cap`. +- Does **not** perform data migration from Z-tables to BTP storage — separate ETL exercise. +- Does **not** pre-build or maintain a centralized SAP documentation cache. JIT only. + +## When to Use This Skill + +- Customer is planning a Clean Core compliance program. +- Customer needs a refactor plan with effort estimates to scope the program. +- Pre-S/4HANA migration: classify and reduce custom code before the system move. +- Post-S/4HANA: re-validate Clean Core compliance and extract residual custom logic to BTP. +- Annual / quarterly governance review. + +## When NOT to Use + +- For a single small change to a Z object (use ARC-1 directly). +- For green-field BTP development (use `modernize-abap-to-btp-cap` directly). +- For a system upgrade plan (use SAP Activate). +- When the customer hasn't decided to adopt BTP — propose a discovery / readiness assessment first. + +## Battle-Tested Patterns Referenced + +This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md): + +- **3.0 Deployment target decision matrix** — selecting the right BTP target for the extension. +- **3.A.* / 3.B.* / 3.C.* / 3.D.*** — target-specific deployment patterns. +- **3.11 Clean Core Level A as deployment gate** — the gate the CAP extension must pass. + +For the ABAP side: +- [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md) — Level A/B/C/D classification. +- [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) — dead-code identification. +- [`../migrate-custom-code/SKILL.md`](../migrate-custom-code/SKILL.md) — ATC fix patterns when rewriting. + +For the side-by-side: +- [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md) — top-level orchestrator. +- [`../modernize-abap-cap-schema/SKILL.md`](../modernize-abap-cap-schema/SKILL.md) — Z-table → CDS entity. +- [`../modernize-abap-cap-service/SKILL.md`](../modernize-abap-cap-service/SKILL.md) — FM / program → CAP service. + +## Recommended Companion Plugins + +| Plugin / Skill / MCP | Used for | +|---|---| +| **ARC-1 MCP server** | ABAP discovery, classification, in-place writes | +| **Apify MCP server** (optional) | JIT documentation lookups against HTTP/SPA sources | +| `mcp-sap-docs` (optional) | Live cross-check vs Apify; preferred for SAP Help Portal queries | +| `sap-cap-capire` | CAP scaffolding patterns for the side-by-side | +| `sap-cloud-sdk` | Cloud SDK examples for the extension's S/4 consumption | +| `sap-fiori-tools` | Fiori Elements V4 scaffolding for the extension's UI | +| `sap-btp-cloud-platform` | BTP service binding reference | +| `sap-docs` | SAP Notes / Help Portal cross-reference | + +See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. + +## See also + +- [`./SOURCES.md`](./SOURCES.md) — curated list of authoritative SAP documentation sources consulted by this skill (just the URLs + when-to-use guidance, no crawled content). + +## References + +- [SAP/abap-atc-cr-cv-s4hc README](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — Released ABAP objects authority +- [SAP API Hub (`api.sap.com`)](https://api.sap.com/) — OData service catalog + lifecycle +- [Clean Core principles (help.sap.com)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) +- [SAP Custom Code Migration Guide](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) +- [Apify Website Content Crawler docs](https://apify.com/apify/website-content-crawler) +- [Apify Puppeteer Scraper docs](https://apify.com/apify/puppeteer-scraper) diff --git a/skills/sap-erp-clean-core-refactor/SOURCES.md b/skills/sap-erp-clean-core-refactor/SOURCES.md new file mode 100644 index 00000000..6f1df22e --- /dev/null +++ b/skills/sap-erp-clean-core-refactor/SOURCES.md @@ -0,0 +1,84 @@ +# SAP Authoritative Sources — JIT lookup catalog + +This file is the **catalog of authoritative SAP documentation sources** that the [`sap-erp-clean-core-refactor`](./SKILL.md) skill consults just-in-time when it needs evidence for a specific custom-code finding. It is **NOT a pre-crawled knowledge base**; it is a **list of URLs + how to query each one**. + +The skill reads this file and decides which sources to consult per finding, within a bounded per-finding lookup budget. Results are cached locally under `.cache/sap-clean-core/` for 30 days (stable docs) or 7 days (community / blogs). + +## Tier 1 — Git-cloned sources (free, weekly refresh, no Apify cost) + +These are SAP-maintained git repositories that can be `git clone`d locally and refreshed via `git pull --ff-only`. They are the **first source consulted** for every finding because they are free, fast, and authoritative. + +| ID | Source | URL | Domain | Refresh strategy | +|---|---|---|---|---| +| `abap-atc-cr-cv-s4hc` | SAP API Release State repository | https://github.com/SAP/abap-atc-cr-cv-s4hc | Released ABAP object authority — JSON files per object class, per edition (Public Cloud / Private Cloud / On-Premise) | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-cap-sflight` | SAP-samples — CAP SFlight reference | https://github.com/SAP-samples/cap-sflight | Canonical CAP service + Fiori Elements V4 example | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-cloud-cap-samples` | SAP-samples — CAP samples (multi-domain) | https://github.com/SAP-samples/cloud-cap-samples | Multiple side-by-side patterns: orders, hello-world, sflight, … | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-btp-cap-multitenant-saas` | SAP-samples — multitenant CAP SaaS | https://github.com/SAP-samples/btp-cap-multitenant-saas | Multi-customer extension pattern | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-cap-event-handling` | SAP-samples — CAP event handling | https://github.com/SAP-samples/cap-sample-event-handling | Event Mesh / NATS subscription patterns | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-odata-v4-cds-cap-fiori` | SAP-samples — OData V4 + CDS + CAP + Fiori | https://github.com/SAP-samples/odata-v4-cds-cap-fiori | End-to-end OData V4 / CAP / FE V4 example | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-samples-btp-typescript` | SAP-samples — BTP TypeScript app | https://github.com/SAP-samples/btp-build-business-application-with-typescript | TypeScript CAP scaffolding | `git clone --depth 1`, weekly `git pull --ff-only` | +| `sap-cloud-sdk` | SAP Cloud SDK (docs only) | https://github.com/SAP/cloud-sdk | JS/TS/Java SDK for consuming S/4 + BTP services | `git clone --depth 1 --filter=blob:none`, weekly `git pull --ff-only` | + +**Operational note**: a project that installs this skill runs a one-time setup that clones the Tier 1 list under `.cache/git/`. Total size after clone ≈ 100-200 MB. Weekly refresh is bandwidth-only (no compute cost). + +## Tier 2 — JIT Apify lookups (per-page cost, user pays at lookup time) + +These are HTTP / SPA sources that cannot be efficiently mirrored offline. They are queried on-demand via Apify only when a finding's evidence requires it, and only for the specific page that holds the answer (not a full-site crawl). + +| ID | Source | URL | Domain | Apify actor | Est. cost / page | +|---|---|---|---|---|---| +| `api-sap-com` | SAP API Hub | https://api.sap.com/ | OData service lifecycle (released / sandbox / deprecated), Communication Scenario membership, version history | `apify/puppeteer-scraper` (React SPA) | ~€0.01 | +| `help-sap-clean-core` | SAP Help Portal — Clean Core | https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core | Clean Core principles + Levels A/B/C/D definitions | `apify/website-content-crawler` | ~€0.005 | +| `help-sap-btp` | SAP Help Portal — BTP | https://help.sap.com/docs/btp/sap-business-technology-platform | BTP services, runtime, eventing | `apify/website-content-crawler` | ~€0.005 | +| `help-sap-s4hana-cloud` | SAP Help Portal — S/4HANA Cloud | https://help.sap.com/docs/SAP_S4HANA_CLOUD | S/4HANA Cloud feature docs | `apify/website-content-crawler` | ~€0.005 | +| `help-sap-abap-development` | SAP Help Portal — ABAP for Cloud Development | https://help.sap.com/docs/abap-cloud | ABAP Cloud language reference, released-API catalog | `apify/website-content-crawler` | ~€0.005 | +| `help-sap-integration-suite` | SAP Help Portal — Integration Suite | https://help.sap.com/docs/integration-suite | iFlow design, API Management | `apify/website-content-crawler` | ~€0.005 | +| `help-sap-event-mesh` | SAP Help Portal — Event Mesh | https://help.sap.com/docs/event-mesh | BTP Event Mesh | `apify/website-content-crawler` | ~€0.005 | +| `developers-clean-core` | developers.sap.com — Clean Core topic | https://developers.sap.com/topics/clean-core.html | Clean Core tutorials | `apify/website-content-crawler` | ~€0.005 | +| `cap-cloud-sap` | SAP CAP — capire documentation | https://cap.cloud.sap/docs/ | CAP runtime, CDS, deployment | `apify/website-content-crawler` | ~€0.005 | +| `sapui5-sdk` | SAPUI5 SDK Reference | https://sapui5.hana.ondemand.com/sdk/ | UI5 / Fiori Elements V4 controls and annotations | `apify/website-content-crawler` | ~€0.005 | +| `community-sap-com` | SAP Community Q&A | https://community.sap.com/ | Recent symptom-specific troubleshooting | `apify/website-content-crawler` (recent-90d filter) | ~€0.01 | +| `blogs-sap-com` | SAP Blogs | https://community.sap.com/t5/technology-blogs-by-sap/bg-p/technology-blog-sap | Architecture / pattern essays from SAP engineers | `apify/website-content-crawler` (tag-filtered) | ~€0.01 | +| `fiori-design` | SAP Fiori Design Guidelines | https://experience.sap.com/fiori-design-web/ | Fiori UX guidance | `apify/website-content-crawler` | ~€0.005 | +| `discovery-center` | SAP Discovery Center | https://discovery-center.cloud.sap/ | Reference architectures | `apify/puppeteer-scraper` (SPA) | ~€0.01 | +| `learning-sap-com` | learning.sap.com / openSAP | https://learning.sap.com/ | Clean Core / extensibility courses | `apify/puppeteer-scraper` (SPA, some pages auth-gated) | ~€0.01 | + +**Operational note**: every Tier 2 lookup hits `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md` first. Cache TTL: 30 days for stable docs, 7 days for community / blogs. Within the TTL window, repeat lookups are free. + +## Tier 3 — Manual-consultation sources (auth-gated, NOT crawled) + +These sources require S-user credentials and **cannot be automated** without the customer providing valid login. They are listed for awareness only; the skill emits "consult manually" pointers when they apply. + +| ID | Source | URL | Auth | Purpose | +|---|---|---|---|---| +| `launchpad-support` | SAP Notes | https://launchpad.support.sap.com/ | S-user required | Authoritative SAP Notes for specific code paths / corrections | +| `me-sap-com` | Software Lifecycle | https://me.sap.com/ | S-user required | Product lifecycle, release information | +| `support-sap-com` | Support Catalog | https://support.sap.com/ | S-user required | Support documentation, incident management | + +## Tier 4 — MCP-server-backed lookup (preferred when installed) + +When the consuming environment has these MCP servers configured, the skill prefers them over Apify (faster, often free, better structured): + +| MCP server | Replaces Apify for | When preferred | +|---|---|---| +| `mcp-sap-docs` | help.sap.com, abap-atc-cr-cv-s4hc queries | When `mcp__sap-docs__*` tools are available | +| `context7` | Generic library docs (non-SAP) | When the lookup is about an npm package or non-SAP library | + +## When-to-use heuristic — what source for what finding + +| Finding category | Tier 1 (git) | Tier 2 (Apify) | Tier 3 (manual) | +|---|---|---|---| +| `non-released-api` | `abap-atc-cr-cv-s4hc` (check object status) | `api-sap-com` (find released alternative service) | `launchpad-support` (SAP Note for migration path) | +| `direct-db-access` | `abap-atc-cr-cv-s4hc` (released CDS view available?) | `help-sap-abap-development` (released-CDS catalog) | — | +| `modification` (Level D) | — | `help-sap-clean-core` (BAdI / enhancement Level B pattern) | `launchpad-support` (mandatory SAP Note for the modification) | +| `enhancement-point` (Level B eligible) | — | `developers-clean-core` (tutorial for key-user extensibility) | — | +| `side-by-side candidate` | `sap-samples-*` (matching pattern repo) + `sap-cloud-sdk` | `cap-cloud-sap` + `help-sap-btp` (target framework docs) | — | +| `unused` (zero SCMON hits) | — | — | — (remove after stakeholder sign-off; no doc lookup needed) | + +## Refresh discipline + +**Tier 1 git-clones**: weekly `git pull --ff-only` for each repo. Cost: bandwidth only. + +**Tier 2 Apify cache**: per-entry TTL applies. The cache is local to the project (under `.cache/sap-clean-core/`), not shared across projects. Users with multiple projects on the same machine can symlink the cache to a shared location. + +**No centralized infrastructure**: this skill does NOT maintain a centralized KB. There is no weekly cron crawling everything; there is no shared cache for all users. Every project owns its own cache; every Apify call is on the user's own account. From a0b112a10b6baf332c5e3a942aeb61820ea1619f Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:47:11 +0200 Subject: [PATCH 09/18] feat(sap-erp-clean-core-refactor): add --aggressive and --push-to-a flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets the user override the default "Level B kept as B" behaviour and explore Level A escalation paths (rewrite via Customizing OR side-by-side extraction) per object. Why this matters: Level B is already Clean-Core compliant, so by default the skill emits keep_at_level_b for Level B objects. But there are real scenarios where the customer needs to push further to A: - Governance mandate "zero custom logic in ERP" - Target deployment is S/4HANA Public Cloud strict mode - Customer has strong BTP team and wants to consolidate logic out of ERP - The B pattern was an accident (Customizing alternative exists) - The B pattern uses a deprecated BAdI being sunset Three escalation mechanisms (least invasive first): 1. Per-object override via plan-file editing (already supported): After plan emission, edit docs/refactor/<date>-clean-core-plan.md to change any keep_at_level_b row to rewrite_in_place or extract_to_side_by_side. Execute mode reads the edited file. Finest grained, no re-run, no extra Apify cost. 2. --push-to-a=A,B,C (selective): only listed object names get the expanded analysis. Cheaper than --aggressive (+€0.05-€0.10 per object). Use when you know upfront which objects to escalate. 3. --aggressive (global): every Level B gets the expanded analysis. +30-50% on plan generation cost. Use for governance-driven full review. New Step 4e — escalation criteria and when-to / when-not-to tables: documents when push-to-A is appropriate and when keep-at-B is correct (many B objects use SAP-blessed patterns where A means "abandon SAP recommendation" — not always the right call). New Step 4f — extended decision logic for escalated findings: emits a multi-option proposal (default_decision + escalation_options array + recommended option with reasoning) instead of a single decision. The recommendation considers consistency with other decisions in the same plan (if plan already has 5 side-by-side extensions in the same domain, recommend side-by-side for the escalated B too). New Step 4g — post-plan editing as the finest-grained override mechanism, complementary to the flags. Plan output template (Step 5) updated: - New "Level B escalation proposals" section (only when --aggressive or --push-to-a was used) - New "Effort estimate" section showing per-decision-path deltas - New "Sequencing" entry for user-chosen escalations Cost model updated with delta lines for --aggressive (+30-50%) and --push-to-a (€0.05-€0.10 per listed object). Smart Defaults updated with explicit "Level B handling = keep by default" entry explaining why escalation is opt-in. Examples section adds two new invocations: - `ZFI plan --target=btp-kyma --aggressive` - `ZFI plan --target=btp-kyma --push-to-a=ZTABLE_TAX_RATES_LOCAL,ZCL_VENDOR_LOOKUP_EXT` [skip ci] --- skills/sap-erp-clean-core-refactor/SKILL.md | 116 ++++++++++++++++++-- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index 673e080f..f25f4f21 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -37,10 +37,11 @@ This skill does NOT ship a pre-built KB. It ships: | Output destination | `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md` | Committed; serves as the decision record | | Target Clean Core level | `A` | Most restrictive; works for all cloud targets | | Side-by-side target | Asked once at session start | Reuses Step 0 of [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | +| Level B handling | `keep_at_level_b` by default (compliant; documented) | B is already Clean-Core compliant. Pushing to A is opt-in via `--aggressive` or `--push-to-a` because escalation adds cost and may be wrong choice (most B objects use SAP-recommended patterns) | ## Input -Single argument with format `<package-or-object> [mode] [--target=<deployment-target>]`: +Single argument with format `<package-or-object> [mode] [flags]`: | Argument | Meaning | |---|---| @@ -50,12 +51,16 @@ Single argument with format `<package-or-object> [mode] [--target=<deployment-ta | `--target=` | `btp-cf` · `btp-kyma` · `onprem-kyma` · `onprem-cf` (drives side-by-side target choice) | | `--force-refresh` | Bypass the local cache; re-query the source even if cached < 30 days | | `--budget=N` | Override the default per-finding Apify lookup budget (default 5 pages) | +| `--aggressive` | For every Level B finding, also research Level A escalation paths (rewrite via Customizing OR side-by-side extraction). Emits **multi-option proposals** instead of default `keep_at_level_b`. Adds ~30-50% to plan generation cost | +| `--push-to-a=A,B,C` | Selective B→A escalation: only the listed object names get the expanded analysis. Cheaper than `--aggressive`, requires you know which objects upfront | Examples: -- `ZFI plan --target=btp-kyma` — typical first call. -- `ZCL_INVOICE_HANDLER plan` — single-object focus. -- `ZFI execute --target=btp-kyma` — apply the plan (requires confirmation per object). -- `ZFI plan --force-refresh` — re-query every source even if cached. +- `ZFI plan --target=btp-kyma` — typical first call (default: Level B kept as B) +- `ZCL_INVOICE_HANDLER plan` — single-object focus +- `ZFI execute --target=btp-kyma` — apply the plan (requires confirmation per object) +- `ZFI plan --force-refresh` — re-query every source even if cached +- `ZFI plan --target=btp-kyma --aggressive` — explore Level A escalation for every Level B +- `ZFI plan --target=btp-kyma --push-to-a=ZTABLE_TAX_RATES_LOCAL,ZCL_VENDOR_LOOKUP_EXT` — selective B→A for two specific objects ## Step 1: Pre-flight @@ -188,6 +193,79 @@ Persist as `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md`. The If the per-finding lookup budget runs out without a definitive answer, mark the finding as **research-required** in the plan. Do **not** guess. The plan's "Research backlog" section flags these for human investigation. +### 4e — Level B → Level A escalation (aggressive mode) + +By default, an object classified Level B by `sap-clean-core-atc` is decided `keep_at_level_b` — it is already Clean-Core compliant; pushing to A is effort with diminishing returns, and the chosen pattern (BAdI, Key User Extensibility, custom CDS on released base, …) is usually the SAP-recommended one for the use case. + +When the user opts into escalation (`--aggressive` for all Level B, OR `--push-to-a=A,B,C` for selected objects), the skill performs an **extended analysis** per Level B finding: it researches Level A escalation paths in addition to the default decision, and emits a **multi-option proposal** instead of a single decision. + +**Why this is not the default**: + +- Level B is already compliant (Clean Core green). +- Most B objects exist because the SAP-recommended pattern IS a B-eligible extension (BAdI, key-user, custom CDS on released base). Pushing to A means *removing the extension*, which often defeats the purpose. +- Each `--aggressive` pass adds ~30-50% to per-finding cost (extra Apify lookups for Customizing alternatives + side-by-side patterns + extension samples cross-check). + +**When the user DOES want escalation**: + +| Scenario | Reason | +|---|---| +| Governance mandate: "zero custom logic in ERP" | Organizational policy supersedes SAP defaults | +| Target deployment: S/4HANA Public Cloud strict mode | Public Cloud rejects most B patterns; must go A or side-by-side | +| Strong BTP team available | Side-by-side extraction is cleaner long-term | +| Object's B pattern was an accident ("did it custom 4 years ago for laziness") | Customizing alternative exists and is simpler | +| Object's B pattern uses a deprecated BAdI / enhancement-point | Forced migration anyway — escalate to choose target | + +**When the user should NOT escalate**: + +| Scenario | Reason | +|---|---| +| Object uses an SAP-blessed BAdI / Key User app | You're already at the SAP-recommended pattern; A would mean abandoning it | +| Object is a stable lookup table (e.g. country-specific tax rates) | B with documentation is correct; A would mean "no logic", which loses business meaning | +| Customer has no BTP plan and no Customizing alternative | Side-by-side and config rewrite both blocked; B is the only option | +| Effort budget is tight | A escalation costs 2-5× the effort of B-keep | + +### 4f — Extended decision logic for escalated findings + +When an object is in the escalation set (matched by `--aggressive` or `--push-to-a`): + +1. **First**, perform the standard Level B classification check (Step 4b) — record what pattern made it eligible. +2. **Then**, run the escalation lookup: + - Search the KB for "Customizing alternative to <pattern>" → if found, propose `rewrite_in_place` via standard Customizing. + - Search the KB for "side-by-side pattern for <domain>" → if found, propose `extract_to_side_by_side`. + - If neither escalation path is found, fall back to `keep_at_level_b` (as default). +3. **Emit a multi-option proposal** in the plan instead of a single decision: + +```yaml +ZTABLE_TAX_RATES_LOCAL # currently Level B via BAdI BADI_TAX_RATE_DETERMINATION + default_decision: keep_at_level_b + default_effort: S (2-4 h) + default_risk: low + escalation_options: + - decision: rewrite_in_place + replacement: Tax Customizing (T007A entries via SM30 / Key User app) + effort: M (8-12 h) + risk: medium (may not cover all custom rules — needs validation per country) + kb_evidence: kb-cache/<hash>/help-sap-tax-customizing-2026-05-13.md + - decision: extract_to_side_by_side + pattern: BTP CAP tax service consumed via Cloud SDK + effort: L (16-24 h) + risk: medium (introduces BTP runtime dependency) + kb_evidence: kb-cache/<hash>/cap-tax-service-pattern-2026-05-13.md + recommended: extract_to_side_by_side (consistent with vendor risk score extension already planned) +``` + +The recommendation is informed by **consistency with other decisions in the same plan**: if the plan already has 5 side-by-side extensions and this Level B is in the same domain, recommend side-by-side too. If the plan is otherwise all in-place rewrites, recommend Customizing. + +### 4g — Per-object override mechanism (post-plan editing) + +After the plan is emitted, the user can ALWAYS override any decision by editing `docs/refactor/<date>-clean-core-plan.md` between Step 5 and Step 6. The plan file is the contract: `execute` mode reads it back and respects the latest decision. Useful for: + +- Overriding `keep_at_level_b` to `rewrite_in_place` for one specific object after manual inspection. +- Downgrading an `extract_to_side_by_side` to `keep_at_level_b` after cost concerns surface. +- Adding research notes to `research_required` findings before re-running. + +This is the **finest-grained control mechanism**, complementary to `--aggressive` / `--push-to-a`. No re-run needed; just edit and execute. + ## Step 5: Refactor plan emission Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: @@ -211,6 +289,16 @@ Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: [Per-object decision: rewrite_in_place / extract_to_side_by_side / keep_at_level_b / remove_unused with effort estimate + risk + source citations] +## Level B escalation proposals (only if --aggressive or --push-to-a was used) +[For each Level B object in the escalation set, multi-option proposal showing: + - default_decision (keep_at_level_b) + - escalation_options (rewrite_in_place via Customizing, extract_to_side_by_side via BTP) + - recommended option with reasoning + - per-option effort + risk + KB evidence] + +The user reviews and chooses per object by editing the plan file before invoking execute. +Default behaviour (no flag): every Level B is kept; no escalation proposals are generated. + ## Side-by-side extension catalog [For every "extract" outcome, the proposed CAP extension scaffold spec] @@ -220,11 +308,17 @@ Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: 3. Rewrite in-place — phase 1 (low-risk): <N> objects 4. Rewrite in-place — phase 2 (medium-risk): <N> objects 5. Side-by-side extraction — per-extension PR cadence: <N> extensions +6. Level B escalations chosen by user (if any): mixed cadence per chosen path ## Effort estimate -- Total: <hours> across <count> objects +- Total: <hours> across <count> objects (default plan) - Critical path: <hours> - Quick wins (unused + documentation): <hours> +- Escalation deltas (if aggressive/push-to-a used): + * Keep at B (default): <hours> + * Rewrite via Customizing (option 1): <hours> + * Side-by-side extraction (option 2): <hours> + User chooses per object; effort can vary 2-5× depending on choices. ## Research backlog (budget-exhausted findings) [Items where the lookup budget was insufficient; flag for human research] @@ -285,11 +379,15 @@ The skill is designed so total Apify cost for a typical refactor is **€0.50- | Discovery (ARC-1 only) | €0 | | Classification (ATC + git-clone) | €0 | | Per-finding JIT Apify (5 pages × ~€0.01) | €0.05 | -| Refactor of 50 findings | ~€2.50 | -| Refactor of 200 findings | ~€10 | +| Refactor plan of 50 findings (default mode) | ~€2.50 | +| Refactor plan of 200 findings (default mode) | ~€10 | | Re-running the plan within 30 days (cache hit) | €0 | +| `--aggressive` mode delta (extra escalation lookups per Level B) | +30-50% on plan cost | +| `--push-to-a=<list>` selective (3-5 extra lookups per listed object) | +€0.05-€0.10 per object | + +Example: a 200-finding refactor with **default** = ~€10. The same with `--aggressive` = ~€13-15. The same with `--push-to-a` on 8 specific objects = ~€11. -For projects with no Apify budget, the skill operates in **manual mode**: it emits the plan with `consult URL X manually` pointers; the user pastes back doc snippets; the skill incorporates them. Slower, but zero cost. +For projects with no Apify budget, the skill operates in **manual mode**: it emits the plan with `consult URL X manually` pointers; the user pastes back doc snippets; the skill incorporates them. Slower, but zero cost. Escalation modes (`--aggressive` / `--push-to-a`) still work in manual mode — they just produce more "consult manually" pointers per object. ## Error Handling From 3d7ffc38146147a3a969065d3c020286c392e07e Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:51:40 +0200 Subject: [PATCH 10/18] feat(sap-erp-clean-core-refactor): --target-level flag + explicit decision tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the --target-level=A|B flag and makes the per-object decision tree explicit in the skill (Step 4d-bis), so users understand exactly how Level C and Level D findings land at A vs B by default. New flag --target-level=A|B: - --target-level=A (ambitious): always prefer A. For D, forces side-by- side extraction over BAdI-rewrite. For C, forces released-API replacement over BAdI substitution even when BAdI is cheaper. For B, activates escalation analysis (same as --aggressive — now a synonym). - --target-level=B (minimal compliance): settle for B everywhere. For C, prefer cheaper BAdI path even if released-API replacement lands at A. For D, BAdI-rewrite only — never side-by-side just for "purity". Useful when customer wants "good enough" with minimum disruption. Default behavior (no flag): cost-minimizing per object. D → B via BAdI when feasible. C → A via released API when feasible. Falls back to side- by-side or keep-at-B based on lookup results. New Step 4d-bis "Decision tree (per-object)" with explicit pseudo-code showing exactly how Start Level + flags → Target Level + Decision is computed. Captures the five key invariants: - A stays A - B stays B (default) or A (only via opt-in escalation) - C goes to A (default) / B (--target-level=B) / side-by-side (no equiv) - D goes to B via BAdI (default) / A via side-by-side (--target-level=A or no BAdI option) / accept_as_d as last resort - Unused removed Smart Defaults table extended with three new rows: - Level C target: A via released-API by default, B fallback - Level D target: B via BAdI by default, side-by-side fallback - Mixed-findings objects: object level = MAX of finding levels Plan output template now shows an explicit table with columns: | Object | Start Level | Target Level | Decision | Replacement | Effort | Risk | KB evidence | This makes the refactor outcome immediately visible per object — no more "outcome: rewrite_in_place" hiding the actual target level. Concrete examples in the plan template: - ZCL_INVOICE_HANDLER: D → A via API_SUPPLIERINVOICE_PROCESS_SRV - ZCL_VENDOR_RISK_SCORE: C → A (ERP-side) via side-by-side - ZTABLE_TAX_RATES_LOCAL: D → B via BAdI BADI_TAX_RATE_DETERMINATION Clarifies "ERP-side A" semantics: when an object is extracted to BTP, the ERP no longer contains it (A by absence). The logic on BTP is governed by separate Clean Core gates (see sap-cap-clean-core-enforce). [skip ci] --- skills/sap-erp-clean-core-refactor/SKILL.md | 84 ++++++++++++++++++++- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index f25f4f21..b752d612 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -37,7 +37,10 @@ This skill does NOT ship a pre-built KB. It ships: | Output destination | `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md` | Committed; serves as the decision record | | Target Clean Core level | `A` | Most restrictive; works for all cloud targets | | Side-by-side target | Asked once at session start | Reuses Step 0 of [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | -| Level B handling | `keep_at_level_b` by default (compliant; documented) | B is already Clean-Core compliant. Pushing to A is opt-in via `--aggressive` or `--push-to-a` because escalation adds cost and may be wrong choice (most B objects use SAP-recommended patterns) | +| Level B handling | `keep_at_level_b` by default (compliant; documented) | B is already Clean-Core compliant. Pushing to A is opt-in via `--aggressive` / `--push-to-a` / `--target-level=A` because escalation adds cost and may be wrong choice (most B objects use SAP-recommended patterns) | +| Level C target | A (via released-API rewrite) when equivalent exists, else B (via BAdI substitution), else side-by-side (ERP becomes A) | Cost-minimizing default. Override with `--target-level=B` to settle for B even when A is cheaper | +| Level D target | B (via BAdI / enhancement-point rewrite) when feasible, else side-by-side (ERP becomes A) | Cost-minimizing default. D objects usually have business logic that genuinely needs an extension surface; BAdI-rewrite is the SAP-recommended path. Override with `--target-level=A` to prefer side-by-side over BAdI | +| Mixed-findings objects | Object level = MAX of finding levels (e.g. one D finding + one B finding → object treated as D) | The refactor decision is per-object, not per-finding, but the plan reports residual findings post-refactor | ## Input @@ -51,8 +54,10 @@ Single argument with format `<package-or-object> [mode] [flags]`: | `--target=` | `btp-cf` · `btp-kyma` · `onprem-kyma` · `onprem-cf` (drives side-by-side target choice) | | `--force-refresh` | Bypass the local cache; re-query the source even if cached < 30 days | | `--budget=N` | Override the default per-finding Apify lookup budget (default 5 pages) | -| `--aggressive` | For every Level B finding, also research Level A escalation paths (rewrite via Customizing OR side-by-side extraction). Emits **multi-option proposals** instead of default `keep_at_level_b`. Adds ~30-50% to plan generation cost | +| `--aggressive` | Synonym for `--target-level=A`. For every Level B finding, also research Level A escalation paths (rewrite via Customizing OR side-by-side extraction). Emits **multi-option proposals** instead of default `keep_at_level_b`. Adds ~30-50% to plan generation cost | | `--push-to-a=A,B,C` | Selective B→A escalation: only the listed object names get the expanded analysis. Cheaper than `--aggressive`, requires you know which objects upfront | +| `--target-level=A` | **Ambitious mode** — always prefer Level A outcomes. Forces side-by-side extraction over BAdI-rewrite for Level D when both feasible; forces released-API replacement over BAdI-substitution for Level C even when BAdI is cheaper. Activates Level B escalation analysis (same as `--aggressive`) | +| `--target-level=B` | **Minimal-compliance mode** — settle for B everywhere. For Level C, pick the cheapest path that reaches B (instead of A via released API) when that saves effort. For Level D, BAdI-rewrite only (never side-by-side just for "purity"). Useful when customer's appetite is "good enough compliance with minimum disruption" | Examples: - `ZFI plan --target=btp-kyma` — typical first call (default: Level B kept as B) @@ -193,6 +198,62 @@ Persist as `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md`. The If the per-finding lookup budget runs out without a definitive answer, mark the finding as **research-required** in the plan. Do **not** guess. The plan's "Research backlog" section flags these for human investigation. +### 4d-bis — Decision tree (per-object) + +For each Z/Y object with a starting Clean Core Level (A / B / C / D / Unused), apply this decision tree to determine the **decision** and the **target level**. The default behaviour is cost-minimizing per object; `--target-level` and `--aggressive` / `--push-to-a` flags shift the preference. + +``` +Object starting level = A? + YES → decision = no_action (target = A) + STOP + +Object starting level = Unused? + YES → decision = remove_unused (target = N/A) + STOP + +Object starting level = D (Modification)? + YES → Released-API equivalent fully replaces the modification? + YES → decision = rewrite_in_place via released API (target = A) + NO → BAdI / enhancement-point eligible substitute exists? + YES (default behaviour) → decision = rewrite_in_place via BAdI (target = B) + YES + --target-level=A flag → continue to side-by-side check + NO → continue to side-by-side check + Side-by-side extraction feasible (CAP + BTP target available)? + YES → decision = extract_to_side_by_side (target = A, ERP-side; logic lives on BTP) + NO → decision = accept_as_d_documented (requires explicit sign-off + ATC exemption) + STOP + +Object starting level = C (Warning — non-released-api or direct-db-access)? + YES → Released-API / released-CDS equivalent exists? + YES (default behaviour) → decision = rewrite_in_place via released API (target = A) + YES + --target-level=B flag → BAdI substitution path cheaper? if yes, decision = rewrite_in_place via BAdI (target = B) + NO → continue + Is the residual logic data-access-only (no business logic)? + YES → decision = keep_at_level_b with documentation (target = B) + NO → Side-by-side extraction feasible? + YES → decision = extract_to_side_by_side (target = A, ERP-side) + NO → decision = keep_at_level_b with documentation (target = B) + STOP + +Object starting level = B (Eligible)? + YES → --aggressive or --push-to-a covers this object or --target-level=A? + YES → emit multi-option proposal (Step 4e/4f) + default_decision: keep_at_level_b (target = B) + option 1: rewrite_in_place via Customizing (target = A) + option 2: extract_to_side_by_side (target = A, ERP-side) + NO → decision = keep_at_level_b with documentation (target = B) + STOP +``` + +**Key invariants**: +- A → only stays A (no_action). +- B → stays B (default) OR goes to A (only via opt-in escalation). +- C → goes to A (default) OR to B (only via `--target-level=B`) OR side-by-side (A, ERP-side) when no equivalent. +- D → goes to B (default via BAdI) OR to A via side-by-side (when BAdI not feasible OR `--target-level=A` is on) OR accept_as_d (last resort with explicit sign-off). +- Unused → removed (no level applies). + +**Side-by-side outcome — clarification**: when an object is extracted to BTP, the **ERP-side artefact disappears**. The ERP no longer contains the custom code, so by absence the ERP is at Level A for that domain. The logic continues to live on BTP as a CAP extension (not classified by ABAP Clean Core levels — BTP-side compliance is governed by the deployment-target gate, see [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md)). + ### 4e — Level B → Level A escalation (aggressive mode) By default, an object classified Level B by `sap-clean-core-atc` is decided `keep_at_level_b` — it is already Clean-Core compliant; pushing to A is effort with diminishing returns, and the chosen pattern (BAdI, Key User Extensibility, custom CDS on released base, …) is usually the SAP-recommended one for the use case. @@ -286,8 +347,23 @@ Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: - Level A: <N> | Level B: <N> | Level C: <N> | Level D: <N> ## Refactor plan per object -[Per-object decision: rewrite_in_place / extract_to_side_by_side / keep_at_level_b / remove_unused - with effort estimate + risk + source citations] + +Per-object table with explicit starting-level → target-level transitions: + +| Object | Start Level | Target Level | Decision | Replacement / Pattern | Effort | Risk | KB evidence | +|---|---|---|---|---|---|---|---| +| ZCL_INVOICE_HANDLER | D | A | rewrite_in_place | API_SUPPLIERINVOICE_PROCESS_SRV (released) | M (6h) | low | .cache/.../api-supplierinvoice-2026-05-13.md | +| ZCL_VENDOR_RISK_SCORE | C | A (ERP-side) | extract_to_side_by_side | CAP service on BTP Kyma + Event Mesh subscribe to BusinessPartnerChanged | L (24h) | medium | .cache/.../cap-event-mesh-2026-05-13.md | +| ZTABLE_TAX_RATES_LOCAL | D | B | rewrite_in_place via BAdI | BADI_TAX_RATE_DETERMINATION (eligible) | M (8h) | medium | .cache/.../tax-badi-2026-05-13.md | +| ZRFI_REPORT_OLD | (Unused) | — | remove_unused | (none) | S (1h) | low | sap-unused-code report | +| ZDDLS_VENDOR_VIEW | B | B | keep_at_level_b | (annotation hygiene fix only) | S (2h) | low | (no escalation requested) | +| (… rest of objects …) | | | | | | | | + +**Column meanings**: +- **Start Level**: classification by `sap-clean-core-atc` before refactor (A/B/C/D/Unused). +- **Target Level**: classification expected after refactor lands. For `extract_to_side_by_side`, the target is "A (ERP-side)" because the ERP no longer contains the artefact; the logic lives on BTP under separate compliance rules. +- **Decision**: one of `no_action` / `rewrite_in_place` / `rewrite_in_place via BAdI` / `extract_to_side_by_side` / `keep_at_level_b` / `remove_unused` / `accept_as_d_documented` / `research_required`. +- **Replacement / Pattern**: the concrete substitute (released API, BAdI name, BTP CAP pattern, …). ## Level B escalation proposals (only if --aggressive or --push-to-a was used) [For each Level B object in the escalation set, multi-option proposal showing: From f24b31e42a57ff8df9fc37d364c3d4c26b300d7a Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:54:16 +0200 Subject: [PATCH 11/18] fix(skills): default deployment target is BTP Cloud Foundry, not Kyma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the master skill (Category 3.0 decision matrix narrative) and the stack-audit orchestrator (Step 0b ask-user template) to recommend BTP Cloud Foundry as the default when the customer's target is ambiguous or unspecified. Rationale: - BTP CF is the most widely-adopted SAP-managed runtime — broadest customer reach. - BTP CF has the broadest service ecosystem (Free + paid tiers for xsuaa, html5-apps-repo, destination, connectivity, event-mesh, audit-log, job-scheduling, …) — many of those have Free tiers that Kyma cannot consume. - BTP CF tooling is the most mature (mta.yaml + mbt build + cf deploy). - BTP CF has the lowest operational ceremony for teams without dedicated Kubernetes expertise — most SAP customers do not have a K8s operations team. BTP Kyma stays as the right choice when the customer explicitly wants the Kubernetes operational model (pay-tier services, more runtime flexibility, container-native eventing, CronJob CRDs). The skill describes those use cases so a deliberate Kyma choice is well-supported. On-Premise targets unchanged: explicit data-sovereignty mandate or existing Kubernetes investment (Kyma on-prem); legacy continuity only (CF on-prem, EOL). Stack-audit orchestrator (sap-cap-stack-audit-full) Step 0b: the ask- user template now annotates option 1 as *(default)* and adds best-for context per option. When the user defers ("I don't know yet"), the orchestrator defaults to BTP CF and emits a finding recommending the customer confirm the target before deployment-readiness sign-off. [skip ci] --- skills/sap-cap-fiori-battle-tested-patterns/SKILL.md | 2 +- skills/sap-cap-stack-audit-full/SKILL.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md index e79c1333..7cc45e56 100644 --- a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md +++ b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md @@ -265,7 +265,7 @@ CAP + Fiori Elements V4 projects deploy across **four canonical target environme | **On-Premise Kyma** | Customer-managed cluster (k3d local, Rancher, Gardener, OpenShift), uses customer IdP (Keycloak), needs full data sovereignty | `k8s-onprem` (Keycloak + HANA/PG) or `k8s-hana` (Kyma + HANA on-prem) | OIDC via customer IdP (Keycloak, Active Directory) | HANA on-prem or PostgreSQL on-prem | UI ZIPs embedded; ingress via NGINX or cluster-native | `*.svc.cluster.local` for in-cluster S/4 proxies + Cloud Connector for SaaS bridges | | **On-Premise CF** | Customer runs CF on-prem (rare; SAP Cloud Foundry On-Premise is EOL) — only if existing investment | `onprem` | XSUAA on-prem | HANA on-prem | html5-apps-repo on-prem | S/4 Flex Workflow + SMTP + filesystem DMS | -The default recommendation for a new project is **BTP Kyma** (cloud-native, pay-as-you-go, broadest customer reach). **BTP CF** when the customer mandate is "managed services only, no Kubernetes operations". **On-Premise Kyma** when the customer mandates data sovereignty or has existing Kubernetes infrastructure. **On-Premise CF** only for legacy continuity. +The default recommendation for a new project is **BTP Cloud Foundry** — it is the most widely-adopted SAP-managed runtime, has the broadest service ecosystem (Free + paid tiers for `xsuaa`, `html5-apps-repo`, `destination`, `connectivity`, `event-mesh`, `audit-log`, `job-scheduling`, …), the most mature operational tooling (`mta.yaml` + `mbt build` + `cf deploy`), and the lowest operational ceremony for teams without dedicated Kubernetes expertise. Choose **BTP Kyma** when the customer wants the Kubernetes operational model (pay-tier services, more runtime flexibility, container-native eventing, CronJob CRDs). Choose **On-Premise Kyma** when the customer mandates data sovereignty or has existing Kubernetes infrastructure. Choose **On-Premise CF** only for legacy continuity. ### 3.A — BTP Cloud Foundry patterns diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md index 102d9c9a..35307afb 100644 --- a/skills/sap-cap-stack-audit-full/SKILL.md +++ b/skills/sap-cap-stack-audit-full/SKILL.md @@ -80,14 +80,14 @@ ls k8s/onprem/keycloak-realm.json k8s/keycloak/*.json 2>/dev/null && echo " → If auto-detection is inconclusive or yields multiple candidates, ask the user explicitly: -> **Which deployment target are we auditing?** +> **Which deployment target are we auditing?** (Default: **BTP Cloud Foundry** if you're unsure — most widely-adopted SAP-managed runtime) > -> 1. **BTP Cloud Foundry** — XSUAA + HANA HDI + html5-apps-repo + mta.yaml -> 2. **BTP Kyma** — XSUAA/IAS via OIDC + PostgreSQL in-cluster or HANA Cloud + APIRule CRD + Kyma BTP Operator -> 3. **On-Premise Kyma** — Customer IdP (Keycloak) + HANA on-prem or PostgreSQL on-prem + cluster flavor (k3d / Rancher / Gardener / OpenShift) + `*.svc.cluster.local` remotes -> 4. **On-Premise CF** — legacy continuity only — XSUAA on-prem + S/4 Flex Workflow + SMTP + filesystem DMS +> 1. **BTP Cloud Foundry** *(default)* — XSUAA + HANA HDI + html5-apps-repo + mta.yaml. Best for: managed-services-only customers; lowest operational ceremony. +> 2. **BTP Kyma** — XSUAA/IAS via OIDC + PostgreSQL in-cluster or HANA Cloud + APIRule CRD + Kyma BTP Operator. Best for: customers who want Kubernetes operational model; container-native eventing. +> 3. **On-Premise Kyma** — Customer IdP (Keycloak) + HANA on-prem or PostgreSQL on-prem + cluster flavor (k3d / Rancher / Gardener / OpenShift) + `*.svc.cluster.local` remotes. Best for: data sovereignty mandates; existing Kubernetes investment. +> 4. **On-Premise CF** — legacy continuity only — XSUAA on-prem + S/4 Flex Workflow + SMTP + filesystem DMS. EOL; only if existing investment dictates. -Do **not** guess. The audit's per-target Phase 3 dispatches differ; getting this wrong wastes the agent budget. +Do **not** guess. The audit's per-target Phase 3 dispatches differ; getting this wrong wastes the agent budget. When the user defers ("I don't know yet"), default to **BTP Cloud Foundry** and emit a finding recommending the customer confirm the target before deployment-readiness sign-off. ### 0c — Persist the target for downstream phases From 589d10fb2b4f18139918acc110199a3843eb0c87 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:56:08 +0200 Subject: [PATCH 12/18] fix(sap-erp-clean-core-refactor): default target btp-cf + remove onprem-cf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two coordinated changes to align the skill's deployment-target story with operational reality: 1. Default target is now BTP Cloud Foundry (was implicit BTP Kyma in the original examples). Rationale: - BTP CF is the most widely-adopted SAP-managed runtime — broadest customer reach. - BTP CF has the broadest service ecosystem (Free + paid tiers for xsuaa, html5-apps-repo, destination, connectivity, event-mesh, audit-log, job-scheduling, …) — many of those have Free tiers that Kyma cannot consume. - BTP CF tooling is the most mature (mta.yaml + mbt build + cf deploy). - BTP CF has the lowest operational ceremony for teams without dedicated Kubernetes expertise — most SAP customers do not have a K8s operations team. BTP Kyma remains the right choice when the customer explicitly wants the Kubernetes operational model (pay-tier services, more runtime flexibility, container-native eventing, CronJob CRDs). The Examples section now shows --target=btp-kyma as an explicit override, with the default invocation `ZFI plan` going to BTP CF. 2. Removed `onprem-cf` from the supported targets. Rationale: SAP Cloud Foundry On-Premise reached end-of-maintenance. The skill should not propose deployment plans against an EOL target. Customers with existing CF on-prem investment should be advised to migrate to BTP CF (managed) or BTP Kyma, not to keep extending on the EOL runtime. Changes: - Input table: --target= now lists btp-cf (default) / btp-kyma / onprem-kyma — onprem-cf removed. - Examples updated to use `ZFI plan` (no explicit target) for the default case, and `--target=btp-kyma` for the explicit override. - Plan output template: side-by-side runtime variable list now reads <btp-cf | btp-kyma | onprem-kyma>. - BTP vs On-Premise Differences table: onprem-cf row removed; btp-cf marked as default. This is a coordinated change with the master skill and orchestrator on the Wave 2-3 branch (PR #280, separate commit f24b31e + follow-up). [skip ci] --- skills/sap-erp-clean-core-refactor/SKILL.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index b752d612..53bf705d 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -51,7 +51,7 @@ Single argument with format `<package-or-object> [mode] [flags]`: | `<package>` | Top-level customer package (e.g. `ZFI`, `ZHR`, `Y_CUSTOM`) — recursively scoped | | `<object>` | Single object focus mode (e.g. `ZCL_INVOICE_HANDLER`, `ZFM_GET_BP_DATA`) | | `mode` | `discover` · `plan` (default) · `execute` | -| `--target=` | `btp-cf` · `btp-kyma` · `onprem-kyma` · `onprem-cf` (drives side-by-side target choice) | +| `--target=` | `btp-cf` *(default)* · `btp-kyma` · `onprem-kyma` (drives side-by-side target choice). Default is BTP Cloud Foundry — most widely-adopted SAP-managed runtime, broadest service ecosystem (Free + paid tiers), mature `mta.yaml` tooling, lowest operational ceremony | | `--force-refresh` | Bypass the local cache; re-query the source even if cached < 30 days | | `--budget=N` | Override the default per-finding Apify lookup budget (default 5 pages) | | `--aggressive` | Synonym for `--target-level=A`. For every Level B finding, also research Level A escalation paths (rewrite via Customizing OR side-by-side extraction). Emits **multi-option proposals** instead of default `keep_at_level_b`. Adds ~30-50% to plan generation cost | @@ -60,12 +60,13 @@ Single argument with format `<package-or-object> [mode] [flags]`: | `--target-level=B` | **Minimal-compliance mode** — settle for B everywhere. For Level C, pick the cheapest path that reaches B (instead of A via released API) when that saves effort. For Level D, BAdI-rewrite only (never side-by-side just for "purity"). Useful when customer's appetite is "good enough compliance with minimum disruption" | Examples: -- `ZFI plan --target=btp-kyma` — typical first call (default: Level B kept as B) +- `ZFI plan` — typical first call (defaults: target=`btp-cf`, mode=`plan`, Level B kept as B) - `ZCL_INVOICE_HANDLER plan` — single-object focus -- `ZFI execute --target=btp-kyma` — apply the plan (requires confirmation per object) +- `ZFI plan --target=btp-kyma` — override target to Kyma when the customer wants the Kubernetes operational model +- `ZFI execute` — apply the plan (requires confirmation per object) - `ZFI plan --force-refresh` — re-query every source even if cached -- `ZFI plan --target=btp-kyma --aggressive` — explore Level A escalation for every Level B -- `ZFI plan --target=btp-kyma --push-to-a=ZTABLE_TAX_RATES_LOCAL,ZCL_VENDOR_LOOKUP_EXT` — selective B→A for two specific objects +- `ZFI plan --aggressive` — explore Level A escalation for every Level B (target stays at default `btp-cf`) +- `ZFI plan --target=btp-kyma --push-to-a=ZTABLE_TAX_RATES_LOCAL,ZCL_VENDOR_LOOKUP_EXT` — Kyma target + selective B→A for two specific objects ## Step 1: Pre-flight @@ -336,7 +337,7 @@ Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: ## Target deployment - Side-by-side framework: SAP CAP (Node.js + TypeScript) -- Side-by-side runtime: <btp-cf | btp-kyma | onprem-kyma | onprem-cf> +- Side-by-side runtime: <btp-cf | btp-kyma | onprem-kyma> - Lookup budget: 5 Apify pages + 1 git lookup + 1 MCP query per finding (default) - Apify lookups consumed: <N> (estimated cost: €<X>) - Cache hits: <N> (no cost; reused from prior runs) @@ -439,10 +440,9 @@ The decision tree is the same; what differs is the **side-by-side target framewo | Target | Side-by-side framework | Storage | Eventing | |---|---|---|---| -| BTP CF | CAP Node.js / TypeScript | HANA HDI | BTP Event Mesh | +| BTP CF *(default)* | CAP Node.js / TypeScript | HANA HDI | BTP Event Mesh | | BTP Kyma | CAP Node.js / TypeScript | PostgreSQL in-cluster or HANA Cloud | BTP Event Mesh or Kyma-native NATS | | On-Premise Kyma | CAP Node.js / TypeScript | HANA on-prem or PostgreSQL on-prem | Kyma-native NATS | -| On-Premise CF | CAP Node.js / TypeScript (legacy) | HANA on-prem | SMTP / file-poll | See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-3--btp--kyma--on-premise-deployment-lessons`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) Category 3 for per-target deployment patterns. From 50113fb9e3166324d46ae41dd96a12a179b33230 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 15:57:30 +0200 Subject: [PATCH 13/18] =?UTF-8?q?fix(skills):=20drop=20On-Premise=20CF=20t?= =?UTF-8?q?arget=20=E2=80=94=20not=20supported?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the On-Premise Cloud Foundry deployment target from both the master skill (sap-cap-fiori-battle-tested-patterns Category 3) and the orchestrator (sap-cap-stack-audit-full Step 0). Rationale: SAP Cloud Foundry On-Premise reached end of maintenance. The skills should not propose deployment plans against an EOL runtime. Customers with existing on-prem CF investment should be advised to migrate to BTP CF (managed) or BTP Kyma, not to extend the EOL footprint. Changes: Master skill (sap-cap-fiori-battle-tested-patterns): - Category 3.0 decision matrix: On-Premise CF row removed; added a callout block "Not supported: On-Premise Cloud Foundry" with migration recommendation. - Narrative paragraph: "Choose On-Premise CF only for legacy continuity" sentence removed. - Section 3.D On-Premise CF patterns + 3.D.1 EOL pattern: fully removed (no longer relevant once the target is unsupported). Orchestrator (sap-cap-stack-audit-full): - Step 0a auth indicator detection: XSUAA hint now reads "BTP CF" only (was "BTP CF or On-Prem CF"). - Step 0b ask-user template: option 4 (On-Premise CF) removed. Added "Not supported" note: if auto-detection finds an on-prem CF environment, the orchestrator emits a strategic finding recommending migration and stops. - Step 0c valid $TARGET values: now btp-cf / btp-kyma / onprem-kyma (onprem-cf removed). Coordinated with the corresponding change on the erp-clean-core-refactor skill (PR #281, commit 589d10f) which made the same removal there. [skip ci] --- .../SKILL.md | 21 +++---------------- skills/sap-cap-stack-audit-full/SKILL.md | 7 ++++--- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md index 7cc45e56..e2d79814 100644 --- a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md +++ b/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md @@ -263,9 +263,10 @@ CAP + Fiori Elements V4 projects deploy across **four canonical target environme | **BTP Cloud Foundry** | Customer is fully BTP-managed, has CF entitlements, accepts BTP-managed services (Free or pay) | `production` (HANA HDI) or `production-pg` (PostgreSQL, deprecated 2026-Q4) | XSUAA | HANA HDI or BTP PostgreSQL service | `@sap/html5-app-repo` (Free plan OK) + approuter | BTP Destination service + Cloud Connector | | **BTP Kyma** | Customer wants Kubernetes operational model, BTP-hosted but more flexible than CF, uses pay-tier or in-cluster services | `k8s` | OIDC (XSUAA or IAS via OAuth2) | PostgreSQL in-cluster (Bitnami Helm) or HANA Cloud | UI ZIPs embedded in approuter Docker image | `S4_BASE_URL` + Destination via Kyma BTP Operator | | **On-Premise Kyma** | Customer-managed cluster (k3d local, Rancher, Gardener, OpenShift), uses customer IdP (Keycloak), needs full data sovereignty | `k8s-onprem` (Keycloak + HANA/PG) or `k8s-hana` (Kyma + HANA on-prem) | OIDC via customer IdP (Keycloak, Active Directory) | HANA on-prem or PostgreSQL on-prem | UI ZIPs embedded; ingress via NGINX or cluster-native | `*.svc.cluster.local` for in-cluster S/4 proxies + Cloud Connector for SaaS bridges | -| **On-Premise CF** | Customer runs CF on-prem (rare; SAP Cloud Foundry On-Premise is EOL) — only if existing investment | `onprem` | XSUAA on-prem | HANA on-prem | html5-apps-repo on-prem | S/4 Flex Workflow + SMTP + filesystem DMS | -The default recommendation for a new project is **BTP Cloud Foundry** — it is the most widely-adopted SAP-managed runtime, has the broadest service ecosystem (Free + paid tiers for `xsuaa`, `html5-apps-repo`, `destination`, `connectivity`, `event-mesh`, `audit-log`, `job-scheduling`, …), the most mature operational tooling (`mta.yaml` + `mbt build` + `cf deploy`), and the lowest operational ceremony for teams without dedicated Kubernetes expertise. Choose **BTP Kyma** when the customer wants the Kubernetes operational model (pay-tier services, more runtime flexibility, container-native eventing, CronJob CRDs). Choose **On-Premise Kyma** when the customer mandates data sovereignty or has existing Kubernetes infrastructure. Choose **On-Premise CF** only for legacy continuity. +> **Not supported**: On-Premise Cloud Foundry. SAP Cloud Foundry On-Premise reached end of maintenance; the skill does not propose deployment plans against an EOL runtime. Customers with existing CF on-prem investment should be advised to migrate to **BTP CF** (managed) or **BTP Kyma**, not to extend the EOL footprint. + +The default recommendation for a new project is **BTP Cloud Foundry** — it is the most widely-adopted SAP-managed runtime, has the broadest service ecosystem (Free + paid tiers for `xsuaa`, `html5-apps-repo`, `destination`, `connectivity`, `event-mesh`, `audit-log`, `job-scheduling`, …), the most mature operational tooling (`mta.yaml` + `mbt build` + `cf deploy`), and the lowest operational ceremony for teams without dedicated Kubernetes expertise. Choose **BTP Kyma** when the customer wants the Kubernetes operational model (pay-tier services, more runtime flexibility, container-native eventing, CronJob CRDs). Choose **On-Premise Kyma** when the customer mandates data sovereignty or has existing Kubernetes infrastructure. ### 3.A — BTP Cloud Foundry patterns @@ -519,22 +520,6 @@ Exit cleanly with actionable error before consuming time / state. **Remedy.** Configure `S4_BASE_URL` (or equivalent) to use Kubernetes service DNS: `http://s4-proxy.s4-namespace.svc.cluster.local:8080`. The CAP runtime resolves it cluster-internally without exiting to public DNS. Faster, more secure, no Cloud Connector needed for in-cluster paths. -### 3.D — On-Premise CF patterns - -#### 3.D.1 — On-prem CF is EOL — only for legacy continuity - -**Symptom.** Customer mandates CF on-prem because that's what they invested in 2018. - -**Root cause.** SAP Cloud Foundry On-Premise reached end-of-maintenance; community equivalents (CF Open Source) lack the SAP integration shims. - -**Remedy.** Surface this as a strategic finding to the customer. If continuity is non-negotiable, the `onprem` profile bundles: -- XSUAA on-prem (legacy installation). -- HANA on-prem. -- S/4 Flex Workflow (instead of BPA). -- SMTP relay (instead of BTP notifications). -- Filesystem DMS (instead of cloud-based document storage). -Document each shim's substitution explicitly in the project README so the customer is aware of the divergence from the BTP feature set. - ### 3.1 — `cds run` does NOT mount UI5 apps **Symptom.** Production deployment serves the OData service but the UI5 apps return 404. diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md index 35307afb..94cadee4 100644 --- a/skills/sap-cap-stack-audit-full/SKILL.md +++ b/skills/sap-cap-stack-audit-full/SKILL.md @@ -72,7 +72,7 @@ grep -lE "ingress\.kubernetes\.io|networking\.k8s\.io" k8s/*.yaml 2>/dev/null | grep -oE '"(production|production-pg|k8s|k8s-hana|k8s-onprem|onprem)"' .cdsrc*.json package.json 2>/dev/null | sort -u # Auth indicator -test -f xs-security.json && echo " → XSUAA (BTP CF or On-Prem CF)" +test -f xs-security.json && echo " → XSUAA (BTP CF)" ls k8s/onprem/keycloak-realm.json k8s/keycloak/*.json 2>/dev/null && echo " → Keycloak (On-Prem Kyma)" ``` @@ -85,13 +85,14 @@ If auto-detection is inconclusive or yields multiple candidates, ask the user ex > 1. **BTP Cloud Foundry** *(default)* — XSUAA + HANA HDI + html5-apps-repo + mta.yaml. Best for: managed-services-only customers; lowest operational ceremony. > 2. **BTP Kyma** — XSUAA/IAS via OIDC + PostgreSQL in-cluster or HANA Cloud + APIRule CRD + Kyma BTP Operator. Best for: customers who want Kubernetes operational model; container-native eventing. > 3. **On-Premise Kyma** — Customer IdP (Keycloak) + HANA on-prem or PostgreSQL on-prem + cluster flavor (k3d / Rancher / Gardener / OpenShift) + `*.svc.cluster.local` remotes. Best for: data sovereignty mandates; existing Kubernetes investment. -> 4. **On-Premise CF** — legacy continuity only — XSUAA on-prem + S/4 Flex Workflow + SMTP + filesystem DMS. EOL; only if existing investment dictates. + +**Not supported**: On-Premise Cloud Foundry (SAP Cloud Foundry On-Premise reached end of maintenance — the orchestrator refuses to plan deployment-readiness against an EOL runtime). If auto-detection identifies an on-prem CF environment, emit a strategic finding recommending migration to BTP CF (managed) or BTP Kyma, and stop. Do **not** guess. The audit's per-target Phase 3 dispatches differ; getting this wrong wastes the agent budget. When the user defers ("I don't know yet"), default to **BTP Cloud Foundry** and emit a finding recommending the customer confirm the target before deployment-readiness sign-off. ### 0c — Persist the target for downstream phases -Record the chosen target as `$TARGET` (one of `btp-cf`, `btp-kyma`, `onprem-kyma`, `onprem-cf`). Every subsequent phase consults `$TARGET`: +Record the chosen target as `$TARGET` (one of `btp-cf`, `btp-kyma`, `onprem-kyma`). Every subsequent phase consults `$TARGET`: | Phase | What changes with target | |---|---| From cf8811a90f780bae6e6121ec5d066b898776979d Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 16:09:01 +0200 Subject: [PATCH 14/18] feat(sap-erp-clean-core-refactor): enforce ARC-1 MCP + skills + secondsky integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deep integration with the surrounding tooling — ARC-1 MCP, arc-1 native skills, and secondsky/sap-skills plugins — to make the Level A refactor workflow rigorously grounded instead of relying on agent intuition. New SKILL.md additions: - Step 1e — Bootstrap system context (prereq). Runs bootstrap-system- context once per system to ground subsequent decisions in system-info.md (release, components, ATC preset, formatter). - Step 2e — Impact / dependency analysis via SAPContext(action=impact). THE most important MCP call: fan-in count drives effort × risk multipliers (0 callers = orphan/remove; 50+ = mandatory architectural review). Without this, the rewrite-vs-extract decision is blind. - Step 4d-quater — Pattern mining via SAPRead(VERSIONS, VERSION_SOURCE). Mines the customer's own history for refactor patterns already emerged (ZCL_*_OLD, ZCL_*_V2, version-history on the package). Reduces rewrite effort 30-50% on customers with established conventions. The mined pattern is cited in the plan alongside the SAP-released-API citation. - Step 4d (extended) — `explain-abap-code` invocation for research_required findings; narrows human research scope ~50%. - Step 6-pre — Generate regression tests BEFORE rewrite via generate-abap-unit-test (CLAS/FUGR) / generate-cds-unit-test (DDLS). The tests capture current behaviour as a baseline; after rewrite they become the regression gate. Objects where test generation is infeasible are flagged `rewrite_in_place_no_tests`. - Step 6a — Adds SAPLint(format) to honor project formatter, SAPDiagnose(run_unit_tests) for regression validation, generate- rap-logic + generate-rap-service-researched delegation when rewrite goes to RAP. - Step 6b — Cross-link convert-ui5-to-fiori-elements for the UI side of the side-by-side scaffold. - Step 6c — Delegate Level B documentation to sap-object-documenter (was inline SAPWrite(attach_sktd); now uses the dedicated skill that produces structured rationale). - Step 6.5 — Transport management: SAPTransport(requirement_check) before activate, SAPTransport(create|reassign) with phase/cluster strategy. Optional gCTS/abapGit auto-commit via SAPGit when SAP_ALLOW_GIT_WRITES=true. - Step 7 — Cumulative ATC regression + unit test full run + analyze-chat-session for learnings capture. Recommended Companion Plugins section restructured into MUST / SHOULD / OPTIONAL tiers: MUST (4 secondsky plugins): - sap-abap: ABAP language patterns reference, required during every rewrite_in_place so the generated ABAP is Cloud-compatible. - sap-abap-cds: CDS view design reference, required when rewrite introduces new CDS views. - sap-cap-capire: CAP framework with 4 dispatchable agents (cap-cds-modeler, cap-service-developer, cap-performance-debugger, cap-project-architect) invoked during side-by-side scaffold. - sap-btp-developer-guide: comprehensive BTP reference for target resolution and scaffold. SHOULD (5 plugins): sapui5, sap-fiori-tools, sapui5-linter, sap-btp-cloud-platform, sap-btp-connectivity, sap-cloud-sdk, sap-cloud-sdk-ai. OPTIONAL (8 plugins): Apify MCP, mcp-sap-docs, context7, sap-btp-job-scheduling, sap-btp-cloud-logging, sap-btp- master-data-integration, sap-btp-cloud-transport-management, sap-btp-cias, sap-btp-business-application-studio, sap-btp- integration-suite, sap-btp-build-work-zone-advanced, sap-btp-intelligent-situation-automation. arc-1 native skill chain (12 skills cross-linked in the steps): bootstrap-system-context, setup-abap-mirror, explain-abap-code, sap-clean-core-atc, sap-unused-code, sap-object-documenter, generate-abap-unit-test, generate-cds-unit-test, generate-rap- logic, generate-rap-service-researched, migrate-segw-to-rap, migrate-custom-code, modernize-abap-to-btp-cap chain, convert-ui5-to-fiori-elements, analyze-chat-session. New file INTEGRATIONS.md (173 lines): - Architectural roles (ARC-1 MCP = hands; arc-1 skills = playbook; secondsky = library of patterns; this skill = orchestrator). - Step-by-step integration map (7 tables, one per workflow step, columns: ARC-1 MCP × arc-1 skills × secondsky plugin × external). - Coverage assessment: this skill engages all 12 ARC-1 MCP tools at least once (only SAPNavigate is implicit via SAPContext). - Coverage assessment: 4 secondsky MUST + 7 SHOULD + 12 OPTIONAL plugins mapped; clear "NOT used by this skill" exclusion list. - Cost model by integration layer: €0 for ARC-1 / arc-1 skills / secondsky; €0.50-€5 for Apify JIT lookups (user-paid). README.md update: - Skill ships THREE files (was two): SKILL.md + SOURCES.md + INTEGRATIONS.md. - Required companion plugins section calls out the 4 secondsky MUST plugins with hyperlinks to their secondsky repo paths. - Notes the engagement of all 12 ARC-1 MCP tools and 12+ arc-1 native skills as a delegation chain. [skip ci] --- skills/README.md | 10 +- .../INTEGRATIONS.md | 173 ++++++++++++++++++ skills/sap-erp-clean-core-refactor/SKILL.md | 149 +++++++++++++-- 3 files changed, 316 insertions(+), 16 deletions(-) create mode 100644 skills/sap-erp-clean-core-refactor/INTEGRATIONS.md diff --git a/skills/README.md b/skills/README.md index 05a2ee50..ae8446d9 100644 --- a/skills/README.md +++ b/skills/README.md @@ -108,7 +108,15 @@ Plans and executes the refactor of custom ABAP / ERP / S/4HANA code back to Clea |---|---|---| | [sap-erp-clean-core-refactor](sap-erp-clean-core-refactor/SKILL.md) | Inventories Z/Y custom code via ARC-1, classifies Clean Core Level A/B/C/D, consults authoritative SAP sources just-in-time (git-clone of `abap-atc-cr-cv-s4hc` + curated `SAP-samples` + `cloud-sdk`; Apify on-demand for `api.sap.com` / `help.sap.com` / `developers.sap.com` / `cap.cloud.sap` / community / blogs), and emits a per-object refactor plan with rewrite-in-place / extract-to-side-by-side / keep-at-B decisions. Apify lookups bounded per finding (~5 pages); user pays for own Apify account; results cached locally for 30 days | Planning a Clean Core compliance program; pre / post-S/4HANA migration cleanup; quarterly governance review | -The skill ships **two files**: [`SKILL.md`](sap-erp-clean-core-refactor/SKILL.md) (the protocol) + [`SOURCES.md`](sap-erp-clean-core-refactor/SOURCES.md) (the curated catalog of 23 authoritative SAP sources organized in 4 tiers — Tier 1 git-cloneable, Tier 2 JIT Apify, Tier 3 manual-consultation auth-gated, Tier 4 MCP-server-backed). **No centralized KB is shipped**; documentation lookups happen on-demand within a bounded per-finding budget, charged to the user's own Apify account at ~€0.005-0.02 per lookup (typical refactor: €0.50-€5 total). +The skill ships **three files**: [`SKILL.md`](sap-erp-clean-core-refactor/SKILL.md) (the protocol), [`SOURCES.md`](sap-erp-clean-core-refactor/SOURCES.md) (the curated catalog of 23 authoritative SAP sources organized in 4 tiers — Tier 1 git-cloneable, Tier 2 JIT Apify, Tier 3 manual-consultation auth-gated, Tier 4 MCP-server-backed), and [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md) (the step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × [secondsky/sap-skills](https://github.com/secondsky/sap-skills) plugin × external sources). **No centralized KB is shipped**; documentation lookups happen on-demand within a bounded per-finding budget, charged to the user's own Apify account at ~€0.005-0.02 per lookup (typical refactor: €0.50-€5 total). + +**Required companion plugins** (the skill is materially less useful without them — all four enforced as MUST in [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md)): +- **`sap-abap`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap)) — ABAP language patterns reference, required during Step 6a `rewrite_in_place` so generated ABAP is Cloud-compatible. +- **`sap-abap-cds`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap-cds)) — CDS view design reference, required when rewrite introduces new CDS views. +- **`sap-cap-capire`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-cap-capire)) — CAP framework, ships 4 dispatchable agents (`cap-cds-modeler`, `cap-service-developer`, `cap-performance-debugger`, `cap-project-architect`) invoked during Step 6b side-by-side scaffold. +- **`sap-btp-developer-guide`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-btp-developer-guide)) — comprehensive BTP reference, required during target resolution and scaffold generation. + +The skill engages **all 12 ARC-1 MCP tools** (`SAPRead` source + VERSIONS, `SAPWrite` in-place rewrite, `SAPContext` impact analysis — the most important call, `SAPLint` ATC + formatting, `SAPDiagnose` unit tests, `SAPTransport`, …) and **9 arc-1 native skills** as a delegation chain (`bootstrap-system-context`, `setup-abap-mirror`, `sap-clean-core-atc`, `sap-unused-code`, `explain-abap-code`, `sap-object-documenter`, `generate-abap-unit-test`, `generate-cds-unit-test`, `generate-rap-logic`, `modernize-abap-to-btp-cap` chain, `convert-ui5-to-fiori-elements`, `analyze-chat-session`). See [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md) for the full coverage table. ### Clean Core & Custom Code Retirement diff --git a/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md b/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md new file mode 100644 index 00000000..becfdcac --- /dev/null +++ b/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md @@ -0,0 +1,173 @@ +# Integrations Map — Level A Refactor + +Detailed mapping of every refactor step against: +- **ARC-1 MCP tools** that perform the operation on the SAP system +- **arc-1 native skills** that the orchestrator delegates to +- **secondsky/sap-skills plugins** that provide the language / framework knowledge for the operation +- **Other external sources** (Apify, MCP servers, manual) + +Use this document to: +- Verify the skill's coverage of available tooling. +- Diagnose what's missing in an environment ("ARC-1 not configured" / "secondsky plugin X not installed" / …). +- Understand the cost/value contribution of each integration point. + +## Architectural roles + +| Layer | Role | Examples | +|---|---|---| +| **ARC-1 MCP** | the *hands* — reads/writes ABAP objects on the SAP system via ADT REST API | `SAPRead`, `SAPWrite`, `SAPActivate`, `SAPLint`, `SAPContext`, `SAPDiagnose`, `SAPTransport`, `SAPGit`, `SAPManage`, `SAPNavigate`, `SAPSearch`, `SAPQuery` | +| **arc-1 native skills** | the *playbook* — sequences of MCP operations encoded as reusable agents | `bootstrap-system-context`, `sap-clean-core-atc`, `generate-rap-logic`, `modernize-abap-to-btp-cap`, … | +| **secondsky/sap-skills plugins** | the *library of patterns* — how to write what the hands will write | `sap-abap`, `sap-abap-cds`, `sap-cap-capire` (with 4 agents), `sap-btp-developer-guide`, `sapui5`, `sap-fiori-tools`, … | +| **This skill** (`sap-erp-clean-core-refactor`) | the *orchestrator* — decides which patterns to apply using which hands following which playbook | (you are here) | +| **External knowledge** | JIT documentation lookup when local cache misses | Apify (per-page paid), MCP-sap-docs (free when installed), WebFetch (free for simple HTML) | + +## Step-by-step integration map + +### Step 1 — Pre-flight + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 1a — ARC-1 connectivity probe | `SAPSearch(package_lookup, "<pkg>")` | — | — | — | Cheap call to verify the server responds + auth works | +| 1b — Apify MCP availability | — | — | — | `mcp__apify__*` | Degrades to manual mode if absent | +| 1c — Resolve `$TARGET` | — | — | `sap-btp-developer-guide` (target landscape reference) | — | Default `btp-cf` | +| 1d — Init local cache | (bash) | — | — | — | `.cache/sap-clean-core/` gitignored | +| 1e — Bootstrap system context | `SAPManage(probe_features)` + `SAPRead(SKTD)` | **`bootstrap-system-context`** | — | — | One-time per system; produces `system-info.md` | + +### Step 2 — Inventory + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 2a — Package enumeration | `SAPSearch(package_tree, root=<pkg>)` | — | — | — | Recursive sub-package walk | +| 2b — Object enumeration | `SAPSearch(tadir_lookup, devclass=<pkg>)` | — | — | — | All object types per package | +| 2c — Namespace filter | (post-processing) | — | — | — | Keep only `Z*`, `Y*`, customer namespace | +| 2d — Unused detection | `SAPQuery(SCMON / SUSG)` (requires `SAP_ALLOW_FREE_SQL=true`) | **`sap-unused-code`** | — | — | Last 6 months runtime hits | +| 2e — Impact analysis | **`SAPContext(action="impact")`** | — | — | — | ⚠️ **MOST IMPORTANT MCP CALL**. Fan-in count drives effort × risk multipliers | + +### Step 3 — Classification + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 3a — ATC run | `SAPLint(action="run_atc", target_level="A")` | **`sap-clean-core-atc`** | — | — | Per-object Level A/B/C/D | +| 3b — Augment with finding categories | (post-processing) | — | — | — | non-released-api / direct-db-access / modification / enhancement-point | + +### Step 4 — JIT documentation lookup + decision + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 4a — Cache-first lookup | (filesystem read) | — | — | — | 30d TTL stable, 7d community/blogs | +| 4b — Tier 1 git lookup | (filesystem grep) | — | — | git clones: `abap-atc-cr-cv-s4hc`, `SAP-samples`, `cloud-sdk` | Free, fast, authoritative for object classification | +| 4b — Tier 2 JIT Apify | — | — | — | `apify/website-content-crawler`, `apify/puppeteer-scraper` | Per-page cost ~€0.005-0.02; user pays | +| 4b — Tier 4 MCP-server lookup | — | — | — | `mcp-sap-docs`, `context7` | Preferred when installed (free) | +| 4c — Cite + cache | (filesystem write) | — | — | — | `.cache/sap-clean-core/<topic-hash>/<source>-<date>.md` | +| 4d — Budget exhaustion fallback | — | **`explain-abap-code`** (single-object deep dive) | — | — | Reduces human research effort ~50% | +| 4d-quater — Pattern mining | **`SAPRead(VERSIONS)`**, **`SAPRead(VERSION_SOURCE)`** | — | — | — | Mine customer's own history for refactor patterns. ⚠️ Reduces rewrite effort 30-50% on customers with established conventions | +| 4d-bis — Decision tree | (agent reasoning) | — | — | — | Per-object Start Level + flags → Target Level + Decision | +| 4e-4g — Level B escalation | — | — | — | — | `--aggressive` / `--push-to-a` / `--target-level=A` flags | + +### Step 5 — Plan emission + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 5a — Generate plan markdown | (filesystem write) | — | — | — | `docs/refactor/<date>-clean-core-plan.md` | +| 5b — Per-object decision rows | (templating) | — | — | — | Object / Start Level / Target Level / Decision / Replacement / Effort / Risk / KB evidence | + +### Step 6 — Execute (opt-in) + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 6-pre — Generate regression tests (CLAS/FUGR) | — | **`generate-abap-unit-test`** | `sap-abap` (test patterns reference) | — | Capture current behaviour as baseline | +| 6-pre — Generate regression tests (DDLS) | — | **`generate-cds-unit-test`** | `sap-abap-cds` (CDS Test Double Framework patterns) | — | For CDS views | +| 6a — Rewrite in-place: read + write | `SAPRead(VERSIONS)`, `SAPWrite(action="update")`, `SAPActivate(scope="object")` | **`generate-rap-logic`** (when rewrite goes to RAP behavior pool), **`generate-rap-service-researched`** (full RAP stack, rare) | **`sap-abap`** (language patterns), **`sap-abap-cds`** (CDS views if introduced) | — | Pattern-mined via Step 4d-quater | +| 6a — Format | `SAPLint(action="format")` | — | — | — | Apply project formatter from `system-info.md` | +| 6a — ATC regression | `SAPLint(action="run_atc")` | — | — | — | Gate: blocks loop if regression | +| 6a — Unit test regression | `SAPDiagnose(action="run_unit_tests")` | — | — | — | Gate: blocks loop on test failure | +| 6a — Rollback if regression | `SAPGit(revert)` | — | — | — | Requires `SAP_ALLOW_GIT_WRITES=true` | +| 6b — Side-by-side scaffold | — | **`modernize-abap-to-btp-cap`** chain, **`convert-ui5-to-fiori-elements`** (UI) | **`sap-cap-capire`** (4 agents: cap-cds-modeler, cap-service-developer, cap-performance-debugger, cap-project-architect), **`sap-btp-developer-guide`**, **`sap-fiori-tools`** + `sapui5` (UI), **`sap-btp-cloud-platform`** (service binding) | — | Per-extension CAP project under `bs/<name>/` | +| 6c — Document Level B keeper | `SAPWrite(action="attach_sktd")` | **`sap-object-documenter`** | — | — | Markdown rationale + ATC exemption update | +| 6.5 — Transport requirement check | `SAPTransport(action="requirement_check")` | — | — | — | Ensure deps reachable | +| 6.5 — Transport create / reuse | `SAPTransport(action="create"|"reassign")` | — | — | — | One TR per phase or per cluster | +| 6.5 — gCTS / abapGit commit | `SAPGit(commit)` | — | — | — | Optional; with `SAP_ALLOW_GIT_WRITES=true` | + +### Step 7 — Verification + +| Sub-step | ARC-1 MCP | arc-1 skills | secondsky plugin | External | Notes | +|---|---|---|---|---|---| +| 7a — ATC final check | `SAPLint(action="run_atc")` (whole package) | — | — | — | Cumulative regression | +| 7b — Unit test full run | `SAPDiagnose(action="run_unit_tests")` (whole package) | — | — | — | All tests including pre-existing | +| 7c — Cross-check against CAP audit | — | [`sap-cap-clean-core-enforce`](../sap-cap-clean-core-enforce/SKILL.md) (other branch) | — | — | Verify BTP-side compliance | +| 7d — Session learnings | — | **`analyze-chat-session`** | — | — | Propose new skill traps for future runs | + +## Coverage assessment + +The table below shows **what fraction of ARC-1 MCP capabilities the skill currently engages**. + +| ARC-1 MCP tool | Engagement in this skill | +|---|---| +| `SAPRead` (source + VERSIONS + VERSION_SOURCE + SKTD) | 🟢 Step 2 (inventory), Step 4d-quater (mining), Step 6a (pre-write VERSIONS check), Step 6c (read SKTD) | +| `SAPSearch` (tadir_lookup + package_tree + where_used + full-text) | 🟢 Step 2 (enumerate), Step 6 (where_used for unused removal) | +| `SAPWrite` (update + delete + attach_sktd + batch_create) | 🟢 Step 6a (update), Step 6c (attach_sktd), Step 6 unused (delete) | +| `SAPActivate` | 🟢 Step 6a (post-write activation) | +| `SAPNavigate` (go-to-definition, find references) | 🟡 Implicit in `SAPContext`; not directly called | +| `SAPQuery` (free SQL, off by default) | 🟡 Used by `sap-unused-code` (delegate) — requires `SAP_ALLOW_FREE_SQL=true` | +| `SAPTransport` | 🟢 Step 6.5 (requirement_check + create + reassign) | +| `SAPGit` | 🟡 Step 6a rollback + Step 6.5 commit (both opt-in via `SAP_ALLOW_GIT_WRITES=true`) | +| `SAPContext` (impact + reverse-deps + CDS impact) | 🟢 Step 2e (MOST IMPORTANT call — drives risk multipliers) | +| `SAPLint` (run_atc + format + get_formatter_settings) | 🟢 Step 3 (classification), Step 6a (pre+post regression + format) | +| `SAPDiagnose` (syntax + unit tests + ATC + quickfix + dumps + profiler) | 🟢 Step 6a (run_unit_tests), Step 7 (full run) | +| `SAPManage` (capability detection) | 🟢 Step 1e (via `bootstrap-system-context`) | + +**Overall**: the skill engages all 12 ARC-1 tools at least once. `SAPNavigate` is the only one used implicitly (via `SAPContext`); explicit calls are not needed for the refactor workflow. + +## Coverage assessment — secondsky/sap-skills + +The table below shows **which secondsky plugins this skill enforces as MUST / SHOULD / OPTIONAL**. + +| Plugin | Severity | Step where invoked | +|---|---|---| +| `sap-abap` | **MUST** | Step 6a rewrite_in_place (every ABAP rewrite consults its language patterns) | +| `sap-abap-cds` | **MUST** | Step 6a when rewrite introduces CDS views | +| `sap-cap-capire` (with 4 agents) | **MUST** | Step 6b side-by-side scaffold | +| `sap-btp-developer-guide` | **MUST** | Step 1c target resolution, Step 6b scaffold | +| `sapui5` (with 4 agents) | SHOULD | Step 6b when extension has UI | +| `sap-fiori-tools` | SHOULD | Step 6b Fiori Elements UI generation | +| `sapui5-linter` | SHOULD | Step 6b post-scaffold UI quality gate | +| `sap-btp-cloud-platform` | SHOULD | Step 6b service binding | +| `sap-btp-connectivity` | SHOULD | Step 6b when extension uses destinations | +| `sap-cloud-sdk` | SHOULD | Step 6b when extension uses Cloud SDK | +| `sap-cloud-sdk-ai` | OPTIONAL | Step 6b when extension is AI-heavy | +| `sap-btp-cloud-logging` | OPTIONAL | Step 6b production observability | +| `sap-btp-job-scheduling` | OPTIONAL | Step 6b when extension has scheduled jobs | +| `sap-btp-cloud-transport-management` | OPTIONAL | Step 6.5 when customer uses cTMS | +| `sap-btp-master-data-integration` | OPTIONAL | Step 6b when extension subscribes to MDI events | +| `sap-btp-cias` | OPTIONAL | Step 6b when customer uses IAS instead of XSUAA | +| `sap-btp-business-application-studio` | OPTIONAL | Hand-off documentation | +| `sap-btp-integration-suite` | OPTIONAL | Step 6b when side-by-side uses iFlows | +| `sap-btp-build-work-zone-advanced` | OPTIONAL | Step 6b when UI surfaces in Work Zone | +| `sap-btp-intelligent-situation-automation` | OPTIONAL | Step 6b when extension includes workflow logic | + +Plugins **NOT** used by this skill (out of scope): +- `sap-sqlscript`, `sap-hana-ml`, `sap-hana-cloud-data-intelligence`, `sap-hana-cli` — HANA-native dev +- `sap-datasphere`, `sap-sac-*` — analytics +- `sap-ai-core` — AI infrastructure +- `sap-api-style` — API design guidelines + +## Cost model — by integration layer + +| Layer | Cost per refactor (50-200 objects) | Who pays | +|---|---|---| +| ARC-1 MCP | €0 (server runs on user's infra) | User (server hosting) | +| arc-1 native skills | €0 (skill execution is agent-time, not API-billed) | (agent inference cost is platform-billed) | +| secondsky/sap-skills | €0 (knowledge base; no per-call cost) | (one-time install) | +| Tier 1 git clones | €0 (~100 MB on first install) | User (bandwidth) | +| Tier 2 Apify JIT | €0.50-€5 typical | User (own Apify token) | +| Tier 4 MCP-server-backed | €0 | User (server hosting) | + +**Total**: €0.50-€5 per typical customer refactor, all charged to the user's own infrastructure / accounts. No centralized cost. + +## See also + +- [`./SKILL.md`](./SKILL.md) — main protocol; this document is the integration deep-dive. +- [`./SOURCES.md`](./SOURCES.md) — authoritative SAP source catalog (Tier 1-4). +- [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) — broader companion plugin map (Category 8) for the CAP-side toolkit. +- [ARC-1 README](https://github.com/marianfoo/arc-1) — full MCP capability reference. +- [secondsky/sap-skills](https://github.com/secondsky/sap-skills) — 32-plugin SAP skill catalog. diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index 53bf705d..4afeddf6 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -105,6 +105,12 @@ grep -q "^\.cache/$" .gitignore 2>/dev/null || echo ".cache/" >> .gitignore Cache entries are gitignored. Each entry is `.cache/sap-clean-core/<sha256-of-topic>/<source-id>-<yyyy-mm-dd>.md` so the same topic queried twice in the same week reads from disk. +### 1e — Bootstrap system context (one-time per system) + +Before the first invocation against a given SAP system, run [`../bootstrap-system-context/SKILL.md`](../bootstrap-system-context/SKILL.md) to capture: SID, release, installed components, feature toggles, ATC preset, formatter settings. The output `system-info.md` grounds all subsequent decisions (which BAdIs are available on this release; which formatter to apply post-rewrite; which transport backend is configured). + +The refactor skill checks for `system-info.md` in the working directory; if missing, prompts the user to run bootstrap first. Idempotent and cheap (~30s, read-only). Without it, the plan is system-generic; with it, the plan is system-grounded. + ## Step 2: Inventory via ARC-1 MCP Discover every customer-owned object in scope. @@ -131,6 +137,26 @@ Keep only `Z*`, `Y*`, and `/<customer-namespace>/*` prefixes. Cross-check with [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) when available. Mark each row with `usage_status`: `ACTIVE` / `UNUSED` / `Z_ONLY`. +### 2e — Impact / dependency analysis (`SAPContext`) + +For every non-A object that may end up rewritten or extracted, ARC-1's `SAPContext(action="impact")` is the **most important MCP call** the skill makes. It returns: + +- **Upstream dependencies**: what the object reads / consumes. +- **Reverse dependencies (fan-in)**: who calls the object — count + identity of caller objects. +- **CDS upstream / downstream**: for CDS views, the view dependency graph. + +The fan-in count drives effort and risk estimation: + +| Fan-in | Risk multiplier | Strategy hint | +|---|---|---| +| 0 (orphan) | 0× | candidate for `remove_unused` even if SCMON shows recent hits (might be one-off) | +| 1-3 callers, all internal Z | 1× | low-risk rewrite_in_place | +| 4-10 callers | 2× | medium-risk; consider keeping a thin adapter layer when rewriting | +| 11-50 callers | 4× | high-risk rewrite; **prefer extract_to_side_by_side** if BTP available (move new logic to BTP, leave a thin proxy in ERP that's auto-deprecation-tagged) | +| 50+ callers | 8× + manual review | **mandatory architectural review** before any decision — surface as `research_required` regardless of other signals | + +Persist the impact table alongside the inventory TSV. It feeds Step 4 decision logic. + ## Step 3: Classification via `sap-clean-core-atc` Delegate to the existing [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md). Receive back per-object Clean Core Level + ATC findings + top-finding categories. @@ -199,6 +225,21 @@ Persist as `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md`. The If the per-finding lookup budget runs out without a definitive answer, mark the finding as **research-required** in the plan. Do **not** guess. The plan's "Research backlog" section flags these for human investigation. +For `research_required` findings, the skill optionally invokes [`../explain-abap-code/SKILL.md`](../explain-abap-code/SKILL.md) on the specific object to produce a structured deep-dive (object purpose, dependencies, style classification, suggested investigation paths). This narrows the human research scope from "open the code" to "verify or refute this hypothesis", reducing per-finding manual effort by ~50%. + +### 4d-quater — Pattern mining (`SAPRead(VERSIONS)`) + +Before generating rewrite suggestions for a non-trivial finding, the skill mines the project's own history via ARC-1: + +``` +SAPRead(type="VERSIONS", object_type="CLAS", object_name="<similar-Z-class>") +SAPRead(type="VERSION_SOURCE", object_type="CLAS", object_name="<class>", revision="<rev-with-relevant-change>") +``` + +The signal: how have **similar** Z objects in this package or neighbouring packages been migrated **already** by the customer's own team? Pattern matches on naming (`ZCL_*_OLD`, `ZCL_*_V2`, `ZCL_NEW_*`), version history of the target class, or `SAPTransport(action="history")` on the package. + +When a pattern emerges (e.g. "every BAPI replacement in this customer's code follows pattern X with helper Z"), the rewrite suggestion adopts the same pattern instead of a generic SAP-recommended one. **Reduces rewrite effort by 30-50% on customers with established conventions**. The mined pattern is cited in the plan alongside the SAP-released-API citation. + ### 4d-bis — Decision tree (per-object) For each Z/Y object with a starting Clean Core Level (A / B / C / D / Unused), apply this decision tree to determine the **decision** and the **target level**. The default behaviour is cost-minimizing per object; `--target-level` and `--aggressive` / `--push-to-a` flags shift the preference. @@ -408,31 +449,60 @@ Default behaviour (no flag): every Level B is kept; no escalation proposals are `mode = execute` enables real changes. The skill processes objects one at a time, asks confirmation per object, and routes each to: +### 6-pre — Generate regression tests BEFORE the rewrite + +For every object whose decision is `rewrite_in_place` and that has no existing unit test, the skill invokes [`../generate-abap-unit-test/SKILL.md`](../generate-abap-unit-test/SKILL.md) (for `CLAS` / `FUGR`) or [`../generate-cds-unit-test/SKILL.md`](../generate-cds-unit-test/SKILL.md) (for `DDLS`) **before** the rewrite is applied. + +The pre-rewrite tests capture the **current behaviour** as a baseline. After the rewrite they become the regression gate: if any pre-rewrite test fails post-rewrite, the rewrite is rolled back via `SAPGit`. Without this gate, "ATC pass" alone is not a sufficient verification — ATC checks syntax/policy compliance, not semantic equivalence. + +For objects where test generation is infeasible (too complex, too many DB dependencies), the skill flags the rewrite as **`rewrite_in_place_no_tests`** in the plan: the user can still proceed but acknowledges the higher risk. + ### 6a — Rewrite in-place (Outcome 1) Delegate to ARC-1 MCP: ``` +SAPRead(type="VERSIONS", ...) → confirm no concurrent writer SAPWrite(action="update", object_type="CLAS", object_name="ZCL_X", source="<rewritten-source>") SAPActivate(scope="object", object_type="CLAS", object_name="ZCL_X") +SAPLint(action="format", object_name="ZCL_X") ← honor project formatter from system-info.md SAPLint(action="run_atc", scope="object", object_name="ZCL_X", target_level="A") +SAPDiagnose(action="run_unit_tests", scope="object", object_name="ZCL_X") ← run regression tests ``` -Roll back via SAPGit if ATC regresses. +For rewrites that introduce RAP behavior pool logic, delegate to [`../generate-rap-logic/SKILL.md`](../generate-rap-logic/SKILL.md). For rewrites that introduce a full RAP service stack (rare in refactor but possible), delegate to [`../generate-rap-service-researched/SKILL.md`](../generate-rap-service-researched/SKILL.md). + +Roll back via `SAPGit` if ATC regresses OR regression tests fail. ### 6b — Side-by-side scaffold (Outcome 2) Delegate to [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). The source ABAP object is **not yet removed** — both coexist until QA confirms parity. Optionally annotate the ABAP source as deprecated. +When the side-by-side target has a Fiori Elements UI, [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) generates the UI on top of the new CAP service. + ### 6c — Document Level B keep-as-is (Outcome 3) -Attach SKTD documentation via ARC-1 explaining the eligible-Level-B rationale. Update ATC exemption configuration if the project uses one. +Delegate to [`../sap-object-documenter/SKILL.md`](../sap-object-documenter/SKILL.md) which produces structured SKTD documentation explaining the eligible-Level-B rationale (which BAdI / enhancement-point pattern the object uses, what business rule it implements, why no released alternative). Update ATC exemption configuration if the project uses one (`SAPLint` exemption file). + +### 6.5 — Transport management + +For every object touched in this execute pass, route the change through a coordinated transport: + +``` +SAPTransport(action="requirement_check", object="<obj>", target="<TR>") ← ensure deps reachable +SAPTransport(action="create", description="Clean Core Phase <N> — <package>") OR reuse open TR +SAPTransport(action="reassign", object="<obj>", from="<auto>", to="<chosen-TR>") +``` + +For projects with abapGit / gCTS, [`SAPGit`](https://github.com/marianfoo/arc-1) (with `SAP_ALLOW_GIT_WRITES=true`) auto-commits the change with a structured message: `chore(clean-core): rewrite ZCL_X to released API X (Phase N)`. ## Step 7: Verification After any execute action: -- ATC regression check (`SAPLint`). -- Cross-check against the audit catalog used by [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. -- Compare ATC counts before/after; any net regression aborts the execute loop and surfaces a finding. +- **ATC regression check** (`SAPLint(run_atc)`): the gate that blocks the execute loop if findings count regresses. +- **Unit test regression** (`SAPDiagnose(run_unit_tests)`): runs the tests generated in Step 6-pre PLUS pre-existing tests. Any failure aborts the loop. +- **Cross-check against the audit catalog** used by [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. +- **Compare ATC counts before/after**; any net regression aborts the execute loop and surfaces a finding. +- **Optional**: invoke [`../analyze-chat-session/SKILL.md`](../analyze-chat-session/SKILL.md) at session end to capture learnings (which rewrite patterns worked, which findings recurred) and propose new skill traps for the team. ## BTP vs On-Premise Differences @@ -522,18 +592,67 @@ For the side-by-side: ## Recommended Companion Plugins +The plugins below split into **MUST** (the skill is materially less useful without them) and **SHOULD/OPTIONAL** (situationally valuable depending on the refactor's target). + +### MUST — required for the skill to function correctly + | Plugin / Skill / MCP | Used for | |---|---| -| **ARC-1 MCP server** | ABAP discovery, classification, in-place writes | -| **Apify MCP server** (optional) | JIT documentation lookups against HTTP/SPA sources | -| `mcp-sap-docs` (optional) | Live cross-check vs Apify; preferred for SAP Help Portal queries | -| `sap-cap-capire` | CAP scaffolding patterns for the side-by-side | -| `sap-cloud-sdk` | Cloud SDK examples for the extension's S/4 consumption | -| `sap-fiori-tools` | Fiori Elements V4 scaffolding for the extension's UI | -| `sap-btp-cloud-platform` | BTP service binding reference | -| `sap-docs` | SAP Notes / Help Portal cross-reference | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. +| **ARC-1 MCP server** | All 12 ABAP-side operations: discovery (`SAPSearch`), impact (`SAPContext`), classification (`SAPLint`), pattern mining (`SAPRead`), rewrite (`SAPWrite` + `SAPActivate`), verification (`SAPDiagnose`), transport (`SAPTransport`). The skill is non-functional without it | +| **`sap-abap`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap)) | Authoritative reference for ABAP language patterns (Cloud-compatible syntax, OO, EML, RTTI/RTTC, exception handling, ABAP SQL, dynamic programming). Required during Step 6a `rewrite_in_place` so the generated ABAP is Cloud-compatible by construction | +| **`sap-abap-cds`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap-cds)) | CDS view design reference (associations, annotations, DCL access control, CURR/QUAN handling, CASE expressions, built-in functions). Required when the rewrite introduces new CDS views (e.g. replacing direct-DB-access with a view projection) | +| **`sap-cap-capire`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-cap-capire)) | CAP framework knowledge: CDS modeling, service handlers, deployment, multitenancy. Ships **4 dispatchable agents** the skill invokes during Step 6b side-by-side scaffold: `cap-cds-modeler`, `cap-service-developer`, `cap-performance-debugger`, `cap-project-architect` | +| **`sap-btp-developer-guide`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-btp-developer-guide)) | Comprehensive BTP reference; broad enough to cover all four target deployments. Required during Step 1c side-by-side target resolution and Step 6b scaffold generation | + +### SHOULD — strongly recommended depending on refactor scope + +| Plugin / Skill | When | +|---|---| +| `sap-btp-cloud-platform` | When the side-by-side scaffold binds BTP-managed services | +| `sap-btp-connectivity` | When the extension consumes S/4 via Destination service + Cloud Connector | +| `sap-fiori-tools` | When the side-by-side extension exposes a Fiori Elements UI | +| `sapui5` ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sapui5)) | UI5 framework reference for the extension UI; ships 4 dispatchable agents (ui5-api-explorer, ui5-app-scaffolder, ui5-code-quality-advisor, ui5-migration-specialist) | +| `sapui5-linter` | Code quality gate for the generated UI5 | +| `sap-cloud-sdk` | When the extension uses Cloud SDK to consume S/4 services | +| `sap-cloud-sdk-ai` | When the extension includes LLM/embedding logic (e.g. document classification, vendor risk scoring) | + +### OPTIONAL — situational + +| Plugin / Skill | When | +|---|---| +| **Apify MCP server** | JIT documentation lookups against HTTP/SPA sources (api.sap.com, help.sap.com). Falls back to manual mode if absent | +| `mcp-sap-docs` | Preferred over Apify for SAP Help Portal queries when installed | +| `context7` | Generic library docs for non-SAP dependencies in the extension | +| `sap-btp-job-scheduling` | When the side-by-side includes scheduled jobs | +| `sap-btp-cloud-logging` | Observability for the production extension | +| `sap-btp-master-data-integration` | When the extension subscribes to MDI events (BusinessPartnerChanged etc.) | +| `sap-btp-cloud-transport-management` | When the customer uses cTMS for coordinated transport across S/4 + BTP | +| `sap-btp-cias` | When the customer uses SAP Cloud Identity Services (IAS) instead of XSUAA | +| `sap-btp-business-application-studio` | Dev environment reference for hand-off to the customer's developer team | +| `sap-btp-integration-suite` | When the side-by-side pattern uses iFlows instead of direct CAP | +| `sap-btp-build-work-zone-advanced` | When the extension UI must surface in SAP Work Zone | + +### arc-1 native skill chain (already cross-linked in the steps above) + +| arc-1 skill | Invoked from | +|---|---| +| [`bootstrap-system-context`](../bootstrap-system-context/SKILL.md) | Step 1e (prereq) | +| [`setup-abap-mirror`](../setup-abap-mirror/SKILL.md) | Step 1 (optional read-only exploration) | +| [`explain-abap-code`](../explain-abap-code/SKILL.md) | Step 4d (research_required fallback) | +| [`sap-clean-core-atc`](../sap-clean-core-atc/SKILL.md) | Step 3 (delegate) | +| [`sap-unused-code`](../sap-unused-code/SKILL.md) | Step 2d (delegate) | +| [`sap-object-documenter`](../sap-object-documenter/SKILL.md) | Step 6c (delegate Level B documentation) | +| [`generate-abap-unit-test`](../generate-abap-unit-test/SKILL.md) | Step 6-pre (regression test for CLAS/FUGR before rewrite) | +| [`generate-cds-unit-test`](../generate-cds-unit-test/SKILL.md) | Step 6-pre (regression test for DDLS before rewrite) | +| [`generate-rap-logic`](../generate-rap-logic/SKILL.md) | Step 6a (when rewrite introduces RAP behavior pool logic) | +| [`generate-rap-service-researched`](../generate-rap-service-researched/SKILL.md) | Step 6a (when rewrite introduces full RAP stack — rare) | +| [`migrate-segw-to-rap`](../migrate-segw-to-rap/SKILL.md) | Step 6a (when the Z package contains a SEGW V2 service to modernize in parallel) | +| [`migrate-custom-code`](../migrate-custom-code/SKILL.md) | Step 6a (sibling — ATC fix patterns for rewrite findings) | +| [`modernize-abap-to-btp-cap`](../modernize-abap-to-btp-cap/SKILL.md) chain | Step 6b (side-by-side scaffold orchestrator) | +| [`convert-ui5-to-fiori-elements`](../convert-ui5-to-fiori-elements/SKILL.md) | Step 6b (Fiori Elements UI on top of new CAP service) | +| [`analyze-chat-session`](../analyze-chat-session/SKILL.md) | Step 7 (session-end learnings capture) | + +See [`./INTEGRATIONS.md`](./INTEGRATIONS.md) for the full step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × secondsky plugin. See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the broader companion plugin map across CAP audit skills. ## See also From 33370a11dc145388277e439886efd5d0e051b595 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 16:15:41 +0200 Subject: [PATCH 15/18] docs: SAP CAP + Clean Core toolkit consolidated overview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds docs/sap-cap-toolkit.md (~770 lines) — a single navigation guide consolidating the 13-skill toolkit contributed across PRs #278, #279, #280, and #281. The doc is the entry point for users who want to understand the toolkit holistically rather than reading each SKILL.md in isolation. Structure (10 sections): 1. Mission — what problem the toolkit solves, audience, in/out of scope. 2. Quick start — 30-second setup + 3 most common invocations. 3. Toolkit at a glance — ASCII visual map showing 6 layers (reference, orchestration, audit, refactor, support, execution); skill count and provenance table. 4. Skill catalog — detailed per-skill description grouped by layer: - Reference (1): sap-cap-fiori-battle-tested-patterns (~70 patterns in 8 categories). - Orchestration (2): sap-cap-stack-audit-full, sap-cap-ci-gates- pattern. - Audit (5): sap-cap-clean-core-enforce, sap-cap-customizing-honor, sap-cap-security-rbac-matrix, sap-fiori-app-audit, sap-cap-text-polish. - Refactor (4): sap-erp-clean-core-refactor (with SOURCES.md + INTEGRATIONS.md companions), modernize-abap-to-btp-cap chain. 5. Strategies — 6 architectural patterns: - JIT lookups (no pre-built KB) with cost rationale. - Defense-in-depth (audit → enforcement → CI gates). - Three-layer architecture (hands × playbook × library). - Decision tree D→B / C→A / B-keep with 3 escalation mechanisms. - Target deployment matrix (btp-cf default, btp-kyma, onprem-kyma; onprem-cf dropped). - Cost model (€0.50-€5 per refactor, user-paid). 6. Dependencies — mandatory ARC-1 MCP, 4 MUST secondsky plugins (sap-abap, sap-abap-cds, sap-cap-capire, sap-btp-developer-guide), 7 SHOULD plugins, optional Apify/mcp-sap-docs/context7/playwright, Tier-1 git clones, Tier-2 JIT Apify. 7. Usage examples — 7 worked end-to-end scenarios: A. End-to-end Clean Core refactor of a Z* package B. Pre-release stack audit (BTP CAP project) C. Security & compliance review (OWASP/NIST/GDPR evidence) D. Single Fiori app audit E. Text polish before release + PII safety check F. Setting up CI gates from scratch G. Side-by-side extension scaffold from a Z FM 8. PR roadmap — the 4 open PRs + this consolidation, total ~6800 lines, 100% generic. 9. FAQs — 8 common questions (why JIT? what if no BTP? differences between sap-clean-core-atc / sap-erp-clean-core-refactor / sap-cap-clean-core-enforce? why no on-prem CF? etc.). 10. References — links to every skill SKILL.md, supporting arc-1 native skills, external documentation, methodology references. skills/README.md updated with a top-of-file link to the toolkit doc so readers landing on the skills index see the consolidated overview. This PR is structurally independent of PRs #278/#279/#280/#281. The toolkit doc references skills that ship in those PRs; cross-links degrade gracefully to dead links until those PRs merge. Once all four ship, the toolkit doc becomes fully navigable. [skip ci] --- docs/sap-cap-toolkit.md | 772 ++++++++++++++++++++++++++++++++++++++++ skills/README.md | 2 + 2 files changed, 774 insertions(+) create mode 100644 docs/sap-cap-toolkit.md diff --git a/docs/sap-cap-toolkit.md b/docs/sap-cap-toolkit.md new file mode 100644 index 00000000..cabb8c0b --- /dev/null +++ b/docs/sap-cap-toolkit.md @@ -0,0 +1,772 @@ +# SAP CAP + Clean Core Toolkit — Overview & Navigation Guide + +A consolidated guide to the **13-skill SAP toolkit** contributed across PRs #278, #279, #280, and #281: end-to-end coverage from ABAP custom-code refactor through CAP service development to BTP deployment, with audits, gates, and battle-tested patterns. + +--- + +## 1. Mission + +SAP customers carry decades of custom ABAP code that needs to evolve toward **Clean Core compliance** and **side-by-side BTP extensions**. The journey is multi-step: + +1. **Inventory** the custom code that exists. +2. **Classify** each object against Clean Core Levels A/B/C/D. +3. **Decide**, per object, between rewrite-in-place, side-by-side extraction, keep-at-B, or remove. +4. **Generate** the rewritten ABAP and/or the new BTP CAP scaffold. +5. **Verify** with regression tests and ATC gates. +6. **Audit** the resulting CAP+Fiori+BTP application end-to-end before deployment. +7. **Enforce** compliance as CI gates that catch regressions. + +This toolkit covers all seven steps in a coherent, generic, OSS-shareable form. Every contribution is **100% generic** — no domain-specific names, no customer-specific patterns hardcoded. The toolkit operates on **any** SAP CAP + ABAP + Fiori project. + +### Audience + +- **SAP architects** planning Clean Core programs or pre/post-S/4HANA migrations. +- **SAP delivery teams** building BTP applications that consume S/4HANA Tier-2 services. +- **Customer development teams** modernizing legacy ABAP custom code. +- **SAP partners** delivering Clean Core compliance assessments and refactor projects. + +### Scope + +| In scope | Out of scope | +|---|---| +| CAP service development (Node.js + TypeScript) | CAP Java runtime (patterns differ; future contribution) | +| Fiori Elements V4 apps + UI5 freestyle | SAPUI5 < 1.108 (legacy UI5; use modernize-ui5-app for migration) | +| BTP Cloud Foundry + BTP Kyma + On-Premise Kyma | On-Premise CF (EOL) | +| S/4HANA Public Cloud + Private Cloud + On-Premise | ECC < 6.0 (use SAP Activate methodology for migration first) | +| ABAP custom code refactor (Z*/Y*/namespace) | ABAP standard code (never touched) | +| Released APIs only (Clean Core Level A target) | Non-released RFC/BAPI consumption (flagged for refactor) | + +--- + +## 2. Quick start (30 seconds) + +```bash +# Pre-flight (one-time) +gh repo clone marianfoo/arc-1-fork +cd arc-1-fork +npx skills add Raistlin82/arc-1-fork -g -y # install all 13 skills +npx skills add secondsky/sap-skills -s sap-abap -g -y # required companion +npx skills add secondsky/sap-skills -s sap-abap-cds -g -y +npx skills add secondsky/sap-skills -s sap-cap-capire -g -y +npx skills add secondsky/sap-skills -s sap-btp-developer-guide -g -y + +# Set up ARC-1 MCP server connection to your SAP system (see arc-1 README) + +# Three most common invocations +/sap-erp-clean-core-refactor ZFI plan # plan a Clean Core refactor (default target=btp-cf) +/sap-cap-stack-audit-full # pre-release audit of a CAP project +/sap-cap-clean-core-enforce --enforce # block deployment if non-released API consumed +``` + +--- + +## 3. Toolkit at a glance + +### Visual map + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ REFERENCE LAYER (knowledge base) │ +│ sap-cap-fiori-battle-tested-patterns │ +│ ~70 patterns in 8 categories — UI5/FE V4 traps, CAP/TS, BTP deploy, │ +│ security, customizing, lifecycle, events, ecosystem plugins │ +└─────────────────────────────────────────────────────────────────────────┘ + ↑ cross-link by anchor + │ +┌───────────────────────────┴─────────────────────────────────────────────┐ +│ ORCHESTRATION LAYER │ +│ sap-cap-stack-audit-full (master orchestrator, asks target) │ +│ sap-cap-ci-gates-pattern (5 reusable CI gate patterns) │ +└─────────────────────────────────────────────────────────────────────────┘ + ↑ orchestrates / generates + │ +┌───────────────────────────┴─────────────────────────────────────────────┐ +│ AUDIT LAYER │ +│ sap-cap-clean-core-enforce (Tier-2 S/4 API gate, --enforce mode) │ +│ sap-cap-customizing-honor (bidirectional CSV↔code, ValueList) │ +│ sap-cap-security-rbac-matrix (OWASP/ASVS/NIST/CIS/SAP-SOM matrix) │ +│ sap-fiori-app-audit (per-app UI/UX + FE/BE contract) │ +│ sap-cap-text-polish (user-text + PII detection) │ +└─────────────────────────────────────────────────────────────────────────┘ + ↑ produces findings consumed by gates above + │ +┌───────────────────────────┴─────────────────────────────────────────────┐ +│ REFACTOR / MIGRATION LAYER │ +│ sap-erp-clean-core-refactor (Z* refactor planner + executor, JIT KB) │ +│ ├── SKILL.md — 668 lines protocol │ +│ ├── SOURCES.md — 23 SAP sources organized in 4 tiers │ +│ └── INTEGRATIONS.md — full mapping ARC-1 MCP × skill × secondsky │ +│ │ +│ modernize-abap-to-btp-cap (top-level orchestrator) │ +│ ├── modernize-abap-cap-schema (Z-table → CDS entity) │ +│ └── modernize-abap-cap-service (FM/program → CAP service action) │ +└─────────────────────────────────────────────────────────────────────────┘ + ↑ delegates to other arc-1 native skills + │ +┌───────────────────────────┴─────────────────────────────────────────────┐ +│ SUPPORT / DELEGATE SKILLS (existing arc-1 native) │ +│ bootstrap-system-context · setup-abap-mirror · explain-abap-code │ +│ sap-clean-core-atc · sap-unused-code · sap-object-documenter │ +│ generate-abap-unit-test · generate-cds-unit-test · generate-rap-logic │ +│ generate-rap-service-researched · migrate-segw-to-rap │ +│ migrate-custom-code · convert-ui5-to-fiori-elements · modernize-ui5-app │ +│ analyze-chat-session │ +└─────────────────────────────────────────────────────────────────────────┘ + ↑ executes operations via + │ +┌───────────────────────────┴─────────────────────────────────────────────┐ +│ EXECUTION LAYER (MCP servers + plugins) │ +│ ARC-1 MCP server — 12 ABAP intent-based tools (the hands) │ +│ secondsky/sap-skills — 32 plugins (the library of patterns) │ +│ ├── sap-abap (MUST) │ +│ ├── sap-abap-cds (MUST) │ +│ ├── sap-cap-capire (MUST, 4 dispatchable agents) │ +│ ├── sap-btp-developer-guide (MUST) │ +│ └── 19 SHOULD / OPTIONAL plugins │ +│ Apify MCP (JIT documentation lookup, user-paid per page) │ +│ mcp-sap-docs (when installed — free SAP doc lookup) │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### Skill count and provenance + +| Skill | Layer | PR | Provenance | +|---|---|---|---| +| `sap-cap-fiori-battle-tested-patterns` | Reference | #280 | New — distilled from production patterns | +| `sap-cap-stack-audit-full` | Orchestration | #280 | New — orchestrator | +| `sap-cap-ci-gates-pattern` | Orchestration | #280 | New — CI library | +| `sap-cap-clean-core-enforce` | Audit (gate) | #279 | New — Tier-2 S/4 API gate | +| `sap-cap-customizing-honor` | Audit | #279 | New — CSV ↔ code | +| `sap-cap-security-rbac-matrix` | Audit | #280 | New — OWASP/ASVS/NIST/CIS/SAP-SOM | +| `sap-fiori-app-audit` | Audit | #280 | New — single Fiori app | +| `sap-cap-text-polish` | Audit | #280 | New — user-text + PII | +| `sap-erp-clean-core-refactor` | Refactor | #281 | New — Z* refactor planner | +| `modernize-abap-to-btp-cap` | Refactor | #278 | New — top-level orchestrator | +| `modernize-abap-cap-schema` | Refactor | #278 | New — Z-table → CDS | +| `modernize-abap-cap-service` | Refactor | #278 | New — FM → CAP | + +**Total: 12 new skills + 1 knowledge-base reference (battle-tested-patterns) = 13 contributed artifacts.** + +--- + +## 4. Skill catalog + +### 4.1 Reference — battle-tested patterns + +#### `sap-cap-fiori-battle-tested-patterns` + +A curated reference catalog of **~70 production-distilled patterns** organized in 8 categories. Not runnable — it's a knowledge base linked by anchor from other skills. + +| Category | Pattern count | Examples | +|---|---|---| +| 1 — UI5 / Fiori Elements V4 traps | 12 | `@UI.Hidden` + `OperationAvailable` interaction; actions returning entity NO `.columns()`; `liveMode` only on small datasets; Composition vs Association for audit children | +| 2 — CAP / TypeScript pitfalls | 8 | `cds.tx` autonomous deadlock on SQLite; `forUpdate` before lifecycle UPDATE; post-commit side effects in `req.on('succeeded')`; cds.log discipline; centralized reject helper | +| 3 — BTP / Kyma / On-Premise deployment | 30+ | 4-target decision matrix (BTP CF default, BTP Kyma, On-Prem Kyma — On-Prem CF dropped as EOL); per-target sub-sections with 5-7 patterns each; Clean Core as deployment gate (3.11) | +| 4 — Security defense-in-depth | 7 | Audit log append-only 3-layer; PII sanitize before audit; magic bytes upload; per-class rate limiters; token rotation; OData $expand depth | +| 5 — Customizing-driven | 5 | SystemParameter SSOT + bounded cache; adapter dual-source fallback; per-tenant override; master-data ValueList enforcement; bidirectional CSV ↔ code | +| 6 — Lifecycle / process | 6 | Centralized phase boundary map; CAS marker for idempotent advance; touchless handler idempotency; exception auto-dispatch ordering | +| 7 — Events / messaging | 5 | Declarative event service; post-commit fire-and-forget; connection cache as Promise; idempotency key cross-emit; outbox replay mirror | +| 8 — Ecosystem plugin landscape | 16 entries | Map of all secondsky/sap-skills plugins applicable to CAP+Fiori projects | + +Each pattern: **symptom** observed by users/operators → **root cause** in framework/runtime/deployment layer → **generic remedy** with portable code snippet. + +### 4.2 Orchestration + +#### `sap-cap-stack-audit-full` + +Master orchestrator. Asks the deployment target (Step 0), runs all relevant audits in parallel, consolidates a deduplicated report. Engages every audit skill from §4.3 plus static checks (UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded customizing sweep). + +Always read-only. Output: a single markdown report under `docs/audit/<date>-stack-audit.md` with severity rollup A/B/C/D. + +#### `sap-cap-ci-gates-pattern` + +Library of **5 reusable CI gate patterns** (bidirectional CSV ↔ code; catalog raise-coverage; API-availability drift; convention/matrix drift; CSV schema lint). Two modes: +- `describe` (default) — print what each pattern would do. +- `apply` — generate the bash gate scripts + GitHub Actions / GitLab / Jenkins / BTP CI/CD workflow YAML. + +The skill never executes the gates — it generates them so the user's CI runs them per-PR. + +### 4.3 Audit + +#### `sap-cap-clean-core-enforce` + +**Discovery-driven Clean Core Level A enforcement** for CAP + S/4HANA projects. Three modes: +- `report` — read-only audit + markdown report +- `--apply` — safe additive corrections to the compatibility catalog +- `--enforce` — **CI-blocking gate** (non-zero exit on HIGH findings); pairs with `sap-cap-ci-gates-pattern` Pattern 3 + +Consults **two authoritative sources**: +1. `SAP/abap-atc-cr-cv-s4hc` (ABAP API Release State repository — JSON per object class) +2. `api.sap.com` (SAP API Hub — OData service lifecycle, Communication Scenarios) + +Per cell of the matrix (service × edition), resolves disagreements: API Hub wins for OData service questions, abap-atc wins for raw ABAP object questions. + +#### `sap-cap-customizing-honor` + +Bidirectional CSV ↔ code customizing audit. Detects: +- **Inverse orphans** — code reads a parameter that the CSV doesn't seed (admin doesn't know it's configurable). +- **Forward orphans** — CSV seeds a parameter that no code reads (admin thinks they can configure but it's ignored). +- **Hardcoded business decisions** — thresholds/timeouts in code that should be SystemParameter. +- **Master-data unreferenced** — FK fields without `@Common.ValueList` annotation. + +Has `fix` mode that applies safe additive corrections (add CSV seed for inverse-orphan; add ValueList annotation on filter bar). + +#### `sap-cap-security-rbac-matrix` + +Multi-area parallel security scan + role coherence matrix. Five area agents run in parallel (S1 handlers, S2 MCP endpoints, S3 file-upload, S4 deploy YAML, S5 background jobs), one orthogonal OWASP pass (O1, 10 categories). Maps every finding to ≥1 framework citation: + +- OWASP Top 10 2021 +- OWASP API Security Top 10 2023 +- ASVS L1-L3 +- NIST CSF 2.0 + NIST SP 800-53 +- CIS Kubernetes 1.9 / Docker 1.6 +- SAP Secure Operations Map 2024 +- GDPR + SOX 404 + +Role coherence matrix across 4 layers: `xs-security.json` ↔ IdP realm (XSUAA/Keycloak/IAS) ↔ `services-auth.cds` ↔ handlers. Drift between layers is a HIGH finding. + +#### `sap-fiori-app-audit` + +Single Fiori Elements V4 app audit. Walks the canonical user journey (entry → filter → list → detail → action → refresh), validates the frontend/backend contract chain (annotation → flag → READ projection → `@restrict` grant → handler → SideEffects → test). + +Three failure classes: +- **UX-positive-false** — button visible, backend rejects → user gets 403/409. +- **UX-negative-false** — button hidden, backend would allow → user stuck. +- **Stuck state** — no button visible, no auto-job advances → entity dead in status. + +Has `fix` mode for safe quick wins (missing `@Core.OperationAvailable`, missing `@Common.SideEffects`, missing tooltip, missing i18n fallback). + +#### `sap-cap-text-polish` + +Audit and rewrite all user-visible text — backend reject/throw, helper rejects, frontend toasts/dialogs, i18n bundles, CDS labels, CodeList descriptions. Detects 10 anti-patterns: +1. Colloquial / Telegram tone +2. Accusatory ("you did wrong") +3. Unexplained tech jargon (404, null pointer, SQL error) +4. Fragmented / incomplete ("Error.") +5. All-caps / multiple exclamations +6. Unexpanded acronyms (BP, CC, SDI, FE, PO) +7. Mixed locale +8. Missing ICU placeholders (`{0}` instead of concatenation) +9. Inconsistent punctuation +10. **PII leak** (IBAN, fiscal code, VAT, full email — automatic masking) + +Tone-profile-driven, locale-aware (resolves primary locale from the project), additive safe rewrites only. + +### 4.4 Refactor / migration + +#### `sap-erp-clean-core-refactor` (the flagship) + +Plans and executes a Level A refactor of ABAP custom code (Z*/Y*). Three modes (`discover` / `plan` / `execute`). **JIT-only** — no centralized pre-built knowledge base; documentation queried on demand within a bounded per-finding budget. + +**Decision tree** per object: + +``` +A → no_action (target: A) +Unused → remove_unused (target: N/A) +D → rewrite_in_place via released API (target: A) + OR rewrite_in_place via BAdI (target: B) + OR extract_to_side_by_side (target: A ERP-side) + OR accept_as_d_documented (last resort) +C → rewrite_in_place via released API (target: A) + OR rewrite_in_place via BAdI (target: B) + OR extract_to_side_by_side (target: A ERP-side) + OR keep_at_level_b (target: B) +B → keep_at_level_b (default) (target: B) + OR rewrite_in_place / extract (via --aggressive flag) +``` + +**Three escalation mechanisms**: +- `--aggressive` (global) — explore A escalation for every Level B finding. +- `--push-to-a=A,B,C` (selective) — only listed objects get expanded analysis. +- `--target-level=A` (ambitious) — always prefer A; `--target-level=B` (minimal) — settle for B. +- **Plan-file editing** (finest-grained) — edit the emitted markdown before `execute`. + +Three ships: +- [`SKILL.md`](../skills/sap-erp-clean-core-refactor/SKILL.md) — 668-line protocol +- [`SOURCES.md`](../skills/sap-erp-clean-core-refactor/SOURCES.md) — 23 SAP sources in 4 tiers +- [`INTEGRATIONS.md`](../skills/sap-erp-clean-core-refactor/INTEGRATIONS.md) — full ARC-1 MCP × skill × secondsky mapping + +#### `modernize-abap-to-btp-cap` chain (#278) + +Three coordinated skills for ABAP → BTP CAP modernization: + +- **`modernize-abap-to-btp-cap`** — top-level orchestrator, takes ABAP target spec, decomposes into schema + service migration. +- **`modernize-abap-cap-schema`** — Z-table / DDIC structure → CDS entity definition. Handles 24 DDIC → CDS type mappings. +- **`modernize-abap-cap-service`** — Function module / report → CAP service action implementation. Preserves semantics, adapts to handler patterns. + +Called by `sap-erp-clean-core-refactor` Step 6b when the decision is `extract_to_side_by_side`. + +--- + +## 5. Strategies (architectural patterns) + +### 5.1 JIT lookups, no pre-built KB + +A pre-crawled knowledge base of SAP documentation sounds appealing but is economically and operationally wrong: +- **Volume**: help.sap.com alone is ~50k pages. Full crawl = GBs of mostly-unread content. +- **Cost**: weekly Apify crawl of 18 sources ≈ €400-780/year. No clear funding model. +- **Staleness**: even weekly is N days behind SAP's monthly API release cadence. + +**JIT alternative**: +- Tier-1 git-cloneable sources (`abap-atc-cr-cv-s4hc`, `SAP-samples`, `cloud-sdk`) are pulled weekly via `git pull --ff-only` — free, fast, authoritative. +- Tier-2 HTTP/SPA sources are queried **per finding** via Apify on the user's own account at ~€0.005-0.02 per page. Bounded 5 lookups per finding. Cached locally for 30 days. +- Tier-3 auth-gated sources (`launchpad-support`, `me.sap.com`) are flagged as manual-consultation pointers. +- Tier-4 MCP-server-backed sources (`mcp-sap-docs`, `context7`) are preferred when installed. + +**Cost per refactor of 50-200 objects**: €0.50-€5, charged to the **user's own Apify account**. No centralized infrastructure. + +### 5.2 Defense-in-depth (audit → enforcement → CI gates) + +Every Clean Core / compliance gate has three layers, none of which can be the only one: + +1. **Audit layer** (read-only, generates report): `sap-cap-clean-core-enforce report`, `sap-cap-customizing-honor`, `sap-cap-security-rbac-matrix`, `sap-fiori-app-audit`. +2. **Enforcement layer** (CI-blocking gate): `sap-cap-clean-core-enforce --enforce` exits non-zero on HIGH findings. Wired into pre-PR and pre-deploy CI. +3. **Gate generation layer**: `sap-cap-ci-gates-pattern` produces the shell scripts + workflow YAML so the customer's CI runs the gates per-PR. + +Findings discovered by audits flow into gates so they don't regress. **Auditing without gates is busywork**. + +### 5.3 Three-layer architecture (hands × playbook × library) + +| Layer | Role | Examples | +|---|---|---| +| **ARC-1 MCP** | the *hands* — reads/writes ABAP via ADT REST API | 12 intent-based tools (SAPRead, SAPWrite, SAPContext, SAPLint, SAPDiagnose, …) | +| **arc-1 native skills** | the *playbook* — sequences of MCP operations | bootstrap-system-context, sap-clean-core-atc, generate-rap-logic, etc. | +| **secondsky/sap-skills** | the *library of patterns* — how to write what the hands write | sap-abap, sap-abap-cds, sap-cap-capire, sap-btp-developer-guide | +| **This toolkit** | the *orchestrator* — decides what to do with which hands using which book | sap-erp-clean-core-refactor + 12 sibling skills | + +Engaging only one layer is insufficient. Without `sap-abap` enforcement, the agent generates ABAP that compiles but doesn't pass ATC. Without `SAPContext(impact)`, the rewrite-vs-extract decision is blind. Without the orchestrator skill, the user has tools but no method. + +### 5.4 Decision tree D→B / C→A / B-keep + +Defaults reflect cost-minimizing paths that match the SAP-recommended pattern: + +| Starting level | Default target | Path | +|---|---|---| +| D | **B** (via BAdI / enhancement-point) | rewrite_in_place. D objects usually have business logic; BAdI rewrite is the eligible pattern | +| C | **A** (via released API) | rewrite_in_place. Released equivalent typically exists | +| B | **B** (keep + document) | keep_at_level_b. B is already compliant; pushing to A is opt-in | +| A | A | no action | +| Unused | (removed) | sign-off + delete | + +Override the defaults with `--aggressive` (push B → A everywhere), `--target-level=B` (settle for B even when A possible), or `--push-to-a=A,B,C` (selective). + +### 5.5 Target deployment matrix + +Three supported targets (On-Premise CF dropped — EOL): + +| Target | CDS profile | Auth | DB | UI delivery | +|---|---|---|---|---| +| **BTP Cloud Foundry** *(default)* | `production` / `production-pg` | XSUAA | HANA HDI / BTP Postgres | `@sap/html5-app-repo` Free OK | +| **BTP Kyma** | `k8s` | OIDC (XSUAA / IAS) | PostgreSQL in-cluster / HANA Cloud | UI ZIPs embedded in approuter image | +| **On-Premise Kyma** | `k8s-onprem` / `k8s-hana` | OIDC via Keycloak | HANA on-prem / Postgres on-prem | UI ZIPs embedded; ingress via NGINX | + +Default is BTP CF: most widely-adopted SAP-managed runtime, broadest service ecosystem (Free + paid), most mature `mta.yaml` tooling, lowest operational ceremony. BTP Kyma is the deliberate choice for Kubernetes-operational-model customers. On-Premise Kyma is for data-sovereignty mandates. + +### 5.6 Cost model + +| Layer | Cost per refactor (50-200 objects) | Who pays | +|---|---|---| +| ARC-1 MCP | €0 | User (server hosting) | +| arc-1 native skills | €0 | (agent inference platform-billed) | +| secondsky/sap-skills | €0 | (one-time install) | +| Tier-1 git clones | €0 (~100 MB on first install) | User (bandwidth) | +| Tier-2 Apify JIT | €0.50-€5 | User (own Apify token) | +| Tier-4 MCP-server | €0 | User (server hosting) | + +**Total: €0.50-€5 per typical customer refactor, all user-paid. No centralized cost.** + +--- + +## 6. Dependencies + +### 6.1 Mandatory + +**ARC-1 MCP server** ([marianfoo/arc-1](https://github.com/marianfoo/arc-1)) + +Setup: +```bash +git clone https://github.com/marianfoo/arc-1.git +cd arc-1 +cp .env.example .env +# Edit .env with SAP_HOST, SAP_CLIENT, SAP_USER, SAP_PASSWORD +# Optional: SAP_ALLOW_FREE_SQL=true (for dead-code detection), SAP_ALLOW_GIT_WRITES=true +npm install && npm run build +# Add to Claude Code MCP config (~/.claude/settings.json) as 'arc-1' server +``` + +Without ARC-1 MCP, the refactor skill cannot discover or modify ABAP code. The audit skills work without ARC-1 (CAP-side only) but are less complete. + +### 6.2 MUST companion plugins (4 from secondsky/sap-skills) + +```bash +npx skills add secondsky/sap-skills -s sap-abap -g -y +npx skills add secondsky/sap-skills -s sap-abap-cds -g -y +npx skills add secondsky/sap-skills -s sap-cap-capire -g -y +npx skills add secondsky/sap-skills -s sap-btp-developer-guide -g -y +``` + +| Plugin | Used by | +|---|---| +| `sap-abap` | `sap-erp-clean-core-refactor` Step 6a (ABAP rewrite); `modernize-abap-*` chain | +| `sap-abap-cds` | `sap-erp-clean-core-refactor` when rewrite introduces CDS views; `modernize-abap-cap-schema` | +| `sap-cap-capire` (4 agents) | `sap-erp-clean-core-refactor` Step 6b (side-by-side); `modernize-abap-to-btp-cap` | +| `sap-btp-developer-guide` | All skills that resolve the deployment target | + +### 6.3 SHOULD companion plugins (7) + +```bash +npx skills add secondsky/sap-skills -s sapui5 -g -y +npx skills add secondsky/sap-skills -s sap-fiori-tools -g -y +npx skills add secondsky/sap-skills -s sapui5-linter -g -y +npx skills add secondsky/sap-skills -s sap-btp-cloud-platform -g -y +npx skills add secondsky/sap-skills -s sap-btp-connectivity -g -y +# sap-cloud-sdk and sap-cloud-sdk-ai are auto-installed with sap-btp-developer-guide bundle +``` + +### 6.4 OPTIONAL + +| Plugin | Use case | +|---|---| +| **Apify MCP server** | JIT documentation lookup (~€0.005-0.02 per page). Without it, the refactor skill degrades to manual mode | +| `mcp-sap-docs` | Preferred over Apify for SAP Help Portal queries; free when installed | +| `context7` | Generic library docs (non-SAP) | +| `playwright` MCP | Browser smoke tests for Fiori app audit | +| 12 optional secondsky plugins (job scheduling, cloud logging, MDI, cTMS, CIAS, BAS, integration suite, work zone, ISA) | Situational based on customer extension scope | + +### 6.5 Tier-1 git clones (free, weekly refresh) + +```bash +# One-time clone (~100 MB total) +mkdir -p .cache/git +git clone --depth 1 https://github.com/SAP/abap-atc-cr-cv-s4hc .cache/git/abap-atc-cr-cv-s4hc +git clone --depth 1 https://github.com/SAP/cloud-sdk .cache/git/cloud-sdk +git clone --depth 1 https://github.com/SAP-samples/cap-sflight .cache/git/cap-sflight +git clone --depth 1 https://github.com/SAP-samples/cloud-cap-samples .cache/git/cloud-cap-samples +git clone --depth 1 https://github.com/SAP-samples/btp-cap-multitenant-saas .cache/git/btp-cap-multitenant-saas +# (+ 3 more from SOURCES.md) + +# Weekly refresh +for d in .cache/git/*/; do (cd "$d" && git pull --ff-only --quiet); done +``` + +### 6.6 Tier-2 JIT Apify (paid per page) + +```bash +# One-time +export APIFY_API_TOKEN=apify_api_xxx # from console.apify.com/account/integrations +# Add apify-mcp to Claude Code MCP config +``` + +Without Apify, the refactor skill falls back to manual mode: it produces the plan with "consult URL X manually" pointers; the user pastes back doc snippets. Slower (~2-3× JIT) but free. + +--- + +## 7. Usage examples + +### 7.1 Example A — End-to-end Clean Core refactor of a Z* package + +Scenario: customer has `ZFI_INVOICES` package, 87 Z objects accumulated over 5 years, S/4HANA 2023 on-prem, target = BTP Cloud Foundry side-by-side. + +```bash +# Pre-flight: bootstrap system context (once per system) +/bootstrap-system-context S4DEV + +# Discovery + plan (defaults: target=btp-cf, mode=plan) +/sap-erp-clean-core-refactor ZFI_INVOICES + +# Review the plan +$EDITOR docs/refactor/2026-05-13-clean-core-plan.md +# (optional: edit decisions per row to override defaults) + +# Execute Phase 1 (quick wins: removes + Level B keepers) +/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=1 + +# Execute Phase 2 (in-place rewrites, low-risk first) +/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=2 + +# Execute Phase 3 (side-by-side extractions) +/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=3 +# This delegates to modernize-abap-to-btp-cap per extension + +# Verify the BTP side after deploy +cd bs/zfi-extensions/vendor-risk-score && cds deploy +/sap-cap-clean-core-enforce --enforce # blocks deploy if non-released API consumed +/sap-cap-stack-audit-full # pre-release audit + +# Capture learnings +/analyze-chat-session +``` + +**Expected outcome**: +- 47 objects rewritten in-place (mostly via released APIs → Level A) +- 12 objects extracted to 7 BTP CAP extensions +- 18 objects documented as Level B keepers +- 6 objects removed (unused) +- 4 objects deferred to research backlog +- ATC delta: 234 → 18 findings (-92%) +- Clean Core compliance: 14% → 87% +- Cost: ~€2-3 in Apify lookups, charged to user's account +- Wall time: 1-3 days + +### 7.2 Example B — Pre-release stack audit (BTP CAP project) + +Scenario: CAP project with 9 Fiori apps, ready for deployment to BTP CF. Want full audit before merge to main. + +```bash +# One command, runs everything in parallel +/sap-cap-stack-audit-full + +# Output: docs/audit/2026-05-13-stack-audit.md with: +# - Phase 1 pre-flight (CDS compile, project shape, target detected) +# - Phase 2 static (UI5 linter × 9, manifest validation × 9, TS typecheck, test suite, hardcoded sweep) +# - Phase 3 specialized (clean-core-enforce, customizing-honor, security-rbac-matrix, +# fiori-app-audit × 9, text-polish dry-run) +# - Phase 4 CAP deep (model integrity, profile audit, build dry-run) +# - Phase 5 best-practice cross-check +# - Phase 6 formal code review (if branch ≠ main) +# - Consolidated findings: Critical / High / Medium / Low +# - Severity grade A/B/C/D +``` + +**Expected outcome**: +- ~2-10 minutes wall time (`quick` mode skips Phase 3 agents → ~2 min) +- Single deduplicated report with file:line citations for every finding +- PRINCIPLE MISMATCH findings escalated to top of report +- Tools-unavailable section if some MCP not installed (graceful degradation) + +### 7.3 Example C — Security & compliance review + +Scenario: SOX auditor asks for evidence pack of OWASP / NIST CSF / GDPR controls. + +```bash +/sap-cap-security-rbac-matrix + +# Output: docs/audit/2026-05-13-security-rbac.md with: +# - Per-area findings (S1-S5 + O1 OWASP) +# - Role coherence matrix: xs-security.json ↔ Keycloak realm ↔ services-auth.cds ↔ handlers +# - Drift detected between layers (HIGH if any) +# - CC segregation audit (cross-company-code leak detection) +# - Compliance mapping: every finding maps to OWASP/ASVS/NIST CSF/CIS/SAP-SOM/GDPR/SOX citations +``` + +### 7.4 Example D — Single Fiori app audit + +Scenario: PR adds a new feature to `manager-ui` app; want to verify nothing regressed before merge. + +```bash +/sap-fiori-app-audit manager-ui + +# Output: docs/audit/2026-05-13-fiori-manager-ui.md +# - User journey walk (entry → filter → list → detail → action → refresh) +# - Frontend/backend contract chain for every primary action +# - Three failure classes detected: +# * UX-positive-false (button visible, backend rejects) +# * UX-negative-false (button hidden, backend would allow) +# * Stuck states + +# Optional: apply safe quick-win fixes on a dedicated branch +/sap-fiori-app-audit manager-ui fix +# Creates branch audit/fiori-manager-ui-2026-05-13 with: +# - Missing @Core.OperationAvailable added (referring to already-computed Can* flag) +# - Missing @Common.SideEffects added +# - Missing i18n key added to webapp/i18n/i18n.properties +# - Missing tooltip / @Common.Label +# Run scoped tests + verify, commit, open PR +``` + +### 7.5 Example E — Text polish before release + +Scenario: release candidate has user-visible text that sounds amateur; want polish before going live + PII safety check. + +```bash +/sap-cap-text-polish all tone:formal + +# Output: docs/audit/2026-05-13-text-polish.md +# - 10 anti-pattern detection per source (backend reject, frontend toast, i18n bundle, …) +# - Before/after rewrites for POLISH / REWRITE classified items +# - PII_RISK items (unmasked IBAN / fiscal code / VAT / email) flagged urgent +# - LEGAL_REVIEW_REQUIRED items (privacy/consent text) deferred to human + +# Apply safe rewrites +/sap-cap-text-polish all fix tone:formal +# Creates branch audit/text-polish-all-2026-05-13 with: +# - Typo / capitalization fixes +# - ICU placeholders added (concatenation → {0}) +# - PII masking helper calls inserted +# - Missing i18n keys created +# - All within ≤50 lines diff per file +``` + +### 7.6 Example F — Setting up CI gates from scratch + +Scenario: new CAP project, no CI gates yet, want to lock structural invariants. + +```bash +# Describe what patterns would apply (no file generation) +/sap-cap-ci-gates-pattern + +# Output: which of the 5 patterns apply based on your project structure +# (bidirectional CSV ↔ code; catalog raise-coverage; API-availability drift; +# convention/matrix drift; CSV schema lint) + +# Generate all applicable gates +/sap-cap-ci-gates-pattern all apply + +# Generated files: +# - scripts/ci/check-settings-bidirectional.sh +# - scripts/ci/check-catalog-raise-coverage.sh +# - scripts/ci/check-availability-drift.sh + check-availability-drift.js +# - scripts/ci/check-convention-drift.sh +# - scripts/ci/check-csv-lint.js +# - .github/workflows/ci-gates.yml (matrix strategy, 5 parallel jobs) + +# Review + commit + push → next PR is gated +``` + +### 7.7 Example G — Side-by-side extension scaffold from a Z FM + +Scenario: `sap-erp-clean-core-refactor` decided `ZFM_VENDOR_RISK_SCORE` should be `extract_to_side_by_side`. Now generate the CAP scaffold. + +```bash +# Delegated automatically during refactor execute mode, OR invoke directly: +/modernize-abap-to-btp-cap ZFM_VENDOR_RISK_SCORE --target=btp-cf + +# Internally orchestrates: +# 1. modernize-abap-cap-schema — derives CDS entities from FM I/O parameters + LFA1 deps +# 2. modernize-abap-cap-service — generates CAP service action that replicates the FM semantics +# 3. Wires deployment manifest (mta.yaml for btp-cf) + +# Output: bs/vendor-risk-score/ — a complete CAP project +# db/schema.cds (Vendors entity from BP CDS + RiskScore custom field) +# srv/risk-service.cds (calculateRisk action) +# srv/risk-service.ts (handler implementation) +# mta.yaml (BTP CF deployment manifest) +# xs-security.json (XSUAA scope) +# package.json (cds + dependencies) +# README.md (extension purpose + deploy steps) + +cd bs/vendor-risk-score && npm install && cds deploy && mbt build && cf deploy +``` + +--- + +## 8. PR roadmap + +Four open draft PRs on `marianfoo/arc-1`. They're structurally independent and can be merged in any order; cross-links between PRs degrade gracefully to "see also" hints until all are merged. + +| PR | Branch | Scope | Lines | Skills | +|---|---|---|---|---| +| **#278** | `feat/skills-modernize-abap-to-btp-cap` | Modernization chain (Z-table → CDS, FM → CAP) | ~1200 | 3 | +| **#279** | `feat/skills-sap-cap-audit-wave1` | Clean Core enforce + Customizing honor | ~970 | 2 | +| **#280** | `feat/skills-sap-cap-audit-wave2-3` | Audit toolkit + master battle-tested-patterns + orchestrator + CI gates | ~3000 | 6 | +| **#281** | `feat/skills-erp-clean-core-refactor` | ERP refactor planner (JIT, decision tree, ARC-1 + secondsky integration) | ~925 | 1 + 2 reference docs | +| (this) | `feat/docs-sap-cap-toolkit-overview` | This consolidated doc | ~700 | 0 (doc-only) | + +**Total upstream**: 13 skills + 2 reference docs (SOURCES.md, INTEGRATIONS.md) + this overview = **~6800 lines**, 100% generic, zero project-specific references. + +--- + +## 9. FAQs + +### Why JIT lookups instead of a pre-built KB? + +Pre-crawl of 18 SAP sources weekly costs €400-780/year on Apify and produces gigabytes of mostly-unread content. Even weekly crawl is 7 days stale at worst. JIT per-finding lookups cost €0.50-5 per refactor, are always fresh, and the user pays only for what they consume. See [§5.1](#51-jit-lookups-no-pre-built-kb). + +### What if my customer has no BTP plan? + +The refactor skill works without BTP. All `extract_to_side_by_side` decisions become `research_required` (not actionable), and the skill defaults to `rewrite_in_place` (Level A or Level B) plus `keep_at_level_b`. Compliance score will be lower (60-70% vs 85%+) but the refactor still progresses. + +### Can I use only the audit skills without the refactor skill? + +Yes. The audit skills (`sap-cap-clean-core-enforce`, `sap-cap-customizing-honor`, `sap-cap-security-rbac-matrix`, `sap-fiori-app-audit`, `sap-cap-text-polish`) work standalone on any CAP+Fiori project, with no ARC-1 MCP requirement. The orchestrator (`sap-cap-stack-audit-full`) coordinates them. + +### What's the difference between this toolkit and `sap-clean-core-atc`? + +- `sap-clean-core-atc` (existing arc-1 native skill) classifies **ABAP custom code on the SAP side** into Levels A/B/C/D. It answers "what is each Z object's level?" +- `sap-erp-clean-core-refactor` (this toolkit, new) **plans and executes the refactor** to move each Z object toward Level A. It answers "what should we do with each Z object?" +- `sap-cap-clean-core-enforce` (this toolkit, new) verifies **the BTP CAP application's outbound S/4 API consumption** is Level A. It answers "are all the S/4 APIs our CAP app calls actually released?" + +The three skills are complementary, addressing classification, refactor planning, and consumption verification respectively. + +### What changes when the deployment target is On-Premise Kyma? + +- CDS profile becomes `k8s-onprem` instead of `k8s`. +- Authentication switches from XSUAA to OIDC via Keycloak (customer's IdP). +- Side-by-side BTP extensions deploy to the customer's K8s cluster instead of BTP Kyma. +- The toolkit's audit skills handle the difference automatically. `sap-cap-stack-audit-full` Step 0 asks the target. + +### Why is On-Premise CF not supported? + +SAP Cloud Foundry On-Premise reached end of maintenance. The toolkit does not plan deployments against an EOL runtime. Customers with existing on-prem CF investment should migrate to BTP CF (managed) or BTP Kyma. + +### Can I run the toolkit on a project that's not on GitHub? + +Yes. The skills are filesystem-driven (read CDS, manifest, schema, etc. from the local working directory). CI gate generation supports GitHub Actions, GitLab CI, Jenkins, and BTP CI/CD. Only the cross-link to `SAP/abap-atc-cr-cv-s4hc` and the optional Apify references require network. + +### Where do I report issues with the toolkit? + +Open an issue on the relevant PR (#278 / #279 / #280 / #281) or against `marianfoo/arc-1` if the issue is in the underlying MCP. For documentation issues with this overview, open against this PR. + +--- + +## 10. References + +### Toolkit skills (this contribution) + +- [`sap-cap-fiori-battle-tested-patterns`](../skills/sap-cap-fiori-battle-tested-patterns/SKILL.md) — reference knowledge base +- [`sap-cap-stack-audit-full`](../skills/sap-cap-stack-audit-full/SKILL.md) — orchestrator +- [`sap-cap-ci-gates-pattern`](../skills/sap-cap-ci-gates-pattern/SKILL.md) — CI gate library +- [`sap-cap-clean-core-enforce`](../skills/sap-cap-clean-core-enforce/SKILL.md) — Tier-2 S/4 API gate +- [`sap-cap-customizing-honor`](../skills/sap-cap-customizing-honor/SKILL.md) — CSV ↔ code audit +- [`sap-cap-security-rbac-matrix`](../skills/sap-cap-security-rbac-matrix/SKILL.md) — security + compliance +- [`sap-fiori-app-audit`](../skills/sap-fiori-app-audit/SKILL.md) — Fiori app audit +- [`sap-cap-text-polish`](../skills/sap-cap-text-polish/SKILL.md) — user-text + PII +- [`sap-erp-clean-core-refactor`](../skills/sap-erp-clean-core-refactor/SKILL.md) — ERP refactor planner + - [`SOURCES.md`](../skills/sap-erp-clean-core-refactor/SOURCES.md) — authoritative SAP source catalog + - [`INTEGRATIONS.md`](../skills/sap-erp-clean-core-refactor/INTEGRATIONS.md) — ARC-1 MCP × skill × secondsky mapping +- [`modernize-abap-to-btp-cap`](../skills/modernize-abap-to-btp-cap/SKILL.md) — orchestrator +- [`modernize-abap-cap-schema`](../skills/modernize-abap-cap-schema/SKILL.md) — Z-table → CDS +- [`modernize-abap-cap-service`](../skills/modernize-abap-cap-service/SKILL.md) — FM → CAP service + +### Supporting arc-1 native skills + +- [`bootstrap-system-context`](../skills/bootstrap-system-context/SKILL.md) +- [`setup-abap-mirror`](../skills/setup-abap-mirror/SKILL.md) +- [`explain-abap-code`](../skills/explain-abap-code/SKILL.md) +- [`sap-clean-core-atc`](../skills/sap-clean-core-atc/SKILL.md) +- [`sap-unused-code`](../skills/sap-unused-code/SKILL.md) +- [`sap-object-documenter`](../skills/sap-object-documenter/SKILL.md) +- [`generate-abap-unit-test`](../skills/generate-abap-unit-test/SKILL.md) +- [`generate-cds-unit-test`](../skills/generate-cds-unit-test/SKILL.md) +- [`generate-rap-logic`](../skills/generate-rap-logic/SKILL.md) +- [`generate-rap-service-researched`](../skills/generate-rap-service-researched/SKILL.md) +- [`migrate-segw-to-rap`](../skills/migrate-segw-to-rap/SKILL.md) +- [`migrate-custom-code`](../skills/migrate-custom-code/SKILL.md) +- [`convert-ui5-to-fiori-elements`](../skills/convert-ui5-to-fiori-elements/SKILL.md) +- [`modernize-ui5-app`](../skills/modernize-ui5-app/SKILL.md) +- [`analyze-chat-session`](../skills/analyze-chat-session/SKILL.md) + +### External + +- [ARC-1 MCP server](https://github.com/marianfoo/arc-1) — 12 ABAP intent-based tools +- [secondsky/sap-skills](https://github.com/secondsky/sap-skills) — 32-plugin SAP catalog +- [SAP/abap-atc-cr-cv-s4hc](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — released ABAP objects authority +- [SAP API Hub](https://api.sap.com/) — OData service catalog + lifecycle +- [SAP Cloud Application Programming Model](https://cap.cloud.sap/docs/) — CAP capire +- [SAP Fiori Elements V4 Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) +- [BTP Reference Architectures](https://help.sap.com/docs/btp/sap-business-technology-platform/reference-architectures) +- [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/) +- [NIST CSF 2.0](https://www.nist.gov/cyberframework) +- [Apify Website Content Crawler](https://apify.com/apify/website-content-crawler) + +### Methodology + +- [Clean Core principles (BTP docs)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) +- [SAP Custom Code Migration Guide](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) +- [SAP Activate Roadmap Viewer](https://go.support.sap.com/roadmapviewer/) (S-user login required) + +--- + +## License & contribution + +This documentation, like the skills it describes, is licensed under the arc-1 fork's license (MIT-compatible). Contributions welcome via PR against `marianfoo/arc-1`. For corrections to this overview, open against the PR that introduced it. diff --git a/skills/README.md b/skills/README.md index 689acb01..5fe211cc 100644 --- a/skills/README.md +++ b/skills/README.md @@ -4,6 +4,8 @@ Best-practice agent skills for common SAP development workflows with ARC-1. Each skill is a directory containing a `SKILL.md` file with YAML frontmatter — the format used by [Anthropic Agent Skills](https://code.claude.com/docs/en/skills) and consumed by the [`vercel-labs/skills`](https://github.com/vercel-labs/skills) CLI. Agents discover them by `description` and load them on demand. +> **Looking for the SAP CAP + Clean Core toolkit?** See [`../docs/sap-cap-toolkit.md`](../docs/sap-cap-toolkit.md) for a consolidated overview of the 13-skill toolkit contributed in PRs #278–#281 — visual map, full skill catalog, architectural strategies (JIT lookups, defense-in-depth, three-layer architecture, decision tree, target deployment matrix, cost model), dependency setup, 7 worked usage examples, FAQs, and PR roadmap. + ## Install via the `skills` CLI (recommended) The fastest way is `npx skills` — it auto-detects the agents installed in your project (Claude Code, Cursor, GitHub Copilot, OpenCode, Gemini CLI, Codex, …) and installs into the right paths. From 82352fa55ee8b0c0ef2a97b65e62f0e9df997441 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 18:00:15 +0200 Subject: [PATCH 16/18] refactor(scope): split CAP audit toolkit to companion repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit narrows the PR scope to ABAP-side refactoring skills only. The CAP-side audit / hardening / CI-gate skills move to a separate companion repository at: https://github.com/Raistlin82/sap-cap-toolkit Rationale: cleaner separation of concerns. arc-1 is fundamentally an ABAP-focused MCP server, so the skills shipped here focus on ABAP custom-code refactoring (Z* → Clean Core compliance + side-by-side BTP extensions). CAP audit / Fiori audit / security / text polish / CI gates are a complementary toolkit that lives in its own repository. Removed from this PR (moved to Raistlin82/sap-cap-toolkit): - skills/sap-cap-clean-core-enforce - skills/sap-cap-customizing-honor - skills/sap-cap-security-rbac-matrix - skills/sap-fiori-app-audit - skills/sap-cap-text-polish - skills/sap-cap-stack-audit-full - skills/sap-cap-ci-gates-pattern - skills/sap-cap-fiori-battle-tested-patterns - docs/sap-cap-toolkit.md Retained in this PR: - skills/modernize-abap-to-btp-cap (orchestrator) - skills/modernize-abap-cap-schema (Z-table → CDS entity) - skills/modernize-abap-cap-service (FM/program → CAP service action) - skills/sap-erp-clean-core-refactor (ABAP Z* refactor planner) sap-erp-clean-core-refactor is now fully self-contained — ships FOUR files instead of three: - SKILL.md — the refactor protocol (~680 lines) - SOURCES.md — 23 SAP source catalog in 4 tiers (~85 lines) - PATTERNS.md — NEW: ~70 battle-tested patterns in 8 categories, ~1070 lines. Inlined from the sap-cap-fiori-battle-tested-patterns skill so the refactor skill doesn't depend on the companion repo for its target-resolution / side-by-side scaffold logic. Same content also published as a standalone skill in Raistlin82/sap-cap-toolkit for CAP-only audiences. - INTEGRATIONS.md — ARC-1 MCP × arc-1 skill × secondsky plugin mapping (~175 lines) Cross-references from the refactor skill files to the CAP-side audit skills (sap-cap-clean-core-enforce, sap-cap-stack-audit-full, etc.) are now external URLs pointing to github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/<name>/SKILL.md — these are "see also" / coordinate-with references, not critical dependencies. The refactor skill works standalone; the CAP toolkit links provide context if both repos are installed. skills/README.md updated: - Top-of-file callout now redirects CAP-toolkit users to Raistlin82/sap-cap-toolkit instead of the local doc. - "SAP CAP Enterprise Audit" section removed (skills no longer here). - "SAP ERP — Clean Core Return" section updated to mention the new four-file ship (was three). [skip ci] --- docs/sap-cap-toolkit.md | 772 ------------------ skills/README.md | 21 +- skills/sap-cap-ci-gates-pattern/SKILL.md | 431 ---------- skills/sap-cap-clean-core-enforce/SKILL.md | 462 ----------- skills/sap-cap-customizing-honor/SKILL.md | 494 ----------- skills/sap-cap-security-rbac-matrix/SKILL.md | 467 ----------- skills/sap-cap-stack-audit-full/SKILL.md | 462 ----------- skills/sap-cap-text-polish/SKILL.md | 404 --------- .../INTEGRATIONS.md | 4 +- .../PATTERNS.md} | 33 +- skills/sap-erp-clean-core-refactor/SKILL.md | 25 +- skills/sap-fiori-app-audit/SKILL.md | 389 --------- 12 files changed, 36 insertions(+), 3928 deletions(-) delete mode 100644 docs/sap-cap-toolkit.md delete mode 100644 skills/sap-cap-ci-gates-pattern/SKILL.md delete mode 100644 skills/sap-cap-clean-core-enforce/SKILL.md delete mode 100644 skills/sap-cap-customizing-honor/SKILL.md delete mode 100644 skills/sap-cap-security-rbac-matrix/SKILL.md delete mode 100644 skills/sap-cap-stack-audit-full/SKILL.md delete mode 100644 skills/sap-cap-text-polish/SKILL.md rename skills/{sap-cap-fiori-battle-tested-patterns/SKILL.md => sap-erp-clean-core-refactor/PATTERNS.md} (93%) delete mode 100644 skills/sap-fiori-app-audit/SKILL.md diff --git a/docs/sap-cap-toolkit.md b/docs/sap-cap-toolkit.md deleted file mode 100644 index cabb8c0b..00000000 --- a/docs/sap-cap-toolkit.md +++ /dev/null @@ -1,772 +0,0 @@ -# SAP CAP + Clean Core Toolkit — Overview & Navigation Guide - -A consolidated guide to the **13-skill SAP toolkit** contributed across PRs #278, #279, #280, and #281: end-to-end coverage from ABAP custom-code refactor through CAP service development to BTP deployment, with audits, gates, and battle-tested patterns. - ---- - -## 1. Mission - -SAP customers carry decades of custom ABAP code that needs to evolve toward **Clean Core compliance** and **side-by-side BTP extensions**. The journey is multi-step: - -1. **Inventory** the custom code that exists. -2. **Classify** each object against Clean Core Levels A/B/C/D. -3. **Decide**, per object, between rewrite-in-place, side-by-side extraction, keep-at-B, or remove. -4. **Generate** the rewritten ABAP and/or the new BTP CAP scaffold. -5. **Verify** with regression tests and ATC gates. -6. **Audit** the resulting CAP+Fiori+BTP application end-to-end before deployment. -7. **Enforce** compliance as CI gates that catch regressions. - -This toolkit covers all seven steps in a coherent, generic, OSS-shareable form. Every contribution is **100% generic** — no domain-specific names, no customer-specific patterns hardcoded. The toolkit operates on **any** SAP CAP + ABAP + Fiori project. - -### Audience - -- **SAP architects** planning Clean Core programs or pre/post-S/4HANA migrations. -- **SAP delivery teams** building BTP applications that consume S/4HANA Tier-2 services. -- **Customer development teams** modernizing legacy ABAP custom code. -- **SAP partners** delivering Clean Core compliance assessments and refactor projects. - -### Scope - -| In scope | Out of scope | -|---|---| -| CAP service development (Node.js + TypeScript) | CAP Java runtime (patterns differ; future contribution) | -| Fiori Elements V4 apps + UI5 freestyle | SAPUI5 < 1.108 (legacy UI5; use modernize-ui5-app for migration) | -| BTP Cloud Foundry + BTP Kyma + On-Premise Kyma | On-Premise CF (EOL) | -| S/4HANA Public Cloud + Private Cloud + On-Premise | ECC < 6.0 (use SAP Activate methodology for migration first) | -| ABAP custom code refactor (Z*/Y*/namespace) | ABAP standard code (never touched) | -| Released APIs only (Clean Core Level A target) | Non-released RFC/BAPI consumption (flagged for refactor) | - ---- - -## 2. Quick start (30 seconds) - -```bash -# Pre-flight (one-time) -gh repo clone marianfoo/arc-1-fork -cd arc-1-fork -npx skills add Raistlin82/arc-1-fork -g -y # install all 13 skills -npx skills add secondsky/sap-skills -s sap-abap -g -y # required companion -npx skills add secondsky/sap-skills -s sap-abap-cds -g -y -npx skills add secondsky/sap-skills -s sap-cap-capire -g -y -npx skills add secondsky/sap-skills -s sap-btp-developer-guide -g -y - -# Set up ARC-1 MCP server connection to your SAP system (see arc-1 README) - -# Three most common invocations -/sap-erp-clean-core-refactor ZFI plan # plan a Clean Core refactor (default target=btp-cf) -/sap-cap-stack-audit-full # pre-release audit of a CAP project -/sap-cap-clean-core-enforce --enforce # block deployment if non-released API consumed -``` - ---- - -## 3. Toolkit at a glance - -### Visual map - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ REFERENCE LAYER (knowledge base) │ -│ sap-cap-fiori-battle-tested-patterns │ -│ ~70 patterns in 8 categories — UI5/FE V4 traps, CAP/TS, BTP deploy, │ -│ security, customizing, lifecycle, events, ecosystem plugins │ -└─────────────────────────────────────────────────────────────────────────┘ - ↑ cross-link by anchor - │ -┌───────────────────────────┴─────────────────────────────────────────────┐ -│ ORCHESTRATION LAYER │ -│ sap-cap-stack-audit-full (master orchestrator, asks target) │ -│ sap-cap-ci-gates-pattern (5 reusable CI gate patterns) │ -└─────────────────────────────────────────────────────────────────────────┘ - ↑ orchestrates / generates - │ -┌───────────────────────────┴─────────────────────────────────────────────┐ -│ AUDIT LAYER │ -│ sap-cap-clean-core-enforce (Tier-2 S/4 API gate, --enforce mode) │ -│ sap-cap-customizing-honor (bidirectional CSV↔code, ValueList) │ -│ sap-cap-security-rbac-matrix (OWASP/ASVS/NIST/CIS/SAP-SOM matrix) │ -│ sap-fiori-app-audit (per-app UI/UX + FE/BE contract) │ -│ sap-cap-text-polish (user-text + PII detection) │ -└─────────────────────────────────────────────────────────────────────────┘ - ↑ produces findings consumed by gates above - │ -┌───────────────────────────┴─────────────────────────────────────────────┐ -│ REFACTOR / MIGRATION LAYER │ -│ sap-erp-clean-core-refactor (Z* refactor planner + executor, JIT KB) │ -│ ├── SKILL.md — 668 lines protocol │ -│ ├── SOURCES.md — 23 SAP sources organized in 4 tiers │ -│ └── INTEGRATIONS.md — full mapping ARC-1 MCP × skill × secondsky │ -│ │ -│ modernize-abap-to-btp-cap (top-level orchestrator) │ -│ ├── modernize-abap-cap-schema (Z-table → CDS entity) │ -│ └── modernize-abap-cap-service (FM/program → CAP service action) │ -└─────────────────────────────────────────────────────────────────────────┘ - ↑ delegates to other arc-1 native skills - │ -┌───────────────────────────┴─────────────────────────────────────────────┐ -│ SUPPORT / DELEGATE SKILLS (existing arc-1 native) │ -│ bootstrap-system-context · setup-abap-mirror · explain-abap-code │ -│ sap-clean-core-atc · sap-unused-code · sap-object-documenter │ -│ generate-abap-unit-test · generate-cds-unit-test · generate-rap-logic │ -│ generate-rap-service-researched · migrate-segw-to-rap │ -│ migrate-custom-code · convert-ui5-to-fiori-elements · modernize-ui5-app │ -│ analyze-chat-session │ -└─────────────────────────────────────────────────────────────────────────┘ - ↑ executes operations via - │ -┌───────────────────────────┴─────────────────────────────────────────────┐ -│ EXECUTION LAYER (MCP servers + plugins) │ -│ ARC-1 MCP server — 12 ABAP intent-based tools (the hands) │ -│ secondsky/sap-skills — 32 plugins (the library of patterns) │ -│ ├── sap-abap (MUST) │ -│ ├── sap-abap-cds (MUST) │ -│ ├── sap-cap-capire (MUST, 4 dispatchable agents) │ -│ ├── sap-btp-developer-guide (MUST) │ -│ └── 19 SHOULD / OPTIONAL plugins │ -│ Apify MCP (JIT documentation lookup, user-paid per page) │ -│ mcp-sap-docs (when installed — free SAP doc lookup) │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - -### Skill count and provenance - -| Skill | Layer | PR | Provenance | -|---|---|---|---| -| `sap-cap-fiori-battle-tested-patterns` | Reference | #280 | New — distilled from production patterns | -| `sap-cap-stack-audit-full` | Orchestration | #280 | New — orchestrator | -| `sap-cap-ci-gates-pattern` | Orchestration | #280 | New — CI library | -| `sap-cap-clean-core-enforce` | Audit (gate) | #279 | New — Tier-2 S/4 API gate | -| `sap-cap-customizing-honor` | Audit | #279 | New — CSV ↔ code | -| `sap-cap-security-rbac-matrix` | Audit | #280 | New — OWASP/ASVS/NIST/CIS/SAP-SOM | -| `sap-fiori-app-audit` | Audit | #280 | New — single Fiori app | -| `sap-cap-text-polish` | Audit | #280 | New — user-text + PII | -| `sap-erp-clean-core-refactor` | Refactor | #281 | New — Z* refactor planner | -| `modernize-abap-to-btp-cap` | Refactor | #278 | New — top-level orchestrator | -| `modernize-abap-cap-schema` | Refactor | #278 | New — Z-table → CDS | -| `modernize-abap-cap-service` | Refactor | #278 | New — FM → CAP | - -**Total: 12 new skills + 1 knowledge-base reference (battle-tested-patterns) = 13 contributed artifacts.** - ---- - -## 4. Skill catalog - -### 4.1 Reference — battle-tested patterns - -#### `sap-cap-fiori-battle-tested-patterns` - -A curated reference catalog of **~70 production-distilled patterns** organized in 8 categories. Not runnable — it's a knowledge base linked by anchor from other skills. - -| Category | Pattern count | Examples | -|---|---|---| -| 1 — UI5 / Fiori Elements V4 traps | 12 | `@UI.Hidden` + `OperationAvailable` interaction; actions returning entity NO `.columns()`; `liveMode` only on small datasets; Composition vs Association for audit children | -| 2 — CAP / TypeScript pitfalls | 8 | `cds.tx` autonomous deadlock on SQLite; `forUpdate` before lifecycle UPDATE; post-commit side effects in `req.on('succeeded')`; cds.log discipline; centralized reject helper | -| 3 — BTP / Kyma / On-Premise deployment | 30+ | 4-target decision matrix (BTP CF default, BTP Kyma, On-Prem Kyma — On-Prem CF dropped as EOL); per-target sub-sections with 5-7 patterns each; Clean Core as deployment gate (3.11) | -| 4 — Security defense-in-depth | 7 | Audit log append-only 3-layer; PII sanitize before audit; magic bytes upload; per-class rate limiters; token rotation; OData $expand depth | -| 5 — Customizing-driven | 5 | SystemParameter SSOT + bounded cache; adapter dual-source fallback; per-tenant override; master-data ValueList enforcement; bidirectional CSV ↔ code | -| 6 — Lifecycle / process | 6 | Centralized phase boundary map; CAS marker for idempotent advance; touchless handler idempotency; exception auto-dispatch ordering | -| 7 — Events / messaging | 5 | Declarative event service; post-commit fire-and-forget; connection cache as Promise; idempotency key cross-emit; outbox replay mirror | -| 8 — Ecosystem plugin landscape | 16 entries | Map of all secondsky/sap-skills plugins applicable to CAP+Fiori projects | - -Each pattern: **symptom** observed by users/operators → **root cause** in framework/runtime/deployment layer → **generic remedy** with portable code snippet. - -### 4.2 Orchestration - -#### `sap-cap-stack-audit-full` - -Master orchestrator. Asks the deployment target (Step 0), runs all relevant audits in parallel, consolidates a deduplicated report. Engages every audit skill from §4.3 plus static checks (UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded customizing sweep). - -Always read-only. Output: a single markdown report under `docs/audit/<date>-stack-audit.md` with severity rollup A/B/C/D. - -#### `sap-cap-ci-gates-pattern` - -Library of **5 reusable CI gate patterns** (bidirectional CSV ↔ code; catalog raise-coverage; API-availability drift; convention/matrix drift; CSV schema lint). Two modes: -- `describe` (default) — print what each pattern would do. -- `apply` — generate the bash gate scripts + GitHub Actions / GitLab / Jenkins / BTP CI/CD workflow YAML. - -The skill never executes the gates — it generates them so the user's CI runs them per-PR. - -### 4.3 Audit - -#### `sap-cap-clean-core-enforce` - -**Discovery-driven Clean Core Level A enforcement** for CAP + S/4HANA projects. Three modes: -- `report` — read-only audit + markdown report -- `--apply` — safe additive corrections to the compatibility catalog -- `--enforce` — **CI-blocking gate** (non-zero exit on HIGH findings); pairs with `sap-cap-ci-gates-pattern` Pattern 3 - -Consults **two authoritative sources**: -1. `SAP/abap-atc-cr-cv-s4hc` (ABAP API Release State repository — JSON per object class) -2. `api.sap.com` (SAP API Hub — OData service lifecycle, Communication Scenarios) - -Per cell of the matrix (service × edition), resolves disagreements: API Hub wins for OData service questions, abap-atc wins for raw ABAP object questions. - -#### `sap-cap-customizing-honor` - -Bidirectional CSV ↔ code customizing audit. Detects: -- **Inverse orphans** — code reads a parameter that the CSV doesn't seed (admin doesn't know it's configurable). -- **Forward orphans** — CSV seeds a parameter that no code reads (admin thinks they can configure but it's ignored). -- **Hardcoded business decisions** — thresholds/timeouts in code that should be SystemParameter. -- **Master-data unreferenced** — FK fields without `@Common.ValueList` annotation. - -Has `fix` mode that applies safe additive corrections (add CSV seed for inverse-orphan; add ValueList annotation on filter bar). - -#### `sap-cap-security-rbac-matrix` - -Multi-area parallel security scan + role coherence matrix. Five area agents run in parallel (S1 handlers, S2 MCP endpoints, S3 file-upload, S4 deploy YAML, S5 background jobs), one orthogonal OWASP pass (O1, 10 categories). Maps every finding to ≥1 framework citation: - -- OWASP Top 10 2021 -- OWASP API Security Top 10 2023 -- ASVS L1-L3 -- NIST CSF 2.0 + NIST SP 800-53 -- CIS Kubernetes 1.9 / Docker 1.6 -- SAP Secure Operations Map 2024 -- GDPR + SOX 404 - -Role coherence matrix across 4 layers: `xs-security.json` ↔ IdP realm (XSUAA/Keycloak/IAS) ↔ `services-auth.cds` ↔ handlers. Drift between layers is a HIGH finding. - -#### `sap-fiori-app-audit` - -Single Fiori Elements V4 app audit. Walks the canonical user journey (entry → filter → list → detail → action → refresh), validates the frontend/backend contract chain (annotation → flag → READ projection → `@restrict` grant → handler → SideEffects → test). - -Three failure classes: -- **UX-positive-false** — button visible, backend rejects → user gets 403/409. -- **UX-negative-false** — button hidden, backend would allow → user stuck. -- **Stuck state** — no button visible, no auto-job advances → entity dead in status. - -Has `fix` mode for safe quick wins (missing `@Core.OperationAvailable`, missing `@Common.SideEffects`, missing tooltip, missing i18n fallback). - -#### `sap-cap-text-polish` - -Audit and rewrite all user-visible text — backend reject/throw, helper rejects, frontend toasts/dialogs, i18n bundles, CDS labels, CodeList descriptions. Detects 10 anti-patterns: -1. Colloquial / Telegram tone -2. Accusatory ("you did wrong") -3. Unexplained tech jargon (404, null pointer, SQL error) -4. Fragmented / incomplete ("Error.") -5. All-caps / multiple exclamations -6. Unexpanded acronyms (BP, CC, SDI, FE, PO) -7. Mixed locale -8. Missing ICU placeholders (`{0}` instead of concatenation) -9. Inconsistent punctuation -10. **PII leak** (IBAN, fiscal code, VAT, full email — automatic masking) - -Tone-profile-driven, locale-aware (resolves primary locale from the project), additive safe rewrites only. - -### 4.4 Refactor / migration - -#### `sap-erp-clean-core-refactor` (the flagship) - -Plans and executes a Level A refactor of ABAP custom code (Z*/Y*). Three modes (`discover` / `plan` / `execute`). **JIT-only** — no centralized pre-built knowledge base; documentation queried on demand within a bounded per-finding budget. - -**Decision tree** per object: - -``` -A → no_action (target: A) -Unused → remove_unused (target: N/A) -D → rewrite_in_place via released API (target: A) - OR rewrite_in_place via BAdI (target: B) - OR extract_to_side_by_side (target: A ERP-side) - OR accept_as_d_documented (last resort) -C → rewrite_in_place via released API (target: A) - OR rewrite_in_place via BAdI (target: B) - OR extract_to_side_by_side (target: A ERP-side) - OR keep_at_level_b (target: B) -B → keep_at_level_b (default) (target: B) - OR rewrite_in_place / extract (via --aggressive flag) -``` - -**Three escalation mechanisms**: -- `--aggressive` (global) — explore A escalation for every Level B finding. -- `--push-to-a=A,B,C` (selective) — only listed objects get expanded analysis. -- `--target-level=A` (ambitious) — always prefer A; `--target-level=B` (minimal) — settle for B. -- **Plan-file editing** (finest-grained) — edit the emitted markdown before `execute`. - -Three ships: -- [`SKILL.md`](../skills/sap-erp-clean-core-refactor/SKILL.md) — 668-line protocol -- [`SOURCES.md`](../skills/sap-erp-clean-core-refactor/SOURCES.md) — 23 SAP sources in 4 tiers -- [`INTEGRATIONS.md`](../skills/sap-erp-clean-core-refactor/INTEGRATIONS.md) — full ARC-1 MCP × skill × secondsky mapping - -#### `modernize-abap-to-btp-cap` chain (#278) - -Three coordinated skills for ABAP → BTP CAP modernization: - -- **`modernize-abap-to-btp-cap`** — top-level orchestrator, takes ABAP target spec, decomposes into schema + service migration. -- **`modernize-abap-cap-schema`** — Z-table / DDIC structure → CDS entity definition. Handles 24 DDIC → CDS type mappings. -- **`modernize-abap-cap-service`** — Function module / report → CAP service action implementation. Preserves semantics, adapts to handler patterns. - -Called by `sap-erp-clean-core-refactor` Step 6b when the decision is `extract_to_side_by_side`. - ---- - -## 5. Strategies (architectural patterns) - -### 5.1 JIT lookups, no pre-built KB - -A pre-crawled knowledge base of SAP documentation sounds appealing but is economically and operationally wrong: -- **Volume**: help.sap.com alone is ~50k pages. Full crawl = GBs of mostly-unread content. -- **Cost**: weekly Apify crawl of 18 sources ≈ €400-780/year. No clear funding model. -- **Staleness**: even weekly is N days behind SAP's monthly API release cadence. - -**JIT alternative**: -- Tier-1 git-cloneable sources (`abap-atc-cr-cv-s4hc`, `SAP-samples`, `cloud-sdk`) are pulled weekly via `git pull --ff-only` — free, fast, authoritative. -- Tier-2 HTTP/SPA sources are queried **per finding** via Apify on the user's own account at ~€0.005-0.02 per page. Bounded 5 lookups per finding. Cached locally for 30 days. -- Tier-3 auth-gated sources (`launchpad-support`, `me.sap.com`) are flagged as manual-consultation pointers. -- Tier-4 MCP-server-backed sources (`mcp-sap-docs`, `context7`) are preferred when installed. - -**Cost per refactor of 50-200 objects**: €0.50-€5, charged to the **user's own Apify account**. No centralized infrastructure. - -### 5.2 Defense-in-depth (audit → enforcement → CI gates) - -Every Clean Core / compliance gate has three layers, none of which can be the only one: - -1. **Audit layer** (read-only, generates report): `sap-cap-clean-core-enforce report`, `sap-cap-customizing-honor`, `sap-cap-security-rbac-matrix`, `sap-fiori-app-audit`. -2. **Enforcement layer** (CI-blocking gate): `sap-cap-clean-core-enforce --enforce` exits non-zero on HIGH findings. Wired into pre-PR and pre-deploy CI. -3. **Gate generation layer**: `sap-cap-ci-gates-pattern` produces the shell scripts + workflow YAML so the customer's CI runs the gates per-PR. - -Findings discovered by audits flow into gates so they don't regress. **Auditing without gates is busywork**. - -### 5.3 Three-layer architecture (hands × playbook × library) - -| Layer | Role | Examples | -|---|---|---| -| **ARC-1 MCP** | the *hands* — reads/writes ABAP via ADT REST API | 12 intent-based tools (SAPRead, SAPWrite, SAPContext, SAPLint, SAPDiagnose, …) | -| **arc-1 native skills** | the *playbook* — sequences of MCP operations | bootstrap-system-context, sap-clean-core-atc, generate-rap-logic, etc. | -| **secondsky/sap-skills** | the *library of patterns* — how to write what the hands write | sap-abap, sap-abap-cds, sap-cap-capire, sap-btp-developer-guide | -| **This toolkit** | the *orchestrator* — decides what to do with which hands using which book | sap-erp-clean-core-refactor + 12 sibling skills | - -Engaging only one layer is insufficient. Without `sap-abap` enforcement, the agent generates ABAP that compiles but doesn't pass ATC. Without `SAPContext(impact)`, the rewrite-vs-extract decision is blind. Without the orchestrator skill, the user has tools but no method. - -### 5.4 Decision tree D→B / C→A / B-keep - -Defaults reflect cost-minimizing paths that match the SAP-recommended pattern: - -| Starting level | Default target | Path | -|---|---|---| -| D | **B** (via BAdI / enhancement-point) | rewrite_in_place. D objects usually have business logic; BAdI rewrite is the eligible pattern | -| C | **A** (via released API) | rewrite_in_place. Released equivalent typically exists | -| B | **B** (keep + document) | keep_at_level_b. B is already compliant; pushing to A is opt-in | -| A | A | no action | -| Unused | (removed) | sign-off + delete | - -Override the defaults with `--aggressive` (push B → A everywhere), `--target-level=B` (settle for B even when A possible), or `--push-to-a=A,B,C` (selective). - -### 5.5 Target deployment matrix - -Three supported targets (On-Premise CF dropped — EOL): - -| Target | CDS profile | Auth | DB | UI delivery | -|---|---|---|---|---| -| **BTP Cloud Foundry** *(default)* | `production` / `production-pg` | XSUAA | HANA HDI / BTP Postgres | `@sap/html5-app-repo` Free OK | -| **BTP Kyma** | `k8s` | OIDC (XSUAA / IAS) | PostgreSQL in-cluster / HANA Cloud | UI ZIPs embedded in approuter image | -| **On-Premise Kyma** | `k8s-onprem` / `k8s-hana` | OIDC via Keycloak | HANA on-prem / Postgres on-prem | UI ZIPs embedded; ingress via NGINX | - -Default is BTP CF: most widely-adopted SAP-managed runtime, broadest service ecosystem (Free + paid), most mature `mta.yaml` tooling, lowest operational ceremony. BTP Kyma is the deliberate choice for Kubernetes-operational-model customers. On-Premise Kyma is for data-sovereignty mandates. - -### 5.6 Cost model - -| Layer | Cost per refactor (50-200 objects) | Who pays | -|---|---|---| -| ARC-1 MCP | €0 | User (server hosting) | -| arc-1 native skills | €0 | (agent inference platform-billed) | -| secondsky/sap-skills | €0 | (one-time install) | -| Tier-1 git clones | €0 (~100 MB on first install) | User (bandwidth) | -| Tier-2 Apify JIT | €0.50-€5 | User (own Apify token) | -| Tier-4 MCP-server | €0 | User (server hosting) | - -**Total: €0.50-€5 per typical customer refactor, all user-paid. No centralized cost.** - ---- - -## 6. Dependencies - -### 6.1 Mandatory - -**ARC-1 MCP server** ([marianfoo/arc-1](https://github.com/marianfoo/arc-1)) - -Setup: -```bash -git clone https://github.com/marianfoo/arc-1.git -cd arc-1 -cp .env.example .env -# Edit .env with SAP_HOST, SAP_CLIENT, SAP_USER, SAP_PASSWORD -# Optional: SAP_ALLOW_FREE_SQL=true (for dead-code detection), SAP_ALLOW_GIT_WRITES=true -npm install && npm run build -# Add to Claude Code MCP config (~/.claude/settings.json) as 'arc-1' server -``` - -Without ARC-1 MCP, the refactor skill cannot discover or modify ABAP code. The audit skills work without ARC-1 (CAP-side only) but are less complete. - -### 6.2 MUST companion plugins (4 from secondsky/sap-skills) - -```bash -npx skills add secondsky/sap-skills -s sap-abap -g -y -npx skills add secondsky/sap-skills -s sap-abap-cds -g -y -npx skills add secondsky/sap-skills -s sap-cap-capire -g -y -npx skills add secondsky/sap-skills -s sap-btp-developer-guide -g -y -``` - -| Plugin | Used by | -|---|---| -| `sap-abap` | `sap-erp-clean-core-refactor` Step 6a (ABAP rewrite); `modernize-abap-*` chain | -| `sap-abap-cds` | `sap-erp-clean-core-refactor` when rewrite introduces CDS views; `modernize-abap-cap-schema` | -| `sap-cap-capire` (4 agents) | `sap-erp-clean-core-refactor` Step 6b (side-by-side); `modernize-abap-to-btp-cap` | -| `sap-btp-developer-guide` | All skills that resolve the deployment target | - -### 6.3 SHOULD companion plugins (7) - -```bash -npx skills add secondsky/sap-skills -s sapui5 -g -y -npx skills add secondsky/sap-skills -s sap-fiori-tools -g -y -npx skills add secondsky/sap-skills -s sapui5-linter -g -y -npx skills add secondsky/sap-skills -s sap-btp-cloud-platform -g -y -npx skills add secondsky/sap-skills -s sap-btp-connectivity -g -y -# sap-cloud-sdk and sap-cloud-sdk-ai are auto-installed with sap-btp-developer-guide bundle -``` - -### 6.4 OPTIONAL - -| Plugin | Use case | -|---|---| -| **Apify MCP server** | JIT documentation lookup (~€0.005-0.02 per page). Without it, the refactor skill degrades to manual mode | -| `mcp-sap-docs` | Preferred over Apify for SAP Help Portal queries; free when installed | -| `context7` | Generic library docs (non-SAP) | -| `playwright` MCP | Browser smoke tests for Fiori app audit | -| 12 optional secondsky plugins (job scheduling, cloud logging, MDI, cTMS, CIAS, BAS, integration suite, work zone, ISA) | Situational based on customer extension scope | - -### 6.5 Tier-1 git clones (free, weekly refresh) - -```bash -# One-time clone (~100 MB total) -mkdir -p .cache/git -git clone --depth 1 https://github.com/SAP/abap-atc-cr-cv-s4hc .cache/git/abap-atc-cr-cv-s4hc -git clone --depth 1 https://github.com/SAP/cloud-sdk .cache/git/cloud-sdk -git clone --depth 1 https://github.com/SAP-samples/cap-sflight .cache/git/cap-sflight -git clone --depth 1 https://github.com/SAP-samples/cloud-cap-samples .cache/git/cloud-cap-samples -git clone --depth 1 https://github.com/SAP-samples/btp-cap-multitenant-saas .cache/git/btp-cap-multitenant-saas -# (+ 3 more from SOURCES.md) - -# Weekly refresh -for d in .cache/git/*/; do (cd "$d" && git pull --ff-only --quiet); done -``` - -### 6.6 Tier-2 JIT Apify (paid per page) - -```bash -# One-time -export APIFY_API_TOKEN=apify_api_xxx # from console.apify.com/account/integrations -# Add apify-mcp to Claude Code MCP config -``` - -Without Apify, the refactor skill falls back to manual mode: it produces the plan with "consult URL X manually" pointers; the user pastes back doc snippets. Slower (~2-3× JIT) but free. - ---- - -## 7. Usage examples - -### 7.1 Example A — End-to-end Clean Core refactor of a Z* package - -Scenario: customer has `ZFI_INVOICES` package, 87 Z objects accumulated over 5 years, S/4HANA 2023 on-prem, target = BTP Cloud Foundry side-by-side. - -```bash -# Pre-flight: bootstrap system context (once per system) -/bootstrap-system-context S4DEV - -# Discovery + plan (defaults: target=btp-cf, mode=plan) -/sap-erp-clean-core-refactor ZFI_INVOICES - -# Review the plan -$EDITOR docs/refactor/2026-05-13-clean-core-plan.md -# (optional: edit decisions per row to override defaults) - -# Execute Phase 1 (quick wins: removes + Level B keepers) -/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=1 - -# Execute Phase 2 (in-place rewrites, low-risk first) -/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=2 - -# Execute Phase 3 (side-by-side extractions) -/sap-erp-clean-core-refactor ZFI_INVOICES execute --start-phase=3 -# This delegates to modernize-abap-to-btp-cap per extension - -# Verify the BTP side after deploy -cd bs/zfi-extensions/vendor-risk-score && cds deploy -/sap-cap-clean-core-enforce --enforce # blocks deploy if non-released API consumed -/sap-cap-stack-audit-full # pre-release audit - -# Capture learnings -/analyze-chat-session -``` - -**Expected outcome**: -- 47 objects rewritten in-place (mostly via released APIs → Level A) -- 12 objects extracted to 7 BTP CAP extensions -- 18 objects documented as Level B keepers -- 6 objects removed (unused) -- 4 objects deferred to research backlog -- ATC delta: 234 → 18 findings (-92%) -- Clean Core compliance: 14% → 87% -- Cost: ~€2-3 in Apify lookups, charged to user's account -- Wall time: 1-3 days - -### 7.2 Example B — Pre-release stack audit (BTP CAP project) - -Scenario: CAP project with 9 Fiori apps, ready for deployment to BTP CF. Want full audit before merge to main. - -```bash -# One command, runs everything in parallel -/sap-cap-stack-audit-full - -# Output: docs/audit/2026-05-13-stack-audit.md with: -# - Phase 1 pre-flight (CDS compile, project shape, target detected) -# - Phase 2 static (UI5 linter × 9, manifest validation × 9, TS typecheck, test suite, hardcoded sweep) -# - Phase 3 specialized (clean-core-enforce, customizing-honor, security-rbac-matrix, -# fiori-app-audit × 9, text-polish dry-run) -# - Phase 4 CAP deep (model integrity, profile audit, build dry-run) -# - Phase 5 best-practice cross-check -# - Phase 6 formal code review (if branch ≠ main) -# - Consolidated findings: Critical / High / Medium / Low -# - Severity grade A/B/C/D -``` - -**Expected outcome**: -- ~2-10 minutes wall time (`quick` mode skips Phase 3 agents → ~2 min) -- Single deduplicated report with file:line citations for every finding -- PRINCIPLE MISMATCH findings escalated to top of report -- Tools-unavailable section if some MCP not installed (graceful degradation) - -### 7.3 Example C — Security & compliance review - -Scenario: SOX auditor asks for evidence pack of OWASP / NIST CSF / GDPR controls. - -```bash -/sap-cap-security-rbac-matrix - -# Output: docs/audit/2026-05-13-security-rbac.md with: -# - Per-area findings (S1-S5 + O1 OWASP) -# - Role coherence matrix: xs-security.json ↔ Keycloak realm ↔ services-auth.cds ↔ handlers -# - Drift detected between layers (HIGH if any) -# - CC segregation audit (cross-company-code leak detection) -# - Compliance mapping: every finding maps to OWASP/ASVS/NIST CSF/CIS/SAP-SOM/GDPR/SOX citations -``` - -### 7.4 Example D — Single Fiori app audit - -Scenario: PR adds a new feature to `manager-ui` app; want to verify nothing regressed before merge. - -```bash -/sap-fiori-app-audit manager-ui - -# Output: docs/audit/2026-05-13-fiori-manager-ui.md -# - User journey walk (entry → filter → list → detail → action → refresh) -# - Frontend/backend contract chain for every primary action -# - Three failure classes detected: -# * UX-positive-false (button visible, backend rejects) -# * UX-negative-false (button hidden, backend would allow) -# * Stuck states - -# Optional: apply safe quick-win fixes on a dedicated branch -/sap-fiori-app-audit manager-ui fix -# Creates branch audit/fiori-manager-ui-2026-05-13 with: -# - Missing @Core.OperationAvailable added (referring to already-computed Can* flag) -# - Missing @Common.SideEffects added -# - Missing i18n key added to webapp/i18n/i18n.properties -# - Missing tooltip / @Common.Label -# Run scoped tests + verify, commit, open PR -``` - -### 7.5 Example E — Text polish before release - -Scenario: release candidate has user-visible text that sounds amateur; want polish before going live + PII safety check. - -```bash -/sap-cap-text-polish all tone:formal - -# Output: docs/audit/2026-05-13-text-polish.md -# - 10 anti-pattern detection per source (backend reject, frontend toast, i18n bundle, …) -# - Before/after rewrites for POLISH / REWRITE classified items -# - PII_RISK items (unmasked IBAN / fiscal code / VAT / email) flagged urgent -# - LEGAL_REVIEW_REQUIRED items (privacy/consent text) deferred to human - -# Apply safe rewrites -/sap-cap-text-polish all fix tone:formal -# Creates branch audit/text-polish-all-2026-05-13 with: -# - Typo / capitalization fixes -# - ICU placeholders added (concatenation → {0}) -# - PII masking helper calls inserted -# - Missing i18n keys created -# - All within ≤50 lines diff per file -``` - -### 7.6 Example F — Setting up CI gates from scratch - -Scenario: new CAP project, no CI gates yet, want to lock structural invariants. - -```bash -# Describe what patterns would apply (no file generation) -/sap-cap-ci-gates-pattern - -# Output: which of the 5 patterns apply based on your project structure -# (bidirectional CSV ↔ code; catalog raise-coverage; API-availability drift; -# convention/matrix drift; CSV schema lint) - -# Generate all applicable gates -/sap-cap-ci-gates-pattern all apply - -# Generated files: -# - scripts/ci/check-settings-bidirectional.sh -# - scripts/ci/check-catalog-raise-coverage.sh -# - scripts/ci/check-availability-drift.sh + check-availability-drift.js -# - scripts/ci/check-convention-drift.sh -# - scripts/ci/check-csv-lint.js -# - .github/workflows/ci-gates.yml (matrix strategy, 5 parallel jobs) - -# Review + commit + push → next PR is gated -``` - -### 7.7 Example G — Side-by-side extension scaffold from a Z FM - -Scenario: `sap-erp-clean-core-refactor` decided `ZFM_VENDOR_RISK_SCORE` should be `extract_to_side_by_side`. Now generate the CAP scaffold. - -```bash -# Delegated automatically during refactor execute mode, OR invoke directly: -/modernize-abap-to-btp-cap ZFM_VENDOR_RISK_SCORE --target=btp-cf - -# Internally orchestrates: -# 1. modernize-abap-cap-schema — derives CDS entities from FM I/O parameters + LFA1 deps -# 2. modernize-abap-cap-service — generates CAP service action that replicates the FM semantics -# 3. Wires deployment manifest (mta.yaml for btp-cf) - -# Output: bs/vendor-risk-score/ — a complete CAP project -# db/schema.cds (Vendors entity from BP CDS + RiskScore custom field) -# srv/risk-service.cds (calculateRisk action) -# srv/risk-service.ts (handler implementation) -# mta.yaml (BTP CF deployment manifest) -# xs-security.json (XSUAA scope) -# package.json (cds + dependencies) -# README.md (extension purpose + deploy steps) - -cd bs/vendor-risk-score && npm install && cds deploy && mbt build && cf deploy -``` - ---- - -## 8. PR roadmap - -Four open draft PRs on `marianfoo/arc-1`. They're structurally independent and can be merged in any order; cross-links between PRs degrade gracefully to "see also" hints until all are merged. - -| PR | Branch | Scope | Lines | Skills | -|---|---|---|---|---| -| **#278** | `feat/skills-modernize-abap-to-btp-cap` | Modernization chain (Z-table → CDS, FM → CAP) | ~1200 | 3 | -| **#279** | `feat/skills-sap-cap-audit-wave1` | Clean Core enforce + Customizing honor | ~970 | 2 | -| **#280** | `feat/skills-sap-cap-audit-wave2-3` | Audit toolkit + master battle-tested-patterns + orchestrator + CI gates | ~3000 | 6 | -| **#281** | `feat/skills-erp-clean-core-refactor` | ERP refactor planner (JIT, decision tree, ARC-1 + secondsky integration) | ~925 | 1 + 2 reference docs | -| (this) | `feat/docs-sap-cap-toolkit-overview` | This consolidated doc | ~700 | 0 (doc-only) | - -**Total upstream**: 13 skills + 2 reference docs (SOURCES.md, INTEGRATIONS.md) + this overview = **~6800 lines**, 100% generic, zero project-specific references. - ---- - -## 9. FAQs - -### Why JIT lookups instead of a pre-built KB? - -Pre-crawl of 18 SAP sources weekly costs €400-780/year on Apify and produces gigabytes of mostly-unread content. Even weekly crawl is 7 days stale at worst. JIT per-finding lookups cost €0.50-5 per refactor, are always fresh, and the user pays only for what they consume. See [§5.1](#51-jit-lookups-no-pre-built-kb). - -### What if my customer has no BTP plan? - -The refactor skill works without BTP. All `extract_to_side_by_side` decisions become `research_required` (not actionable), and the skill defaults to `rewrite_in_place` (Level A or Level B) plus `keep_at_level_b`. Compliance score will be lower (60-70% vs 85%+) but the refactor still progresses. - -### Can I use only the audit skills without the refactor skill? - -Yes. The audit skills (`sap-cap-clean-core-enforce`, `sap-cap-customizing-honor`, `sap-cap-security-rbac-matrix`, `sap-fiori-app-audit`, `sap-cap-text-polish`) work standalone on any CAP+Fiori project, with no ARC-1 MCP requirement. The orchestrator (`sap-cap-stack-audit-full`) coordinates them. - -### What's the difference between this toolkit and `sap-clean-core-atc`? - -- `sap-clean-core-atc` (existing arc-1 native skill) classifies **ABAP custom code on the SAP side** into Levels A/B/C/D. It answers "what is each Z object's level?" -- `sap-erp-clean-core-refactor` (this toolkit, new) **plans and executes the refactor** to move each Z object toward Level A. It answers "what should we do with each Z object?" -- `sap-cap-clean-core-enforce` (this toolkit, new) verifies **the BTP CAP application's outbound S/4 API consumption** is Level A. It answers "are all the S/4 APIs our CAP app calls actually released?" - -The three skills are complementary, addressing classification, refactor planning, and consumption verification respectively. - -### What changes when the deployment target is On-Premise Kyma? - -- CDS profile becomes `k8s-onprem` instead of `k8s`. -- Authentication switches from XSUAA to OIDC via Keycloak (customer's IdP). -- Side-by-side BTP extensions deploy to the customer's K8s cluster instead of BTP Kyma. -- The toolkit's audit skills handle the difference automatically. `sap-cap-stack-audit-full` Step 0 asks the target. - -### Why is On-Premise CF not supported? - -SAP Cloud Foundry On-Premise reached end of maintenance. The toolkit does not plan deployments against an EOL runtime. Customers with existing on-prem CF investment should migrate to BTP CF (managed) or BTP Kyma. - -### Can I run the toolkit on a project that's not on GitHub? - -Yes. The skills are filesystem-driven (read CDS, manifest, schema, etc. from the local working directory). CI gate generation supports GitHub Actions, GitLab CI, Jenkins, and BTP CI/CD. Only the cross-link to `SAP/abap-atc-cr-cv-s4hc` and the optional Apify references require network. - -### Where do I report issues with the toolkit? - -Open an issue on the relevant PR (#278 / #279 / #280 / #281) or against `marianfoo/arc-1` if the issue is in the underlying MCP. For documentation issues with this overview, open against this PR. - ---- - -## 10. References - -### Toolkit skills (this contribution) - -- [`sap-cap-fiori-battle-tested-patterns`](../skills/sap-cap-fiori-battle-tested-patterns/SKILL.md) — reference knowledge base -- [`sap-cap-stack-audit-full`](../skills/sap-cap-stack-audit-full/SKILL.md) — orchestrator -- [`sap-cap-ci-gates-pattern`](../skills/sap-cap-ci-gates-pattern/SKILL.md) — CI gate library -- [`sap-cap-clean-core-enforce`](../skills/sap-cap-clean-core-enforce/SKILL.md) — Tier-2 S/4 API gate -- [`sap-cap-customizing-honor`](../skills/sap-cap-customizing-honor/SKILL.md) — CSV ↔ code audit -- [`sap-cap-security-rbac-matrix`](../skills/sap-cap-security-rbac-matrix/SKILL.md) — security + compliance -- [`sap-fiori-app-audit`](../skills/sap-fiori-app-audit/SKILL.md) — Fiori app audit -- [`sap-cap-text-polish`](../skills/sap-cap-text-polish/SKILL.md) — user-text + PII -- [`sap-erp-clean-core-refactor`](../skills/sap-erp-clean-core-refactor/SKILL.md) — ERP refactor planner - - [`SOURCES.md`](../skills/sap-erp-clean-core-refactor/SOURCES.md) — authoritative SAP source catalog - - [`INTEGRATIONS.md`](../skills/sap-erp-clean-core-refactor/INTEGRATIONS.md) — ARC-1 MCP × skill × secondsky mapping -- [`modernize-abap-to-btp-cap`](../skills/modernize-abap-to-btp-cap/SKILL.md) — orchestrator -- [`modernize-abap-cap-schema`](../skills/modernize-abap-cap-schema/SKILL.md) — Z-table → CDS -- [`modernize-abap-cap-service`](../skills/modernize-abap-cap-service/SKILL.md) — FM → CAP service - -### Supporting arc-1 native skills - -- [`bootstrap-system-context`](../skills/bootstrap-system-context/SKILL.md) -- [`setup-abap-mirror`](../skills/setup-abap-mirror/SKILL.md) -- [`explain-abap-code`](../skills/explain-abap-code/SKILL.md) -- [`sap-clean-core-atc`](../skills/sap-clean-core-atc/SKILL.md) -- [`sap-unused-code`](../skills/sap-unused-code/SKILL.md) -- [`sap-object-documenter`](../skills/sap-object-documenter/SKILL.md) -- [`generate-abap-unit-test`](../skills/generate-abap-unit-test/SKILL.md) -- [`generate-cds-unit-test`](../skills/generate-cds-unit-test/SKILL.md) -- [`generate-rap-logic`](../skills/generate-rap-logic/SKILL.md) -- [`generate-rap-service-researched`](../skills/generate-rap-service-researched/SKILL.md) -- [`migrate-segw-to-rap`](../skills/migrate-segw-to-rap/SKILL.md) -- [`migrate-custom-code`](../skills/migrate-custom-code/SKILL.md) -- [`convert-ui5-to-fiori-elements`](../skills/convert-ui5-to-fiori-elements/SKILL.md) -- [`modernize-ui5-app`](../skills/modernize-ui5-app/SKILL.md) -- [`analyze-chat-session`](../skills/analyze-chat-session/SKILL.md) - -### External - -- [ARC-1 MCP server](https://github.com/marianfoo/arc-1) — 12 ABAP intent-based tools -- [secondsky/sap-skills](https://github.com/secondsky/sap-skills) — 32-plugin SAP catalog -- [SAP/abap-atc-cr-cv-s4hc](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — released ABAP objects authority -- [SAP API Hub](https://api.sap.com/) — OData service catalog + lifecycle -- [SAP Cloud Application Programming Model](https://cap.cloud.sap/docs/) — CAP capire -- [SAP Fiori Elements V4 Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) -- [BTP Reference Architectures](https://help.sap.com/docs/btp/sap-business-technology-platform/reference-architectures) -- [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/) -- [NIST CSF 2.0](https://www.nist.gov/cyberframework) -- [Apify Website Content Crawler](https://apify.com/apify/website-content-crawler) - -### Methodology - -- [Clean Core principles (BTP docs)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) -- [SAP Custom Code Migration Guide](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) -- [SAP Activate Roadmap Viewer](https://go.support.sap.com/roadmapviewer/) (S-user login required) - ---- - -## License & contribution - -This documentation, like the skills it describes, is licensed under the arc-1 fork's license (MIT-compatible). Contributions welcome via PR against `marianfoo/arc-1`. For corrections to this overview, open against the PR that introduced it. diff --git a/skills/README.md b/skills/README.md index f2c04103..3161cf74 100644 --- a/skills/README.md +++ b/skills/README.md @@ -4,7 +4,7 @@ Best-practice agent skills for common SAP development workflows with ARC-1. Each skill is a directory containing a `SKILL.md` file with YAML frontmatter — the format used by [Anthropic Agent Skills](https://code.claude.com/docs/en/skills) and consumed by the [`vercel-labs/skills`](https://github.com/vercel-labs/skills) CLI. Agents discover them by `description` and load them on demand. -> **Looking for the SAP CAP + Clean Core toolkit?** See [`../docs/sap-cap-toolkit.md`](../docs/sap-cap-toolkit.md) for a consolidated overview of the 13-skill toolkit contributed in PRs #278–#281 — visual map, full skill catalog, architectural strategies (JIT lookups, defense-in-depth, three-layer architecture, decision tree, target deployment matrix, cost model), dependency setup, 7 worked usage examples, FAQs, and PR roadmap. +> **CAP-side audit toolkit lives in a separate repo.** For audit / hardening / CI gates of CAP + Fiori Elements V4 + BTP applications consuming S/4HANA Tier-2 services, see [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit) (8 skills: `sap-cap-clean-core-enforce`, `sap-cap-customizing-honor`, `sap-cap-security-rbac-matrix`, `sap-fiori-app-audit`, `sap-cap-text-polish`, `sap-cap-stack-audit-full`, `sap-cap-ci-gates-pattern`, `sap-cap-fiori-battle-tested-patterns`). The skills in `arc-1` focus on ABAP-side custom-code refactoring (`sap-erp-clean-core-refactor` + `modernize-abap-*` chain); they generate CAP code that the audit toolkit in the companion repo verifies. ## Install via the `skills` CLI (recommended) @@ -102,23 +102,6 @@ Both skills produce the same RAP artifact stack. The difference is how they get | [migrate-custom-code](migrate-custom-code/SKILL.md) | Runs ATC readiness checks, groups findings by priority, and generates replacement code | Preparing custom code for S/4HANA migration or ABAP Cloud readiness | | [sap-object-documenter](sap-object-documenter/SKILL.md) | Batch-documents many custom objects at once — purpose, style (Classic/Modern/Mixed), dependencies — as Markdown | Onboarding packages, handoffs, seeding a repo wiki (vs. explain-abap-code which is single-object interactive) | -### SAP CAP Enterprise Audit (Preview) - -End-to-end audit and compliance verification for SAP CAP applications deployed on BTP (Cloud Foundry or Kyma) consuming S/4HANA Tier-2 services. Read-only skills that produce committable markdown reports; `fix` modes are limited to safe additive corrections on dedicated branches. - -| Skill | What it does | When to use | -|---|---|---| -| [sap-cap-clean-core-enforce](sap-cap-clean-core-enforce/SKILL.md) | Discovery-driven Clean Core Level A audit. Scans `cds.connect.to()` runtime + `@cds.external` services, probes SAP API release-state repository via mcp-sap-docs, builds availability matrix (Public × Private × On-Premise), detects catalog drift, suggests SAP-released replacements | Pre-deployment audit / quarterly compliance check for CAP+S/4 stacks | -| [sap-cap-customizing-honor](sap-cap-customizing-honor/SKILL.md) | Bidirectional CSV↔code customizing audit: forward orphans (seeded but unused) + inverse orphans (code-read but unseeded) + hardcoded business-decision sweep + master-data FK ValueList enforcement | Verifying that admin Setup UI parameters are wired to code consumers; pre-release coverage check | -| [sap-cap-security-rbac-matrix](sap-cap-security-rbac-matrix/SKILL.md) | Multi-area parallel security scan (handlers, MCP, file-upload, deploy, jobs) + OWASP Top 10 orthogonal pass + role coherence matrix across 4 layers (xs-security ↔ IdP realm ↔ services-auth ↔ handlers) + compliance mapping (OWASP / ASVS / NIST CSF / CIS / SAP-SOM / GDPR / SOX) | Pre-release security audit; quarterly compliance verification; auditor evidence pack | -| [sap-fiori-app-audit](sap-fiori-app-audit/SKILL.md) | Single Fiori Elements V4 app audit — user journey, frontend/backend contract chain, manifest + annotations + EDMX alignment, computed flag matrix, i18n coverage, action availability, draft behaviour. Optional safe quick-win fixes on a dedicated branch | Before merging a Fiori app PR; after UI5 version bump; quarterly regression check | -| [sap-cap-text-polish](sap-cap-text-polish/SKILL.md) | Audit and rewrite user-visible text (backend reject/throw, helper rejects, frontend toasts/dialogs, i18n bundles, CDS labels, CodeList descriptions). Detects ten anti-patterns including PII leak. Locale-aware, tone-profile-driven, additive safe rewrites only | Pre-release polish; PII safety net for audit logging; after localization phase | -| [sap-cap-stack-audit-full](sap-cap-stack-audit-full/SKILL.md) | Orchestrator that runs the full audit stack in parallel — UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded-customizing sweep, test suite + the specialized audit skills above — and consolidates findings into a single deduplicated report | Pre-release situational awareness; project hand-off baseline; after large refactors | -| [sap-cap-ci-gates-pattern](sap-cap-ci-gates-pattern/SKILL.md) | Library of five reusable CI gate patterns (bidirectional CSV↔code, catalog raise-coverage, API-availability drift, convention-matrix drift, CSV schema lint). Generates portable shell scripts + GitHub Actions / GitLab / Jenkins workflow YAML | Setting up CI for a new CAP project; locking in audit findings as enforced gates | -| [sap-cap-fiori-battle-tested-patterns](sap-cap-fiori-battle-tested-patterns/SKILL.md) | Knowledge base of ~60 production-distilled patterns and gotchas across eight categories (UI5/FE V4 traps, CAP/TypeScript pitfalls, BTP/Kyma deployment, security defense-in-depth, customizing patterns, lifecycle discipline, post-commit events/messaging, ecosystem plugin landscape). Cross-linked by the operational audit skills | As a reference when reviewing best practices, diagnosing bugs, onboarding to CAP+Fiori, or auditing — invoke directly or via the cross-links from other CAP audit skills | - -> **Status**: Preview. The eight skills above form the **SAP CAP Enterprise Audit toolkit**. They are designed to chain together: see the *Typical Workflow* sections below for `sap-cap-stack-audit-full` and `sap-cap-ci-gates-pattern` as composition entry points. Each skill includes a **"Recommended Companion Plugins"** section that lists external skills (`sap-cap-capire`, `sapui5`, `sap-btp-*`, `sap-docs`, `context7`, `playwright`, …) that complement it in a real CAP+Fiori+BTP deployment. - ### SAP ERP — Clean Core Return + Side-by-Side Refactor (Preview) Plans and executes the refactor of custom ABAP / ERP / S/4HANA code back to Clean Core compliance — discovery via ARC-1, classification via `sap-clean-core-atc`, **just-in-time documentation lookup** against authoritative SAP sources (no pre-crawled KB), and hand-off to `modernize-abap-to-btp-cap` for side-by-side extension scaffolds. @@ -127,7 +110,7 @@ Plans and executes the refactor of custom ABAP / ERP / S/4HANA code back to Clea |---|---|---| | [sap-erp-clean-core-refactor](sap-erp-clean-core-refactor/SKILL.md) | Inventories Z/Y custom code via ARC-1, classifies Clean Core Level A/B/C/D, consults authoritative SAP sources just-in-time (git-clone of `abap-atc-cr-cv-s4hc` + curated `SAP-samples` + `cloud-sdk`; Apify on-demand for `api.sap.com` / `help.sap.com` / `developers.sap.com` / `cap.cloud.sap` / community / blogs), and emits a per-object refactor plan with rewrite-in-place / extract-to-side-by-side / keep-at-B decisions. Apify lookups bounded per finding (~5 pages); user pays for own Apify account; results cached locally for 30 days | Planning a Clean Core compliance program; pre / post-S/4HANA migration cleanup; quarterly governance review | -The skill ships **three files**: [`SKILL.md`](sap-erp-clean-core-refactor/SKILL.md) (the protocol), [`SOURCES.md`](sap-erp-clean-core-refactor/SOURCES.md) (the curated catalog of 23 authoritative SAP sources organized in 4 tiers — Tier 1 git-cloneable, Tier 2 JIT Apify, Tier 3 manual-consultation auth-gated, Tier 4 MCP-server-backed), and [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md) (the step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × [secondsky/sap-skills](https://github.com/secondsky/sap-skills) plugin × external sources). **No centralized KB is shipped**; documentation lookups happen on-demand within a bounded per-finding budget, charged to the user's own Apify account at ~€0.005-0.02 per lookup (typical refactor: €0.50-€5 total). +The skill ships **four files** so it is fully self-contained: [`SKILL.md`](sap-erp-clean-core-refactor/SKILL.md) (the protocol), [`SOURCES.md`](sap-erp-clean-core-refactor/SOURCES.md) (curated catalog of 23 authoritative SAP sources organized in 4 tiers), [`PATTERNS.md`](sap-erp-clean-core-refactor/PATTERNS.md) (~70 battle-tested production patterns in 8 categories — consulted during target resolution and side-by-side scaffold), and [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md) (step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × [secondsky/sap-skills](https://github.com/secondsky/sap-skills) plugin × external sources). **No centralized KB is shipped**; documentation lookups happen on-demand within a bounded per-finding budget, charged to the user's own Apify account at ~€0.005-0.02 per lookup (typical refactor: €0.50-€5 total). **Required companion plugins** (the skill is materially less useful without them — all four enforced as MUST in [`INTEGRATIONS.md`](sap-erp-clean-core-refactor/INTEGRATIONS.md)): - **`sap-abap`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap)) — ABAP language patterns reference, required during Step 6a `rewrite_in_place` so generated ABAP is Cloud-compatible. diff --git a/skills/sap-cap-ci-gates-pattern/SKILL.md b/skills/sap-cap-ci-gates-pattern/SKILL.md deleted file mode 100644 index d43ee211..00000000 --- a/skills/sap-cap-ci-gates-pattern/SKILL.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -name: sap-cap-ci-gates-pattern -description: Library of five reusable CI gate patterns for SAP CAP projects — bidirectional CSV ↔ code consistency, catalog raise-coverage, released-state / API-availability drift detection, convention-matrix drift, and CSV schema lint. Each pattern is a small portable shell script with a clear contract (inputs, exit codes, output format) that plugs into GitHub Actions, GitLab CI, Jenkins, or BTP CI/CD pipelines. Use when asked to "add a CI gate", "build a drift-detection check", "enforce CSV/code parity", "prevent orphan catalog entries", "set up CI for a CAP project", or to harden a project against regressions of a structural invariant. ---- - -# SAP CAP — CI Gates Pattern Library - -This skill is a **library of reusable CI gate patterns** for SAP CAP projects, not a runner. It teaches the agent to **build** five small portable shell-based gates that catch the most common classes of regression in CAP+Fiori projects, and to wire them into a CI pipeline (GitHub Actions, GitLab CI, Jenkins, or BTP CI/CD). - -Each gate follows a deliberate contract: -- One concern per gate. -- POSIX `sh` or `bash` only; no Node/Python dependency unless the project already requires it. -- Idempotent: same repo state → same exit code. -- Exit `0` = pass, `1` = fail (CI-blocking), `2` = warning (non-blocking, prints but allows continue). -- Output: structured (TSV / JSON-lines) so humans and automation can read it. - -The skill is **descriptive** (it explains the patterns) and **generative** (in `apply` mode it writes the scripts and the workflow YAML into the project). - -## v1 Guardrails - -- **Generates scripts only.** Never edits production code, never opens PRs. -- **Idempotent generation.** Re-running `apply` overwrites the gate scripts only if `--force` is passed; otherwise it skips existing files. -- **Pattern-first, project-second.** The skill teaches the pattern; the user chooses which patterns apply to their project. Do not apply all five if some are irrelevant. -- **Cite exit codes and output format** in the generated scripts so CI logs are self-explanatory. - -## Smart Defaults (apply silently, do NOT ask) - -| Aspect | Default | Why | -| --- | --- | --- | -| Mode | `describe` (explain patterns, generate nothing) | Safer; user opts into `apply` | -| Script location | `scripts/ci/` | Common convention | -| CI provider | GitHub Actions (`.github/workflows/`) | Most common in CAP open-source | -| Shell | `bash` with `set -euo pipefail` | Portable + safe defaults | -| Exit semantics | `0` pass · `1` fail · `2` warn | Aligns with standard CI runners | -| Output format | Plain text for humans, optional `--json` flag for automation | Easier to triage in CI logs | - -## Input - -Single optional argument with format `<pattern> [mode]`: - -| Argument | Meaning | -| --- | --- | -| (empty) | Describe all five patterns; generate nothing | -| `bidirectional` · `raise-coverage` · `availability-drift` · `convention-drift` · `csv-lint` | Describe one specific pattern | -| `all apply` | Generate all five gates into `scripts/ci/` + a workflow YAML | -| `<pattern> apply` | Generate only that one pattern | - -Examples: (no arg) · `bidirectional apply` · `all apply`. - -## The Five Patterns - -### Pattern 1 — Bidirectional CSV ↔ Code Consistency - -**Intent.** When a project uses a settings table seeded via CSV (e.g. SystemParameter pattern), each parameter must have a consumer in code, and each `params.X` read in code must have a CSV seed. Otherwise the admin UI surfaces dead settings, or the code reads from a setting the admin doesn't know exists. - -**Inputs.** -- CSV file: e.g. `db/data/sap.<namespace>-Settings.csv` with a column representing the key (often the first column, e.g. `Key`). -- Code paths: `srv/` and any subset where settings are consumed. -- Reader pattern: typically `params.X`, `cfg.X`, `<helper>.get(...).<X>`, or similar — discovered from the project. - -**Algorithm.** -1. Extract CSV keys → `csv-keys.txt`. -2. Grep code for keys read from settings → `code-keys.txt`. -3. `comm -23 code-keys.txt csv-keys.txt` → **inverse orphans** (code reads, CSV missing). -4. `comm -23 csv-keys.txt code-keys.txt` → **forward orphans** (CSV seeds, no consumer). -5. Apply an allowlist (some seed keys are intentionally consumed by external systems and won't appear in code). -6. Exit `1` if either list is non-empty (minus allowlist). - -**Skeleton (`scripts/ci/check-settings-bidirectional.sh`).** - -```bash -#!/usr/bin/env bash -set -euo pipefail - -CSV="${1:-db/data/<project>-Settings.csv}" -CODE_DIR="${2:-srv}" -KEY_COL="${3:-1}" # column index in the CSV (1-based) -ALLOWLIST="${4:-scripts/ci/settings-allowlist.txt}" - -# Extract CSV keys (skip header) -awk -F',' -v col="$KEY_COL" 'NR>1 && $col!="" {print $col}' "$CSV" | sort -u > /tmp/csv-keys.txt - -# Extract code-read keys — adapt the regex to the project's reader pattern -grep -rohE "(p|params|cfg|config)\??\.([A-Z][A-Z0-9_]+)" "$CODE_DIR" --include="*.ts" --include="*.js" 2>/dev/null \ - | grep -oE "[A-Z][A-Z0-9_]+" \ - | sort -u > /tmp/code-keys.txt - -# Optional allowlist (one key per line) -test -f "$ALLOWLIST" || : > /tmp/allowlist.txt -cat "${ALLOWLIST:-/tmp/allowlist.txt}" 2>/dev/null | sort -u > /tmp/allowlist-keys.txt || : > /tmp/allowlist-keys.txt - -INV=$(comm -23 /tmp/code-keys.txt /tmp/csv-keys.txt | comm -23 - /tmp/allowlist-keys.txt) -FWD=$(comm -23 /tmp/csv-keys.txt /tmp/code-keys.txt | comm -23 - /tmp/allowlist-keys.txt) - -INV_N=$(echo -n "$INV" | grep -c . || true) -FWD_N=$(echo -n "$FWD" | grep -c . || true) - -echo "Inverse orphans (code reads, CSV missing): $INV_N" -echo "$INV" | head -50 -echo -echo "Forward orphans (CSV seeds, code missing): $FWD_N" -echo "$FWD" | head -50 - -test "$INV_N" -eq 0 -a "$FWD_N" -eq 0 -``` - -**Tuning.** The regex on line 16 is the only project-specific part. Map it to your project's reader signature. - -### Pattern 2 — Catalog Raise-Coverage - -**Intent.** When a project has a "rule catalog" (e.g. `ProcessStepCheck` codes, validation rules, error categories), each catalog entry that is active must be **raised** from at least one place in code; otherwise the catalog is lying. - -**Inputs.** -- Catalog CSV: e.g. `db/data/sap.<namespace>-ProcessStepCheck.csv` with a column `Code` and a column `IsActive` (or equivalent). -- Code paths: `srv/`. -- Raise pattern: typically a helper like `raiseCatalogException(...)`, `raiseError(code, ...)`, or `req.reject(code, ...)`. - -**Algorithm.** -1. Extract active codes from CSV → `active-codes.txt`. -2. Grep code for `<raiseHelper>(.*CODE_X)` matches → `raised-codes.txt`. -3. `comm -23 active-codes.txt raised-codes.txt` → unraised codes. -4. Exit `1` if non-empty. - -**Skeleton.** Identical structure to Pattern 1; the regex on the grep is the only difference. Allowlist is supported (some codes are raised dynamically via configuration). - -### Pattern 3 — Released-State / API-Availability Drift - -**Intent.** SAP CAP projects often consume S/4HANA APIs that have a **release state** (C1 released, C2 sandbox, C3 deprecated). When a project pins a service catalog with an `availability` array per edition (e.g. `Public Cloud`, `Private Cloud`, `On-Premise`), drift between the local pin and the upstream `SAP/abap-atc-cr-cv-s4hc` repository (or equivalent ABAP API Release State source) must be caught. - -**Inputs.** -- Local service catalog file: e.g. `srv/integration/s4CompatibilityPolicy.js` exporting `services[]` with `{name, availability, probeObject}`. -- Upstream source: `SAP/abap-atc-cr-cv-s4hc` repository (or analogous). -- Cache directory for the upstream snapshot. - -**Algorithm.** -1. Refresh local cache of the upstream API Release State (clone or pull). -2. For each entry in the local catalog, query the upstream for the matching object + edition. -3. If upstream marks the object as **deprecated** for an edition the local catalog still claims as available, raise a finding. -4. If upstream marks the object as **available** for an edition the local catalog doesn't list, raise an informational finding (potentially missed capability). -5. Exit `1` on deprecation drift, `2` on missed-capability drift. - -**Skeleton outline.** Detailed implementation lives in [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md); this pattern is the **CI scheduling** of that audit. Typical wrapper: - -```bash -#!/usr/bin/env bash -set -euo pipefail - -CATALOG="${1:-srv/integration/<catalog>.js}" -UPSTREAM_CACHE="${2:-.cache/abap-atc-cr-cv-s4hc}" - -# Refresh cache (idempotent) -if [ -d "$UPSTREAM_CACHE/.git" ]; then - (cd "$UPSTREAM_CACHE" && git pull --ff-only --quiet) -else - git clone --depth 1 --quiet https://github.com/SAP/abap-atc-cr-cv-s4hc "$UPSTREAM_CACHE" -fi - -# Invoke the matrix audit (delegates to sap-cap-clean-core-enforce logic) -node scripts/ci/check-availability-drift.js "$CATALOG" "$UPSTREAM_CACHE" "${3:---format=tsv}" -``` - -The `.js` companion script reads the local catalog and compares each entry against the upstream — pseudocode in [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md). - -### Pattern 4 — Convention / Matrix Drift Detection - -**Intent.** When a project codifies a convention in an ADR or matrix document (e.g. "every cap-js plugin in `package.json` must be documented in ADR 0011 with status `Adopted` / `Deferred` / `Not-applicable`"), CI must enforce the two-way binding: package change without ADR change = drift; ADR change without package change = also drift. - -**Inputs.** -- Source A: e.g. `package.json` dependencies matching a prefix (`@cap-js/*`). -- Source B: e.g. `docs/adr/0011-*.md` containing a table or YAML block enumerating the plugins. - -**Algorithm.** -1. Extract plugin names from Source A. -2. Extract plugin names from Source B. -3. Symmetric difference → drift list. -4. Exit `1` if non-empty. - -**Skeleton.** - -```bash -#!/usr/bin/env bash -set -euo pipefail - -PKG="${1:-package.json}" -ADR="${2:-docs/adr/<matrix-adr>.md}" -PREFIX="${3:-@cap-js/}" - -# From package.json: every dependency matching PREFIX -node -e "const p = require('./$PKG'); const all = {...(p.dependencies||{}), ...(p.devDependencies||{})}; Object.keys(all).filter(k => k.startsWith('$PREFIX')).forEach(k => console.log(k))" | sort -u > /tmp/pkg-list.txt - -# From ADR: every plugin name appearing as an inline-code segment matching PREFIX -grep -oE "\`$PREFIX[a-z-]+\`" "$ADR" | tr -d '`' | sort -u > /tmp/adr-list.txt - -DRIFT_PKG_ONLY=$(comm -23 /tmp/pkg-list.txt /tmp/adr-list.txt) -DRIFT_ADR_ONLY=$(comm -13 /tmp/pkg-list.txt /tmp/adr-list.txt) - -DPO=$(echo -n "$DRIFT_PKG_ONLY" | grep -c . || true) -DAO=$(echo -n "$DRIFT_ADR_ONLY" | grep -c . || true) - -echo "In package.json but missing from ADR: $DPO" -echo "$DRIFT_PKG_ONLY" -echo -echo "In ADR but missing from package.json: $DAO" -echo "$DRIFT_ADR_ONLY" - -test "$DPO" -eq 0 -a "$DAO" -eq 0 -``` - -### Pattern 5 — CSV Schema Lint - -**Intent.** Catch CSV seed corruption before it reaches deployment: missing columns, type mismatches, FK referential integrity violations, duplicate primary keys. - -**Inputs.** -- CSV files under `db/data/`. -- Schema definition: the corresponding entity in `db/schema.cds`. - -**Algorithm.** -1. For each CSV file `<entity>.csv`: - - Read the header row. - - Find the entity in `db/schema.cds`; build a column → type map. - - Verify CSV header matches entity columns (set equality). - - For each row: type-check each cell (UUID, Integer, Decimal, Date/DateTime, Boolean, String length). - - Check primary key uniqueness. - - For FK columns, verify referent exists in the referent CSV. -2. Exit `1` on any violation. - -**Skeleton outline.** This pattern usually requires Node because CSV + CDS introspection is non-trivial in pure shell. Use the project's `@sap/cds` library directly via the Node API (no shell-out to user-provided strings): - -```javascript -// scripts/ci/check-csv-lint.js -const cds = require('@sap/cds'); -const fs = require('fs'); -const path = require('path'); - -(async () => { - const csn = await cds.load(['db', 'srv']); - const csvDir = 'db/data'; - const files = fs.readdirSync(csvDir).filter(f => f.endsWith('.csv')); - let failures = 0; - - for (const f of files) { - // entity name encoded in filename: <namespace>-<EntityName>.csv - const entityName = /-([A-Z]\w+)\.csv$/.exec(f)?.[1]; - if (!entityName) continue; - const entity = Object.values(csn.definitions).find(d => d.name?.endsWith('.' + entityName) && d.kind === 'entity'); - if (!entity) { console.error('Schema not found for', f); failures++; continue; } - - const content = fs.readFileSync(path.join(csvDir, f), 'utf8').trim().split('\n'); - const header = content[0].split(','); - const expected = Object.keys(entity.elements).filter(k => !entity.elements[k].virtual); - - // Header set equality - const missing = expected.filter(k => !header.includes(k)); - const extra = header.filter(k => !expected.includes(k) && k !== 'IsActiveEntity'); - if (missing.length || extra.length) { - console.error(`[${f}] header mismatch — missing: ${missing}; extra: ${extra}`); - failures++; - } - - // Type-check rows (subset of types) - for (let i = 1; i < content.length; i++) { - const cells = content[i].split(','); - header.forEach((col, idx) => { - const def = entity.elements[col]; - if (!def) return; - const v = cells[idx]; - if (def.type === 'cds.UUID' && v && !/^[0-9a-f-]{36}$/i.test(v)) { - console.error(`[${f}:${i+1}] ${col} not a UUID: ${v}`); - failures++; - } - if (def.type === 'cds.Integer' && v && !/^-?\d+$/.test(v)) { - console.error(`[${f}:${i+1}] ${col} not an Integer: ${v}`); - failures++; - } - // ... extend for Decimal / Boolean / Date / String length - }); - } - } - - process.exit(failures > 0 ? 1 : 0); -})(); -``` - -Wrap with a bash script that invokes it and surfaces the exit code. - -## Wiring into GitHub Actions - -The generated workflow runs every gate on every PR. Use a job matrix so a single failing gate doesn't cancel the others. - -```yaml -# .github/workflows/ci-gates.yml -name: CI Gates -on: - pull_request: - push: - branches: [main] - -jobs: - gates: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - gate: - - { name: bidirectional, cmd: "bash scripts/ci/check-settings-bidirectional.sh" } - - { name: raise-coverage, cmd: "bash scripts/ci/check-catalog-raise-coverage.sh" } - - { name: availability-drift, cmd: "bash scripts/ci/check-availability-drift.sh" } - - { name: convention-drift, cmd: "bash scripts/ci/check-convention-drift.sh" } - - { name: csv-lint, cmd: "node scripts/ci/check-csv-lint.js" } - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version: '22' } - - run: npm ci - - name: ${{ matrix.gate.name }} - run: ${{ matrix.gate.cmd }} -``` - -For GitLab CI, mirror the matrix as `parallel:matrix:`. For BTP CI/CD, define one task per gate referencing the same scripts. - -## Step 1: Describe mode (default) - -If `mode` is empty or `describe`: - -1. Read `package.json`, `db/data/`, `db/schema.cds`, `docs/adr/` to detect which patterns apply. -2. For each applicable pattern, print: - - Pattern name and intent - - Sources detected in the project - - Suggested script filename - - Skeleton inputs (e.g., `KEY_COL=2` if a non-default CSV layout) -3. Recommend an order: csv-lint first (fastest), bidirectional + raise-coverage second (catch most regressions), availability-drift third (slowest, needs upstream clone), convention-drift last (cheapest but project-specific). - -Do **not** write files in describe mode. - -## Step 2: Apply mode - -If `mode = apply`: - -1. Discover the inputs: - - Settings CSV: scan `db/data/` for a file whose entity in `db/schema.cds` has a `Key` or `Code` column and a `Value` column → propose as Pattern 1 source. - - Catalog CSV: scan for an entity with `Code` + `IsActive` columns → Pattern 2 source. - - S/4 catalog: scan `srv/integration/` or similar for a file exporting `services[]` with `availability` → Pattern 3 source. - - Plugin matrix ADR: scan `docs/adr/` for files referencing inline-code packages → Pattern 4 source. -2. For each pattern that has inputs: - - Write the script to `scripts/ci/`. - - Make it executable (`chmod +x`). -3. Generate or update `.github/workflows/ci-gates.yml`, preserving any existing jobs. -4. Print a summary: scripts generated, sources used, exit-code semantics, recommended allowlist files to seed. -5. Do **not** commit or push. The user reviews the generated files and decides. - -## BTP vs On-Premise Differences - -| Aspect | BTP | On-Premise | -| --- | --- | --- | -| CI runner | GitHub Actions / BTP CI/CD | Jenkins / GitLab self-hosted / Azure DevOps | -| Node availability in runner | Standard | May require image with Node pre-installed | -| Upstream API source for Pattern 3 | `SAP/abap-atc-cr-cv-s4hc` public repo | Often the same; on-prem may also use ATC results from internal SAP system | -| Workflow scheduling | Cron-friendly via `on: schedule:` | Cron jobs in scheduler | - -The gate scripts are identical across providers; only the YAML wrapper differs. - -## Error Handling - -| Symptom | Cause | Action | -| --- | --- | --- | -| Pattern 1 has no inputs | Project doesn't use a settings table | Skip pattern, mark not-applicable | -| Pattern 3 cannot reach upstream | Network restricted on runner | Cache upstream snapshot weekly via cron, point gate at the cache | -| Regex over-matches | Reader pattern misidentified | Tune the grep regex; add unit test for the gate itself | -| Allowlist not used | Legitimate orphans flagged | Seed allowlist, document the reason in `scripts/ci/<gate>-allowlist.txt` | -| Generated YAML conflicts with existing | Workflow file already present | Merge manually, do not overwrite without `--force` | - -## What This Skill Does NOT Do - -- Does **not** run the gates against the codebase (use the gate scripts directly or run the [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md)). -- Does **not** invent new patterns; the five are the canonical set for CAP projects. -- Does **not** commit or push generated files. -- Does **not** edit production code. -- Does **not** maintain the allowlists; that's a project responsibility. - -## When to Use This Skill - -- When setting up CI for a new CAP project. -- When a regression of a structural invariant was caught manually and you want to automate prevention. -- When porting CI from another stack (Jenkins → GitHub Actions) and want the equivalent gates. -- As a follow-up to one of the audit skills, to lock in the audit's findings as enforced gates. - -## When NOT to Use - -- For functional tests / integration tests (different concern; use the test framework). -- For lint / format gates (use existing tools — ESLint, Prettier, UI5 linter). -- For security scans (use the security ecosystem — CodeQL, Snyk, etc., or the [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md)). -- For performance benchmarks (different concern). - -## Follow-up - -- Pair each generated gate with the audit skill that discovers the underlying issues: - - Pattern 1 ↔ [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) - - Pattern 3 ↔ [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) - - Patterns 2 / 4 / 5 ↔ project-specific audits surfaced by [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) -- When a gate becomes noisy, examine the allowlist before relaxing the gate itself — noisy gates often mean the project's reader pattern has drifted, not that the gate is wrong. -- Schedule Pattern 3 (availability-drift) on a weekly cron, not per-PR — the upstream clone is expensive. - -## Battle-Tested Patterns Referenced - -This skill encodes patterns from [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) as enforced CI gates. The patterns each gate locks in: - -- **5.5 Bidirectional CSV ↔ code consistency** ↔ Gate Pattern 1. -- **6.4 / 6.5 Exception auto-dispatch ordering / Orphan workflow cleanup** ↔ Gate Pattern 2 (catalog raise-coverage prevents dead catalog entries that would break auto-dispatch). -- **3.1 / 3.10 `cds run` does NOT mount UI5 apps** + Clean Core Level A compliance ↔ Gate Pattern 3 (availability drift catches consumed Tier-2 services that lose released state). -- **2.8 `cap-js` plugin matrix discipline** ↔ Gate Pattern 4 (convention/matrix drift between `package.json` and ADR doc). -- **5.4 Master-data references must be value-list-bound** ↔ Gate Pattern 5 partial (CSV schema lint with FK referential integrity). - -A noisy gate (false positives) usually signals a **drift in the project's reader pattern**, not a gate bug. Tune the regex in Pattern 1's skeleton (line 16 of `check-settings-bidirectional.sh`) before relaxing the gate. - -## Recommended Companion Plugins - -| Plugin / Skill | Why for CI gate generation | -|---|---| -| `sap-cap-capire` | Pattern 5 (CSV schema lint) uses `cds.load` to introspect the model; capire docs cover edge cases | -| `sap-docs` | Pattern 3 (availability drift) cross-references `SAP/abap-atc-cr-cv-s4hc` and Help Portal Communication Scenario docs | -| `context7` | GitHub Actions workflow syntax, bash safe-defaults, ICU MessageFormat (when CSV-derived strings carry placeholders) | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. - -## References - -- [SAP CAP — Deployment Guide](https://cap.cloud.sap/docs/guides/deployment/) -- [SAP API Release State — `SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc) -- [GitHub Actions — Workflow Syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) -- [Bash — `set -euo pipefail` Safe Defaults](https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425) -- [SAP CAP — Reference Tests and Custom Tests](https://cap.cloud.sap/docs/node.js/cds-test) diff --git a/skills/sap-cap-clean-core-enforce/SKILL.md b/skills/sap-cap-clean-core-enforce/SKILL.md deleted file mode 100644 index 58b60e8f..00000000 --- a/skills/sap-cap-clean-core-enforce/SKILL.md +++ /dev/null @@ -1,462 +0,0 @@ ---- -name: sap-cap-clean-core-enforce -description: Discovery-driven Clean Core Level A enforcement gate for SAP CAP + S/4HANA projects. Scans `cds.connect.to()` runtime calls + `@cds.external` services, identifies SAP-released probe objects, builds an availability matrix (Public Cloud × Private Cloud × On-Premise) by consulting two authoritative sources (the SAP API release-state repository `SAP/abap-atc-cr-cv-s4hc` + the SAP API Hub at api.sap.com), detects catalog drift versus the project's declared compatibility policy, and supports an `--enforce` mode that fails CI on HIGH findings to block deployment of non-released consumption. Use when asked to "verify Clean Core Level A compliance", "audit S/4 API usage", "check which S/4 services we consume are released", "enforce Clean Core in CI", "build a Clean Core compliance matrix", or "block deployment if non-released API is consumed". ---- - -# SAP CAP Clean Core Level A Enforcement - -Discovery-driven Clean Core Level A enforcement for SAP CAP + S/4HANA projects. This skill **scans your CAP codebase for S/4 API consumption**, **verifies each service against two authoritative sources** — the SAP API release-state repository ([`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md)) and the [SAP API Hub](https://api.sap.com/) — on all three editions (Public Cloud, Private Cloud / RISE, On-Premise), and produces a **compliance matrix** that can be checked into the repo as compliance evidence. With `--enforce`, the matrix becomes a **CI-blocking deployment gate**. - -Unlike [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) which classifies **ABAP custom code on the SAP side** into Levels A-D, this skill classifies **the BTP CAP application's outbound S/4 API consumption** — useful when the CAP app is the consumer and the question is "are all S/4 APIs we call actually released, on all editions we deploy to?". - -Default mode is **`report`** (read-only audit, idempotent, ~5 minute run). Opt into **`--apply`** for safe additive catalog corrections, or **`--enforce`** for CI-gating semantics (non-zero exit on HIGH findings). - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Target edition | All 3: `public_cloud`, `private_cloud`, `on_premise` | Full matrix — multi-customer-deployable projects need to know per-edition availability | -| Clean Core level | `A` (Released APIs only) | The most restrictive level — works for any cloud target | -| Compatibility policy file | Auto-detect: `srv/integration/s4*Policy*.{ts,js,cds}` → `srv/config/s4*.{ts,js}` → `src/config/clean-core*.{ts,js}` | Project-specific files vary; auto-detect first, fall back to discovery-only | -| Probe identification | Catalog `probeObject` field if present → CDS view naming heuristic (`I_*API01` / `I_*`) → MCP search fallback | Multi-strategy gives best coverage | -| BTP managed services | Excluded from S/4 audit | `db`, `auth`, `messaging`, `connectivity`, `responsibility-management`, `enterprise-messaging`, `audit-log-service`, `nats`, `redis` are not S/4 | -| Report output | `docs/audit/<yyyy-mm-dd>-clean-core-level-a.md` | Markdown + committable for traceability | -| Mode | `report` (read-only) | Reversible by default; user opts into `apply` mode for catalog updates | -| Drift severity | Over-declared = HIGH, Under-declared = MEDIUM, Missing-field = LOW | HIGH if catalog claims availability that MCP denies (customer would fail in prod) | - -## Input - -Optional flags (all auto-detected by default): - -- **scope** — `all` (default) | `discovery-only` (skip MCP) | `matrix-only` (presume discovery cached) | `<SERVICE_NAME>` (focus single service) -- **policy file path** — explicit override for the compatibility policy file -- **target editions** — comma list to restrict, e.g., `public_cloud,private_cloud` -- **mode** — `report` (default) | `apply` (auto-update catalog `availability[]` field if MCP-verified differs from declared) - -If neither flag is provided, run with defaults and full discovery. - -## Step 1: Pre-flight + Compatibility Policy Discovery - -### 1a. Verify project type - -```bash -test -f package.json && grep -q '"@sap/cds"' package.json -``` - -If not a CAP project, stop and inform the user this skill targets CAP+S/4HANA stacks. - -### 1b. Detect compatibility policy file - -Search for the project's S/4 compatibility policy file (commonly used pattern: a JS/TS module exporting a `SERVICE_CATALOG` constant that maps logical service names to OData version + destination aliases + entity sets + Communication Scenarios). - -Auto-detect order: - -```bash -find srv/integration -name "s4*Policy*.{ts,js}" -maxdepth 2 2>/dev/null -find srv/config -name "*compat*.{ts,js}" -maxdepth 2 2>/dev/null -find src/config -name "clean-core*.{ts,js}" -maxdepth 2 2>/dev/null -``` - -If multiple candidates → ask user to pick one. If none → proceed with `discovery-only` scope (no catalog drift detection, only inventory + MCP verification). - -### 1c. Parse compatibility policy - -Extract from the policy file (regardless of exact location): - -- Service entries (keys of `SERVICE_CATALOG` or equivalent): logical names like `BUSINESS_PARTNER`, `SUPPLIER_INVOICE`, `PURCHASE_ORDER`, etc. -- Per-entry: `odata` (versions array), `destinations` (alias names), `entitySets`, `communicationScenarios`, optionally `availability` (already-declared edition list), optionally `probeObject` (already-declared SAP probe object). - -If the policy file does not exist or doesn't follow this convention, proceed with code-grep discovery only. - -## Step 2: Dynamic Discovery (4 sources) - -Combine 4 input sources to build the candidate service list. **Do not** start from a hardcoded list — discover dynamically. - -### 2a. Runtime destinations (`cds.connect.to`) - -```bash -grep -rhE "cds\.connect\.to\(['\"]([A-Z][A-Z0-9_-]+)['\"]" srv/ \ - --include="*.ts" --include="*.js" 2>/dev/null \ - | grep -oE "['\"][A-Z][A-Z0-9_-]+['\"]" \ - | tr -d "'\"" \ - | sort -u -``` - -### 2b. External service contracts - -```bash -ls srv/external/*.cds 2>/dev/null | xargs -I {} basename {} .cds | sort -u -``` - -These are typically `@cds.external` services or remote OData proxies with metadata committed to the project. - -### 2c. SERVICE_CATALOG declared destinations (if Step 1c succeeded) - -Extract from the parsed policy file. - -### 2d. ServiceConnectors seed CSV (if present) - -```bash -test -f db/data/sap.*-ServiceConnectors.csv && \ - awk -F',' 'NR>1 {print $2}' db/data/sap.*-ServiceConnectors.csv | sort -u -``` - -This is a common pattern for CAP apps that declare admin-configurable S/4 service connectors as master data. - -### 2e. Apply exclusions - -Remove BTP managed services (not S/4): - -``` -db | auth | messaging | connectivity | responsibility-management | -enterprise-messaging | audit-log-service | nats | redis -``` - -Also remove dev / mock / test destinations (matching `mock-*`, `test-*`, `dev-*`). - -The result is the **service candidate list** to audit. - -## Step 3: Probe Identification - -For each service candidate, identify the representative SAP object to query against the SAP API Release State repository. - -### Heuristic order (most preferred first) - -1. **Explicit `probeObject`** from policy file (if Step 1c found it) — zero cost, already documented. -2. **Naming convention** — SAP's pattern for OData service-backing CDS views: - - Destination `API_*` (e.g., `API_PURCHASEORDER_PROCESS_SRV`) → probe `I_*API01` (e.g., `I_PURCHASEORDERAPI01`) - - Destination `I_*` directly (e.g., `I_PURCHASEORDERHISTORYAPI01`) → probe is the destination itself -3. **Special cases** (known SAP framework patterns): - - Electronic Document Files / DRC services → probe `DTEL EDOC_TYPE` (component `CA-GTF-CSC-EDO`) - - Attachment service (`API_CV_ATTACHMENT_SRV`) → probe `CLAS CL_ATTACHMENT_SERVICE_API` (component `CA-DMS`) - - Workflow services (`s4-flexible-workflow`) → probe `DDLS I_WORKFLOWEXTERNALSTATUS` (component `BC-BMT-WFM`) -4. **MCP search fallback** for ambiguous cases: - ``` - mcp-sap-docs:sap_search_objects( - query="<keyword derived from logical name>", - system_type="public_cloud", - clean_core_level="A", - object_type="DDLS", - limit=10 - ) - ``` - Take the first result matching `I_<TOPIC>API01` or `I_<TOPIC>` pattern. - -### Untraceable services - -If no probe object can be identified for a service (none of the heuristics produce a match), emit a **LOW finding** in the report: - -``` -[LOW] Untraceable service: <SERVICE_NAME> - - Destination(s): <list> - - Inferred attempts: - - Naming heuristic I_*API01: no MCP match - - Search fallback: no result matching pattern - - Recommendation: manually add `probeObject` field to the catalog entry -``` - -The skill continues without crashing — untraceable services are flagged but not treated as compliance gaps (they may be released, just not auto-detectable). - -## Step 4: Matrix MCP-Verified (3 editions × N services) - -The matrix verification consults **three authoritative sources** in priority order. Each cell of the matrix `(service × edition)` is filled from the first source that returns a definitive answer: - -1. **`SAP/abap-atc-cr-cv-s4hc`** — the [ABAP API Release State repository](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md). JSON files per object class; updated by SAP product teams. **Authoritative for**: whether an ABAP object (CDS view, BAPI, RFC FM, table) is released-for-cloud-development and at what Clean Core Level. Queried via `mcp-sap-docs:sap_get_object_details` which wraps the repo. -2. **`api.sap.com`** — the [SAP API Hub](https://api.sap.com/). **Authoritative for**: OData service lifecycle (released / sandbox / deprecated), Communication Scenario membership, version history, deprecation timeline. Queried via `mcp-sap-docs:sap_search_objects` and `WebFetch` against the API Hub HTML pages, OR scraped offline via Apify Website Content Crawler into a refreshable cache. -3. **Project's own compatibility catalog** (e.g. `srv/integration/s4CompatibilityPolicy.js`). **Authoritative only for**: the project's *declared* availability claim — which the audit verifies against sources 1 + 2. - -For each `(service, probe_object)` identified, issue **3 parallel MCP queries** — one per edition: - -``` -mcp-sap-docs:sap_get_object_details( - object_type=<probe.type>, - object_name=<probe.name>, - system_type="public_cloud", # then private_cloud, then on_premise - target_clean_core_level="A" -) -``` - -Cross-check the abap-atc result against the API Hub for the matching Communication Scenario. When the two sources disagree, the API Hub wins for OData service questions; the abap-atc repo wins for raw ABAP object questions. Record the resolution rationale in the report. - -### Per-cell evaluation - -For each `(service, edition)` cell of the matrix: - -| MCP response | Interpretation | Matrix value | -|---|---|---| -| `found: true && state: "released" && cleanCoreLevel: "A" && complianceStatus: "compliant"` | Released L-A available | ✅ | -| `found: true && state: "released" && cleanCoreLevel: "B/C/D"` | Released but not L-A | 🟡 (level mismatch) | -| `found: true && state: "deprecated"` | Available but deprecated | ⚠️ (deprecated, migration needed) | -| `found: true && state: "notToBeReleased*"` | Not released, internal use | 🔴 (Clean Core violation) | -| `found: false` | Not in repository | ⚪ (unknown — probe may be wrong) | - -### Cache MCP results - -Cache by `(object_type, object_name, system_type)` — the same probe is often queried for multiple services that share a CDS view backbone (e.g., `I_BUSINESSPARTNER` underlies multiple API_* services). 30+ queries for a 12-service catalog is normal. - -## Step 5: Catalog Drift Detection - -For each catalog entry, compare declared `availability[]` against MCP-verified result. - -### Drift classification - -| Drift type | Declared vs Verified | Severity | Impact | -|---|---|---|---| -| **NONE** | Declared matches verified exactly | — | Healthy | -| **OVER_DECLARED** | Catalog says `['PUBLIC', 'PRIVATE', 'ONPREM']` but MCP confirms only 2 editions | 🔴 **HIGH** | Customer on Public Cloud configures connector → runtime fails with 404. False positive. | -| **UNDER_DECLARED** | Catalog says `['PRIVATE']` but MCP confirms 3 editions | 🟡 **MEDIUM** | Customer Public Cloud blocked unnecessarily. False negative. | -| **MISSING_FIELD** | Catalog has no `availability` field | 🟢 **LOW** | Undefined runtime behavior. Defaults may be too permissive. | -| **STATE_CHANGE** | Catalog implies released but MCP reports deprecated | ⚠️ **MEDIUM** | Migration needed (probably future cutoff). | - -## Step 6: Compliance Matrix Rollup - -Aggregate the per-cell matrix into multi-dimensional reports. - -### Rollup A: Per-service matrix - -``` -| Service | Public | Private | OnPrem | Probe | App Component | Drift | -|------------------|--------|---------|--------|-----------------------------|---------------------|----------------| -| BUSINESS_PARTNER | ✅ | ✅ | ✅ | DDLS I_BUSINESSPARTNER | AP-MD-BP | NONE | -| PURCHASE_ORDER | ✅ | ✅ | ✅ | DDLS I_PURCHASEORDERAPI01 | MM-PUR-PO-2CL | NONE | -| HYPOTHETICAL_X | ❌ | ✅ | ✅ | DDLS I_HYPOTHETICALX | (not found public) | OVER_DECLARED | -``` - -### Rollup B: Per Communication Scenario - -For each `SAP_COM_XXXX` in the catalog, list which services use it and their edition coverage: - -``` -| SAP_COM | Description | Services NOVA using | Edition coverage | -|--------------|-----------------------------------|---------------------------|----------------------| -| SAP_COM_0008 | Business Partner Integration | BUSINESS_PARTNER | All 3 ✅ | -| SAP_COM_0057 | Supplier Invoice + Attachments | SUPPLIER_INVOICE, ATTACHMENT | All 3 ✅ | -``` - -### Rollup C: Coverage summary - -``` -Total services audited: N -Cells (services × 3 editions): M -Cells compliant (L-A all 3): X (X/M %) -Cells with drift HIGH: n -Cells with drift MEDIUM: n -Cells with drift LOW: n -Untraceable services: n -Z-namespace runtime references: n - -Rating: A (≥95% compliant) / A- (90-94%) / B (75-89%) / C (<75%) -``` - -## Step 7: Emit Report - -Save markdown to `docs/audit/<yyyy-mm-dd>-clean-core-level-a.md` (create `docs/audit/` directory if needed): - -```markdown -# Clean Core Level A Compliance Audit — <yyyy-mm-dd> - -## Pre-flight -- Branch: <branch> -- Commit: <sha> -- Compatibility policy file: <path or 'not detected'> -- Scope: <discovery sources used> - -## Discovery -- Runtime destinations (cds.connect.to): N -- External service contracts: N -- SERVICE_CATALOG entries: N -- ServiceConnectors CSV: N -- Effective services (post-dedup + post-exclusion): N - -## Probe Identification -| Service | Probe | Type | App Component | Source | - -## Matrix (MCP-verified) -| Service | Public | Private | OnPrem | Notes | - -## Drift Detection -| Service | Declared | Verified | Drift | Severity | - -## Compliance per Communication Scenario -| SAP_COM | Description | Services | All editions | - -## Findings (≥0.8 confidence) -### [HIGH] OVER_DECLARED: <service> -- Catalog: <declared> -- MCP verified: <actual> -- Impact: <concrete failure scenario> -- Fix: <recommendation> - -## Compliance Summary -- Services: N · Cells compliant: X/M -- Rating: A / A- / B / C - -## Fix Plan / Applied -| Service | Field | Before | After | Status | - -## Re-verification process -- Cadence: quarterly (Q-review) or on SAP API sunset announcement -- Command: `sap-cap-clean-core-enforce` (this skill) -- Output: re-save report with current date -``` - -## Step 8: Apply Fixes (only when `--apply` mode) - -When user explicitly opts in: - -### Safe auto-fixes - -1. **Sync `availability[]` field** for services where MCP-verified is a **superset** of declared (UNDER_DECLARED case) — adding editions never breaks customers; only removing does. -2. **Add `probeObject` field** when missing but heuristic-identified — improves traceability for re-verification. -3. **Add timestamp comment** at the top of the catalog file recording verification date. - -### NOT auto-applied (require manual review) - -- **Remove edition from `availability[]`** when OVER_DECLARED — risks breaking customers on the over-declared edition. Surface as a finding with recommendation, but never auto-remove. -- **Add new service entry** not currently in catalog. Use `--add-service <name>` explicit flag. -- **Z-namespace usage** at runtime — architectural decision, not auto-fixable. - -### Verification after fix - -Re-run Steps 4-6 against the updated catalog to confirm zero drift remains. - -## Step 9: Enforcement mode (`--enforce` flag) - -The skill defaults to `report` mode. With `--enforce`, it acts as a **CI-blocking deployment gate**: it exits with a non-zero status code on any HIGH finding, and the calling workflow refuses to deploy. - -### Contract - -| Mode | Exit on HIGH | Exit on MEDIUM | Exit on LOW | Use case | -|---|---|---|---|---| -| `report` (default) | 0 | 0 | 0 | Audit run; review the markdown report | -| `--apply` | 0 | 0 | 0 | Audit run + apply safe additive fixes | -| `--enforce` | **1** | 0 (warning) | 0 (info) | CI gate — pre-deployment block | -| `--enforce --strict` | **1** | **1** | 0 | Tightest gate — block on any finding above LOW | - -HIGH severity criteria (the gate-blocking class): -- A consumed S/4 service is **NOT released** for the deployment target's edition (per source 1 + 2 of Step 4). -- A consumed service is **deprecated** with a known sunset date inside the customer's release window. -- The catalog declares availability that MCP-verification refutes (**OVER_DECLARED** drift). -- Direct DB-table or RFC/BAPI consumption detected against a non-released artifact. - -### CI integration patterns - -Pair this skill with `sap-cap-ci-gates-pattern` Pattern 3 (API-availability drift) for the GitHub Actions / GitLab CI / Jenkins YAML wiring. The gate runs **per-PR** (fast feedback) AND **per-deploy** (final safety net before `cf deploy` / `kubectl apply`). - -GitHub Actions example: -```yaml -- name: Clean Core Level A enforcement - run: | - npx skills run sap-cap-clean-core-enforce -- \ - --enforce \ - --target ${{ inputs.deployment_target }} \ - --abap-atc-source ./.cache/abap-atc-cr-cv-s4hc \ - --report-path docs/audit/${{ github.run_id }}-clean-core.md - # CI fails here if any HIGH finding remains -``` - -### Sources refresh discipline - -The two upstream sources (`abap-atc-cr-cv-s4hc` repo + `api.sap.com`) are **mutable**. They evolve as SAP releases new objects, deprecates old ones, or revises Communication Scenarios. - -| Source | Refresh cadence | How | -|---|---|---| -| `abap-atc-cr-cv-s4hc` | weekly | Cron job: `git -C .cache/abap-atc-cr-cv-s4hc pull --ff-only` | -| `api.sap.com` snapshots | weekly (or per-PR if cheap) | Apify Website Content Crawler against the API Hub package pages — store output as JSON cache. Re-run when "stale" (>7 days) | - -A weekly cron in CI surfaces drift caused by SAP-side changes, not by the project's own commits. File these as "upstream drift" findings with a 90-day window to remediate (per SAP's typical deprecation timeline). - -### Side-by-side extension fallback - -When `--enforce` blocks the build on a non-released service that the project genuinely needs: - -1. Surface the finding as a **side-by-side extension candidate** — the consumption should move out of the CAP service into a separate extension service on BTP (or out of ABAP into BTP entirely). -2. Cross-link to the broader Clean Core return path documented in [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#311--clean-core-level-a-as-deployment-gate-not-just-an-audit`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). -3. Optionally invoke a custom-code refactor skill (e.g. a future `sap-erp-clean-core-refactor`) to plan the migration. - -The gate is **not** about preventing all custom logic — it's about preventing **non-released custom logic from coupling to the deployment pipeline**. Custom logic that legitimately needs to live on BTP belongs in a side-by-side extension, not in the CAP service itself. - -## BTP vs On-Premise Differences - -| Aspect | BTP target (Public Cloud) | Private Cloud / RISE | On-Premise | -|---|---|---|---| -| Software component | `SAPSCORE` | `S4CORE` | `S4CORE` or `SAP_BASIS` | -| App component suffix | `-2CL` ("To Cloud") | base name (no suffix) | base name (no suffix) | -| Released API set | Most restrictive | Includes legacy classic APIs | Same as Private | -| ABAP CDS views | `I_*` views released L-A | `I_*` views (no -2CL suffix) | Same as Private | -| Cross-edition probes | Use Public Cloud `-2CL` as authoritative | Both base + -2CL may exist | base name preferred | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| `mcp-sap-docs not connected` | MCP server unavailable | Run installation: `npx skills add marianfoo/arc-1` includes recommended MCP setup | -| `mcp-sap-docs returns found: false` for known SAP object | Repository dataset lag or wrong probe object | Try alternative probe (variant CDS view name); flag service as untraceable | -| Policy file detected but parse fails | Non-standard policy file format | Skill falls back to `discovery-only` scope and logs warning | -| Multiple policy files candidates found | Project structure ambiguity | Ask user to pick one (interactive prompt) | -| Probe object naming heuristic miss | Custom destination name doesn't match `API_*` / `I_*` pattern | Manual probe specification via policy file `probeObject` field | -| No `cds.connect.to` calls found | Project uses different connection pattern (e.g., `cds.requires.<dest>`) | Extend Step 2a grep with project-specific pattern; report what was found | -| Catalog file in TypeScript with complex syntax | Cannot statically parse with regex | Read entries via `node -e` evaluation or ask user to export JSON snapshot | - -## What This Skill Does NOT Do - -- **No SAP write operations** — read-only on SAP side via mcp-sap-docs queries -- **No replacement code generation** — suggests replacement APIs (in drift report), but does not generate the replacement CAP service binding -- **No ABAP-side classification** — that's [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) (which audits Z code on SAP) -- **No SystemParameter / customizing validation** — separate concern (see [sap-cap-customizing-honor](../sap-cap-customizing-honor/SKILL.md)) -- **No deployment / runtime testing** — static audit only; doesn't make HTTP calls to the destination -- **No CDS compile validation** — assumes the project already compiles; this skill audits the S/4 API surface, not CDS syntax -- **No Z-namespace runtime replacement** — flags Z-namespace usage as finding but architectural redesign is out of scope -- **No catalog file generation** — works against an existing compatibility policy or with `discovery-only` if none exists - -## When to Use This Skill - -- **Pre-deployment audit** — before committing to a BTP customer go-live, verify that all S/4 APIs consumed are released for that customer's edition -- **Quarterly compliance check** — schedule as part of release governance; re-verifies the catalog matches latest SAP API release state -- **After adding a new S/4 destination** — verify the new service is released before merge -- **Pre-CI gate validation** — pair with a CI script (e.g., `scripts/ci/check-s4-compat-coverage.sh`) for runtime drift prevention -- **Pre-acquisition audit** — when assessing a 3rd-party CAP application, verify it's Clean Core L-A before adoption -- **Documentation evidence** — auditor-friendly report committed to `docs/audit/` for compliance trail - -## When NOT to Use This Skill - -- **Greenfield project with no SERVICE_CATALOG yet** — there's nothing to audit yet; start with [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) to scaffold the project, then audit later -- **ABAP-only codebase** (no CAP) — use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) instead -- **Custom Z* CDS views on the S/4 side that are NOT meant to be released** — that's a design choice; this skill measures consumption, not source generation -- **Single-edition deployment fixed by customer contract** — you can use this skill but most of the value (cross-edition matrix) is lost; consider `--target-editions public_cloud` flag - -## Follow-up - -After this skill produces the compliance matrix: - -- **HIGH drift findings**: investigate manually, decide whether to remove edition from declared availability OR switch to a different SAP service that IS released on the affected edition -- **UNDER_DECLARED findings**: run with `--apply` to expand `availability[]` and unlock customer editions -- **Untraceable services**: manually add `probeObject` field to policy entries -- **Deprecated state warnings**: plan migration to successor APIs (use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) for ABAP-side migrations) -- **Z-namespace runtime references**: architectural review — either Clean Core B opt-in (documented exception) or replacement - -Related skills: - -- [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) — ABAP-side classification (companion, source side) -- [migrate-custom-code](../migrate-custom-code/SKILL.md) — ABAP-side ATC fixes when replacements are needed -- [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) — generate the CAP scaffold this skill later audits - -## References - -**Primary authoritative sources** (Step 4 verification consults both): - -- [SAP API Release State repository (`SAP/abap-atc-cr-cv-s4hc`)](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — JSON-encoded ABAP object classifications by edition; pin / submodule into the project and refresh weekly. -- [SAP API Hub (`api.sap.com`)](https://api.sap.com/) — OData service lifecycle, Communication Scenario membership, deprecation timeline. Scrape via Apify Website Content Crawler or query via `mcp-sap-docs:sap_search_objects` for offline matrix construction. - -**Methodology references**: - -- [Clean Core principles (BTP docs)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) -- [CAP `cds.connect.to` documentation](https://cap.cloud.sap/docs/node.js/cds-connect) -- [SAP Communication Management (S/4HANA Cloud)](https://help.sap.com/docs/SAP_S4HANA_CLOUD/0f69f8fb28ac4bf48d2b57b9637e81fa/community-management.html) -- [SAP Clean Core extensibility guide (developers.sap.com)](https://developers.sap.com/topics/clean-core.html) -- [SAP Custom Code Migration Guide for S/4HANA](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) - -**Related skills (companion gates)**: - -- [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 3 — wire the `--enforce` mode into a per-PR + per-deploy CI gate. -- [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#311--clean-core-level-a-as-deployment-gate-not-just-an-audit`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) — broader context on Clean Core as a deployment gate. diff --git a/skills/sap-cap-customizing-honor/SKILL.md b/skills/sap-cap-customizing-honor/SKILL.md deleted file mode 100644 index 6380c6bb..00000000 --- a/skills/sap-cap-customizing-honor/SKILL.md +++ /dev/null @@ -1,494 +0,0 @@ ---- -name: sap-cap-customizing-honor -description: Bidirectional CSV ↔ code customizing audit for SAP CAP applications that use a SystemParameter / settings-table pattern. Detects forward orphans (CSV seeded but never consumed in code), inverse orphans (code reads parameter but no CSV seed), hardcoded business decisions that bypass the customizing mechanism (thresholds, timeouts, retry policies, severity mappings), and master-data foreign-key fields missing `@Common.ValueList` annotation. Use when asked to "audit customizing coverage", "verify SystemParameter consumption is 100%", "find hardcoded business decisions", "check that customizing is honored end-to-end", or "is admin config actually being read by the code?". ---- - -# SAP CAP Customizing Honor Audit - -Exhaustive audit that **enforces customizing is honored at 100%** in a SAP CAP application — both forward (every SystemParameter seed in CSV has a code consumer) and inverse (every `params.X` read in code has a CSV seed). Additionally, sweeps for **hardcoded business decisions** that bypass the customizing mechanism, and verifies that every **foreign-key field to master data** is bound to `@Common.ValueList` on filter bars and edit forms — no free-text where customizing exists. - -Read-only audit, idempotent, ~3 minute run, zero side-effects (unless `--apply` mode auto-fixes safe cases). - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Scope | `all` — entire `srv/` + `app/` codebase | Default to full sweep; user narrows if needed | -| SystemParameter source | `db/data/*-SystemParameters.csv` (CAP convention) | CAP CSV seed pattern is conventional | -| Code consumer pattern | `getSystemParamReader().get(...)` + `params.X` / `cfg.X` / `paramReader.X` | Common CAP customizing reader patterns | -| Inverse orphan severity | ERROR (admin sees param in Setup UI but code ignores it) | Customer-facing problem | -| Forward orphan severity | WARNING | Internal cleanliness, less urgent | -| Hardcoded business decision severity | HIGH for thresholds in lifecycle handlers, MEDIUM otherwise | Production impact-aware | -| Master-data unreferenced fields severity | P1 (filter bar) / P2 (edit form) / P3 (display only) | UX-first priority | -| Report output | `docs/audit/<yyyy-mm-dd>-customizing-honor.md` | Committed for traceability | -| Mode | `report` (read-only) | Safe by default | -| Auto-fix scope | Add missing seed CSV rows for inverse orphans + add `@Common.ValueList` annotations for master-data filter bars | Reversible, contained | - -## Input - -Optional flags: - -- **scope** — `all` (default) | `<app-name>` (focus single Fiori app) | `srv-only` | `app-only` | `<subfolder>` (e.g., `srv/handlers`) -- **mode** — `report` (default) | `fix` (auto-apply safe fixes on a dedicated branch) -- **csv pattern** — override default `db/data/*-SystemParameters.csv` glob -- **reader pattern** — override regex for code-side consumer detection -- **skip checks** — comma list of check categories to skip (e.g., `--skip hardcoded,master-data`) - -## Step 1: Pre-flight + Project Detection - -### 1a. Verify project type - -```bash -test -f package.json && grep -q '"@sap/cds"' package.json && test -d srv -``` - -If not a CAP project, stop and inform user this skill targets CAP apps. - -### 1b. Detect SystemParameter CSV files - -```bash -find db/data -name "*-SystemParameter*.csv" -maxdepth 2 2>/dev/null -``` - -Expected pattern: `db/data/<namespace>-SystemParameters.csv` with columns `ParamKey,CompanyCode,ParamValue,Description,Category,IsSecret`. - -If multiple files found → process all; if none → warn user and proceed with inverse-orphan check only. - -### 1c. Detect code-side consumer pattern - -```bash -grep -rohE "(getSystemParamReader|paramReader|params\.|cfg\.)[A-Z][A-Z0-9_]+\(" srv/ \ - --include="*.ts" --include="*.js" 2>/dev/null | head -10 -``` - -If matches are found, the reader pattern is confirmed. Otherwise the project may not use the customizing pattern at all; ask user to confirm. - -### 1d. Resolve scope - -| scope value | Folder paths to audit | -|---|---| -| `all` | `srv/` + `app/` | -| `<app-name>` | `app/<app-name>/` only | -| `srv-only` | `srv/` only | -| `app-only` | `app/` only | -| `<subfolder>` | exact path | - -### 1e. Branch creation (if mode=fix) - -```bash -git checkout -b codex/customizing-honor-<scope>-$(date +%Y-%m-%d) -``` - -## Step 2: Bidirectional CSV ↔ Code Check (Forward + Inverse Orphans) - -### 2a. Extract CSV-seeded parameter keys - -```bash -awk -F',' 'NR>1 {print $1}' db/data/*-SystemParameters.csv 2>/dev/null \ - | sort -u > /tmp/csv-keys.txt -``` - -Result: deduplicated `ParamKey` list (header `ParamKey,...`). - -### 2b. Extract code-consumed parameter keys - -```bash -grep -rohE "(p|params|cfg|wfParams|matchingParams|techParams|aiParams|monitoringParams|envCfg|dbCfg|slaParams|apprParams)(\?\.\|\[['\"]|\.)([A-Z][A-Z0-9_]+)" "$SCOPE" \ - --include="*.ts" --include="*.js" 2>/dev/null \ - | grep -oE "[A-Z][A-Z0-9_]+\b" \ - | sort -u > /tmp/code-keys.txt -``` - -This greps common reader variable names (`params.X`, `cfg.X`, `wfParams.X`, etc.) and extracts the parameter key. - -### 2c. Compute orphans - -```bash -# Inverse orphan: code reads but CSV doesn't seed -comm -23 /tmp/code-keys.txt /tmp/csv-keys.txt > /tmp/inverse-orphans.txt - -# Forward orphan: CSV seeds but code never reads -comm -13 /tmp/code-keys.txt /tmp/csv-keys.txt > /tmp/forward-orphans.txt -``` - -### 2d. Classify orphans - -For each inverse orphan, find the source file + line: - -```bash -while IFS= read -r key; do - grep -rn "\.${key}\b\|\['${key}'\]\|\[\"${key}\"\]" srv/ app/ \ - --include="*.ts" --include="*.js" 2>/dev/null | head -3 -done < /tmp/inverse-orphans.txt -``` - -For each forward orphan, find the CSV line: - -```bash -while IFS= read -r key; do - grep -n "^${key}," db/data/*-SystemParameters.csv 2>/dev/null -done < /tmp/forward-orphans.txt -``` - -### 2e. Compile per-category counts - -Group orphans by `Category` (from CSV column 5): - -``` -SystemParameter category Forward Inverse -INTEGRATION 3 7 -TECHNICAL 1 12 -APPROVAL 0 4 -... -``` - -## Step 3: Hardcoded Business Decisions Sweep - -For each file in scope (`*.ts`, `*.js`), search for patterns that represent **business decisions** but are NOT routed through `getSystemParamReader()`. - -### 3a. Numeric thresholds - -```bash -grep -rnE ">=\s*[0-9]+|>\s*[0-9]+|<=\s*[0-9]+|<\s*[0-9]+|=\s*[0-9]{2,}\b" "$SCOPE" \ - --include="*.ts" --include="*.js" 2>/dev/null \ - | grep -vE "//|^.*\*|\.test\.|HTTP|status:|\.length|substring|setTimeout|process\.env|throw|>=\s*0\b|>\s*0\b|<=\s*100\b" \ - | head -50 -``` - -For each match, classify: - -- **TRUE positive (business threshold)** — e.g., `if (riskScore >= 80) markHighRisk()` — should be `params.HIGH_RISK_THRESHOLD` -- **FALSE positive** — HTTP status codes (`200`, `400`, `500`), array index, `.length`, `setTimeout` ms — ignore - -### 3b. Hardcoded fragment XML thresholds (Fiori app) - -```bash -grep -rnE ">=\s*[0-9]+|>\s*[0-9]+|<\s*[0-9]+" "$SCOPE" \ - --include="*.xml" 2>/dev/null \ - | grep -vE "//|^.*\*|ErrorCount|currentStep|>= ?\$\{|> ?\$\{|>= 0\b|> 0\b|>= 1\b" \ - | head -30 -``` - -### 3c. `cds.env` reads not dual-source - -```bash -grep -rn "cds\.env\." "$SCOPE" --include="*.ts" 2>/dev/null \ - | grep -vE "//|^.*\*|cds\.env\.(profile|profiles|requires|features|build|sql|hana|odata|telemetry|i18n|log|app|server|effective)" \ - | grep -v "_resolveArchiverConfig\|fallback\|legacy" \ - | head -20 -``` - -The **dual-source pattern** is: SystemParameter first, fallback to `cds.env`, fallback to `process.env`, fallback to hardcoded. Direct `cds.env.X` reads without going through SystemParamReader are findings (admin can't override). - -### 3d. UX timing / delay hardcoded - -```bash -grep -rnE "setTimeout.*[0-9]{3,}|DELAY|INTERVAL_MS|TTL\s*=\s*[0-9]" "$SCOPE" \ - --include="*.ts" 2>/dev/null \ - | grep -vE "//|^.*\*|\.test\." | head -10 -``` - -### 3e. i18n hardcoded inline (user-facing strings) - -```bash -grep -rnE "MessageBox\.|MessageToast\.show\(|req\.notify\(" "$SCOPE" \ - --include="*.ts" 2>/dev/null \ - | grep -vE "_t\(|i18n>|getText\(|bundle\.|//|^.*\*" \ - | head -10 -``` - -User-visible text should be i18n keys, not inline strings. Hardcoded inline strings are findings (no localization). - -### 3f. Per-finding triage - -For each match, decide: - -- **TRUE positive** → business decision tunable → add to findings list with file:line + suggested fix -- **FALSE positive** → security boundary / HTTP code / internal cache TTL / `.length` → ignore - -## Step 4: Catalog Raise Coverage (if applicable) - -For CAP apps with a `ProcessStepCheck` catalog (a common pattern for workflow-driven apps): - -```bash -test -f db/data/*-ProcessStepCheck.csv && \ - bash scripts/ci/check-catalog-raise-coverage.sh 2>/dev/null -``` - -This verifies every CSV-seeded check code has a runtime `raiseCatalogException(...)` call in the codebase. - -If the CI script is not present, run an equivalent check inline: - -```bash -# Extract active check codes -awk -F',' 'NR>1 && $X=="true" {print $Y}' db/data/*-ProcessStepCheck.csv | sort -u - -# Find raise sites -grep -rhE "raiseCatalogException\(.*['\"]([A-Z][A-Z0-9_]+)['\"]" srv/ \ - --include="*.ts" | grep -oE "['\"][A-Z][A-Z0-9_]+['\"]" | tr -d "'\"" | sort -u - -# Diff -comm -23 <(active_codes) <(raise_sites) -``` - -Active checks without a raise site = code-coverage gap. - -## Step 5: Adapter Factory Dual-Source Verification - -For CAP apps using adapter factory patterns (`srv/{notifications,messaging,monitoring,...}/`): - -```bash -find srv -path "*/Factory*.ts" -o -name "*AdapterFactory*.ts" 2>/dev/null -``` - -For each factory, verify the canonical pattern: - -```typescript -// Expected pattern (4-layer fallback): -const adapterName = - params['<KEY>_ADAPTER'] || // 1. SystemParameter (admin-configurable) - env.adapter || // 2. cds.env fallback - process.env.ADAPTER_OVERRIDE || // 3. env var fallback - 'default-adapter'; // 4. hardcoded last resort -``` - -If a factory reads only from `cds.env` or `process.env` without going through SystemParameter, that's a finding — "adapter not customizing-driven". - -## Step 6: Master-Data Reference Audit (Filter + Edit Form) - -For CAP+Fiori projects, verify that every field referencing master data has `@Common.ValueList` annotation. - -### 6a. Master data catalog (target entities) - -Common SAP master-data CodeList patterns in CAP: - -| Pattern | Master data target | -|---|---| -| `*CompanyCode*` | `CompanyCodeMappings` | -| `*BusinessPartner*` | `BusinessPartner` / `Suppliers` | -| `*Currency*` | `Currencies` (CodeList) | -| `*PaymentMethod*`, `*PaymentBlock*` | `PaymentMethods` / `PaymentBlockCodes` | -| `*Status*` | Status CodeList (domain-specific) | -| `step_ID`, `check_ID` (process workflow) | `ProcessSteps` / `ProcessStepChecks` | -| `*Severity*` | `Severities` CodeList | -| `*GLAccount*`, `*CostCenter*`, `*WBSElement*` | cost-object master data | - -### 6b. Filter bar (SelectionFields) check - -For each `UI.SelectionFields: [...]` in `app/annotations/*.cds`: - -```bash -grep -rnE "UI\.SelectionFields:\s*\[" app/annotations/ app/*.cds 2>/dev/null -``` - -For each field listed, verify a `@Common.ValueList` annotation exists, either: - -1. **Direct annotation** on the field -2. **Indirect via Association** — e.g., `step` (Association) has the ValueList, and `step_ID` (foreign-key column) is the actual SelectionField - -Pattern verification: - -```bash -for FIELD in CompanyCode SupplierBP Currency PaymentMethod ...; do - # Pattern 1: direct annotation on field - DIRECT=$(grep -rnE "$FIELD\s*@Common\.ValueList" app/annotations/ srv/ 2>/dev/null | wc -l) - # Pattern 2: annotation on Association (FK column ends with _ID) - ASSOC=$(echo "$FIELD" | sed 's/_ID$//') - if [ "$ASSOC" != "$FIELD" ]; then - INDIRECT=$(grep -rnE "$ASSOC\s+@Common\.ValueList" app/annotations/ srv/ 2>/dev/null \ - | grep -E "LocalDataProperty:\s*$FIELD\b" | wc -l) - else - INDIRECT=0 - fi - TOTAL=$((DIRECT + INDIRECT)) - if [ $TOTAL -eq 0 ]; then - echo "❌ $FIELD: missing ValueList in filter bar" - fi -done -``` - -### 6c. Edit form (FieldGroup / LineItem) check - -For each `@odata.draft.enabled` entity, check editable fields against master-data patterns. Edit-form ValueList is **strongly recommended** but can require context-dependent filtering (e.g., `step` filter on active template) — flag for manual review when filter cardinality is non-trivial. - -### 6d. TextArrangement check - -For each FK field with `ValueList`, verify the display-name cascade is configured: - -```cds -@Common.Text: <X>.Name -@Common.TextArrangement: #TextOnly -``` - -Without TextArrangement, the UI displays the raw ID — bad UX. - -### 6e. False-positive whitelist - -These fields are legitimately free-text (NOT findings): - -- Descriptive: `Description`, `Name`, `Notes`, `Comment`, `Justification`, `RejectReason` -- Natural external identifiers: `eDocumentGuid`, `InvoiceNumber`, `IBAN`, `TaxCode`, `VATIdNumber` (input via parsing, not catalog-pick) -- Amounts / quantities: `Amount`, `Percentage`, `Quantity`, `Price`, `Score`, `Confidence` -- Timestamps: `*At`, `*Date`, `*Timestamp` -- Technical: `ID`, `UUID`, `*_ID` (internal keys handled by framework) -- File-system: `FileName`, `FilePath`, `Url` - -## Step 7: Output Report - -Save markdown to `docs/audit/<yyyy-mm-dd>-customizing-honor.md`: - -```markdown -# Customizing Honor Audit — <scope> — <yyyy-mm-dd> - -## Summary -- Forward orphans: N (CSV seeded, code doesn't read) -- Inverse orphans: N (code reads, CSV doesn't seed) -- Hardcoded business decisions: N -- Master-data unreferenced fields: filter=N1, edit=N2, missing TextArrangement=N3 -- Catalog coverage: ✅ PASS / ❌ N CheckCode without raise site -- Adapter dual-source: ✅ M/N OK / ❌ N missing - -## Inverse Orphans -| ParamKey | File:line | Default used | Suggested category | - -## Forward Orphans -| ParamKey | CSV line | Verification grep | Action (remove seed or wire consumer) | - -## Hardcoded Business Decisions -| File:line | Pattern | Type | Severity | Suggested fix | - -## Master Data Unreferenced -| Entity.Field | Position (filter/edit/header) | File:line | Master data target | Severity | Suggested fix | - -## Fix Plan -1. [P1] hardcoded threshold — suggested diff: -```diff -- const HIGH_RISK_THRESHOLD = 80; -+ async function _resolveHighRiskThreshold(cc) { -+ const params = await getSystemParamReader().get('RISK', cc); -+ const n = parseInt(params.HIGH_RISK_THRESHOLD, 10); -+ return Number.isFinite(n) && n > 0 ? n : 80; -+ } -``` - -2. [P1] master-data unreferenced (filter bar) — suggested diff: -```diff -+ annotate service.MyEntity with { -+ companyCode @Common.ValueListWithFixedValues: true -+ @Common.ValueList: { ... }; -+ companyCode @Common.Text: companyCode.CompanyName @Common.TextArrangement: #TextOnly; -+ }; -``` - -## Verifications -- bash scripts/ci/check-systemparams-bidirectional.sh: PASS / FAIL -- bash scripts/ci/check-catalog-raise-coverage.sh: PASS / FAIL -- npm run lint:csv: PASS / FAIL -- npx cds compile srv app: PASS / FAIL - -## Residual Risk -- <list of items requiring manual review> -``` - -## Step 8: Apply Fixes (only when `--apply` / `mode=fix`) - -### Safe auto-applicable fixes - -1. **Add missing CSV seed rows** for inverse orphans: - ```csv - <PARAM_KEY>,,<detected_default>,<placeholder description>,<inferred category>,false - ``` - One commit per seed: `chore(seed): add <PARAM_KEY> for inverse-orphan fix`. - -2. **Add `@Common.ValueList` annotation** on filter bar fields for master-data references (SAFE — non-breaking, additive): - ```cds - annotate service.<Entity> with { - <field> @Common.ValueListWithFixedValues: true - @Common.ValueList: { - CollectionPath: '<TargetEntity>', - Parameters: [ - { $Type: 'Common.ValueListParameterInOut', LocalDataProperty: <field>, ValueListProperty: '<key>' }, - { $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: '<displayName>' } - ] - }; - <field> @Common.Text: <field>.<displayName> @Common.TextArrangement: #TextOnly; - }; - ``` - One commit per annotation: `feat(masterdata): add ValueList on <Entity>.<field>`. - -### NOT auto-applied (manual decision required) - -- **Removing forward-orphan CSV seeds** — could break customer environments that currently rely on the param (even if unused in code), and a future PR may add a consumer. Surface as finding only. -- **Refactoring hardcoded business decisions** — code refactor involves logic changes; risk too high for auto-apply. Surface as finding with suggested diff. -- **Adding ValueList on edit-form fields** — can require filter context (cascading dropdowns); test manually before applying. - -### Post-fix verification - -Re-run Step 2 + Step 6 and confirm orphan/unreferenced counts decrease as expected. - -## BTP vs On-Premise Differences - -This skill is target-edition-neutral — the same audit applies for CAP apps deployed to BTP Cloud Foundry, Kyma, or on-premise. The only consideration: - -| Aspect | BTP | On-Premise | -|---|---|---| -| SystemParameter storage | HANA Cloud / HDI | HANA or PostgreSQL | -| CSV seed loading | `cds deploy` at boot | Same | -| Per-tenant customizing | Single-tenant per service (multi-customer = multi-deployment) | Same | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| No `db/data/*-SystemParameters.csv` found | Project doesn't use the SystemParameter pattern | Skip Steps 2 and 5; run hardcoded sweep + master-data check only | -| `getSystemParamReader` not found in codebase | Project uses different reader pattern | Ask user for the reader function name + adjust grep pattern | -| Grep returns thousands of "matches" in hardcoded check | Permissive regex catches FP | Tighten patterns in Step 3a/3b; user can override with `--strict-thresholds` flag | -| Annotations folder doesn't exist | Project uses inline annotations or non-standard structure | Skip Step 6 or ask user to point to annotation locations | -| Multiple SystemParameter CSV files | Multi-namespace project | Audit all CSVs in scope; deduplicate keys | -| `forward orphan` is in user-known allowlist (e.g., NATS_URL only used in NATS adapter) | False positive | User can extend `FORWARD_ALLOWLIST` in the CSV's frontmatter comment or `--allowlist` flag | - -## What This Skill Does NOT Do - -- **No refactoring** — hardcoded business decisions are flagged with suggested fix, not auto-refactored -- **No CSV seed removal** — never deletes seeded parameters (breaking risk for customer envs) -- **No CDS schema validation** — assumes the schema compiles; audits the customizing coverage, not entity structure -- **No semantic verification** — doesn't check if the param value chosen by admin is a sensible default -- **No fallback chain testing** — verifies dual-source pattern is present, doesn't test runtime fallback behavior -- **No i18n translation generation** — flags hardcoded user-facing strings; user translates manually -- **No master-data CodeList generation** — references existing master data; doesn't create new CodeList entities - -## When to Use This Skill - -- **Pre-release audit** — verify admin Setup UI parameters are all wired to code consumers -- **Quarterly compliance check** — customizing drift over time (params added/removed) -- **Onboarding new developer** — understand which parameters drive runtime behavior -- **Customer escalation** — "admin changed param X but nothing happened" → run this skill to verify code reads it -- **Pre-acquisition audit** — when assessing a 3rd-party CAP app, verify customizing surface area is honest -- **Before refactor / cleanup** — identify which "dead" parameters can safely be removed (forward orphans) - -## When NOT to Use This Skill - -- **Project doesn't use SystemParameter pattern** — skill loses its core value; only hardcoded sweep applies -- **Pure greenfield project** — too early; come back after first iteration when params accumulate -- **Single-tenant fixed-config deployment** — customizing is less relevant; UX-level master-data check is still useful - -## Follow-up - -After this skill produces the audit report: - -- **Inverse orphans**: with `--apply` mode, the skill auto-adds CSV rows. Manually verify the inferred default + category, then commit. -- **Forward orphans**: manually decide for each: (a) remove from CSV (truly unused) or (b) wire consumer (currently TODO). -- **Hardcoded business decisions**: refactor each to `getSystemParamReader()` pattern, one parameter per commit, tested individually. -- **Master-data unreferenced fields**: with `--apply` mode, the skill auto-adds filter-bar ValueList. Manually verify edit-form bindings (context-dependent filters). - -Related skills: - -- [sap-cap-clean-core-enforce](../sap-cap-clean-core-enforce/SKILL.md) — verifies the S/4 API destinations consumed are Clean Core L-A (complementary audit) -- [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) — ABAP-side compliance (companion, source-side) - -## References - -- [CAP `cds.env` profile-aware configuration](https://cap.cloud.sap/docs/node.js/cds-env) -- [CAP CSV seed pattern](https://cap.cloud.sap/docs/guides/databases#providing-initial-data) -- [CAP `@Common.ValueList` annotation](https://cap.cloud.sap/docs/advanced/odata#valuehelp-annotations) -- [Fiori Elements V4 value-help integration](https://experience.sap.com/fiori-design-web/value-helper/) diff --git a/skills/sap-cap-security-rbac-matrix/SKILL.md b/skills/sap-cap-security-rbac-matrix/SKILL.md deleted file mode 100644 index 584d434d..00000000 --- a/skills/sap-cap-security-rbac-matrix/SKILL.md +++ /dev/null @@ -1,467 +0,0 @@ ---- -name: sap-cap-security-rbac-matrix -description: Comprehensive security audit for SAP CAP applications: multi-area parallel scanning (handlers, MCP/server, file-upload, deploy/k8s, jobs/integration, CC segregation) + role and role-collection coherence matrix (xs-security ↔ Keycloak ↔ services-auth ↔ handlers ↔ frontend ↔ SoD policy) + OWASP Top 10 / OWASP API Top 10 / ASVS / NIST CSF / CIS / SAP-SOM compliance mapping. Outputs a committable security audit report with findings ranked by confidence ≥0.8. Use when asked to "audit security", "verify RBAC coherence", "check OWASP compliance", "review CAP auth posture", or "produce a security compliance report". ---- - -# SAP CAP Security & RBAC Matrix Audit - -End-to-end security audit for enterprise SAP CAP applications: scans 6 attack-surface areas in parallel, builds a role / role-collection coherence matrix across 4 declaration layers (xs-security, Keycloak realm, CDS @restrict, handlers), and maps every finding to industry compliance frameworks (OWASP Top 10 2021, OWASP API Top 10 2023, ASVS L1-L3, NIST CSF, NIST SP 800-53, CIS K8s/Docker, SAP Secure Operations Map, GDPR, SOX 404). - -Read-only audit, idempotent, ~5-10 minute run with parallel agent dispatch. Designed for pre-deployment compliance gates and quarterly security health checks. - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Scope | `all` (6 attack-surface areas + role matrix) | Default to full sweep; user narrows if needed | -| Mode | `report` (read-only) | Safe by default | -| Confidence threshold | ≥0.8 | No false-positive shipping | -| Framework citation | OWASP A0X + ASVS V_a.b + at least 1 additional control (NIST / CIS / SAP-SOM / GDPR / SOX) | Defense-in-depth across multiple frameworks | -| Output | `docs/audit/<yyyy-mm-dd>-security-matrix.md` | Committable for traceability | -| Auto-fix | Safe-only: `assertCompanyCodeAccess` injection, `_sanitize*` wrap, CDS `@restrict` CC where addition, xs-security SoD bundle fix, Keycloak realm hardening | Reversible, contained | -| Target ASVS level | L2 minimum | Enterprise SAP + financial workflows posture | -| Parallel agent dispatch | 5 area agents + 1 role agent + 1 CC segregation agent | ~5-10 min total runtime | - -## Input - -Optional flags: - -- **scope** — `all` (default) | `security` (skip role matrix) | `roles` (only role coherence) | `cc-segregation` (only CC scoping audit) | `srv-only` | `auth-only` | `owasp` (only OWASP/Industry) | `compliance` (only matrix mapping) -- **mode** — `report` (default) | `fix` (safe-only auto-apply on dedicated branch) | `pending-only` (re-check previous findings) -- **canonical role list** — path to a project-specific role catalog file (auto-detected if not provided) -- **target frameworks** — comma list to restrict, e.g., `owasp-top10,asvs,nist-csf` - -## Framework Reference (authoritative) - -| Framework | Version | Scope | -|---|---|---| -| **OWASP Top 10** | 2021 | Web app generic — A01 Broken Access Control · A02 Crypto Failures · A03 Injection · A04 Insecure Design · A05 Misconfiguration · A06 Vulnerable Components · A07 ID & Auth · A08 SW & Data Integrity · A09 Logging & Monitoring · A10 SSRF | -| **OWASP API Security Top 10** | 2023 | REST/OData API — API1 BOLA · API2 Broken Auth · API3 BOPLA · API4 Resource Consumption · API5 BFLA · API6 Sensitive Business Flows · API7 SSRF · API8 Misconfig · API9 Improper Inventory · API10 Unsafe Consumption | -| **OWASP ASVS** | 4.0.3 L1-L3 | V2 Auth · V3 Session · V4 Access Control · V5 Validation · V6 Crypto · V7 Errors · V8 Data Protection · V9 Comms · V10 Code · V11 BizLogic · V12 Files · V13 API · V14 Config | -| **NIST CSF** | 2.0 | Identify · Protect · Detect · Respond · Recover | -| **NIST SP 800-53** | rev5 | AC · AU · IA · SC · SI control families | -| **CIS Kubernetes** | 1.9 | Pod security · RBAC · NetworkPolicy · Secret mgmt · Container image | -| **CIS Docker** | 1.6 | Host · Daemon · Image · Container runtime | -| **SAP Secure Operations Map** | 2024 | 12 layer: Awareness · Process · Compliance · Auth · UI · Custom Code · Roles · Audit · Sec Hardening · System Mgmt · Network · OS/DB | -| **GDPR** | EU 2016/679 | Art. 5 Data Minimization · Art. 17 Right to Erasure · Art. 25 Privacy by Design · Art. 32 Sec of Processing | -| **SOX 404** | PCAOB AS 2201 | Audit log append-only · 4-eyes SoD · Change mgmt · Access review | - -## Step 1: Pre-flight + Project Detection - -### 1a. Verify CAP project type - -```bash -test -f package.json && grep -q '"@sap/cds"' package.json -``` - -If not a CAP project, stop and inform user. - -### 1b. Detect security configuration files - -```bash -find . -maxdepth 3 -name "xs-security.json" 2>/dev/null -find . -maxdepth 4 -name "keycloak-realm.json" -o -name "*-keycloak-realm.json" 2>/dev/null -find srv -name "services-auth.cds" -o -name "*-auth.cds" 2>/dev/null -find approuter -name "xs-app.json" 2>/dev/null -``` - -Note presence/absence of each file — Steps 2 and 3 adapt accordingly. - -### 1c. Resolve canonical role catalog - -Auto-detect roles from `xs-security.json`: - -```bash -node -e "JSON.parse(require('fs').readFileSync('xs-security.json','utf8')).scopes.map(s => s.name.split('.').pop())" 2>/dev/null -``` - -If a `CLAUDE.md` or `docs/security.md` exists with a "canonical roles" section, prefer that as the authoritative list. - -### 1d. Branch creation (if mode=fix) - -```bash -git checkout -b codex/security-matrix-<scope>-$(date +%Y-%m-%d) -``` - -## Step 2: Parallel Area Audits (5 area agents) - -Dispatch 5 parallel scans, one per attack-surface area: - -### Agent S1 — Backend handlers + auth - -**Scope**: `srv/handlers/*.ts` + `srv/services-auth.cds` + `srv/*.cds` (main service) - -**Looks for** (frameworks: OWASP A01/A03/A04 · API1/API3/API5 · ASVS V4/V5): - -- **Authorization bypass**: action handler accepting `companyCode` from `req.data` without `assertCompanyCodeAccess(req, cc)` call → A01 Broken Access Control · API1 BOLA · API5 BFLA -- **SQL injection** via template strings in `cds.run` / `db.run` → A03 · ASVS V5.3.4 -- **`forUpdate` missing** in lifecycle action (TOCTOU race) → A04 Insecure Design -- **`req.reject` with `err.message` raw** instead of `rejectSafe()` → A09 · ASVS V7.4.1 (info leak) -- **CDS @restrict gap**: entity exposed without `@restrict` OR `grant: '*'` without CC scope (only SuperAdmin allowed) → API3 BOPLA · ASVS V4.2.2 -- **AuditLogEntry INSERT bypassing `_sanitizePII`** → A02 · GDPR Art. 5 · ASVS V8.3.4 -- **Mass assignment**: `req.data` whitelist not enforced (FE PATCH can write `@Core.Computed` / `LegalImmutable` fields) → API6 · ASVS V5.1.2 - -### Agent S2 — MCP + server + token - -**Scope**: `srv/mcp/*.ts` + `srv/server.ts` + `srv/jobs/CronJob*.ts` + sacAnalyticsHandler.ts + ai/MCP* if present - -**Looks for** (OWASP A02/A05/A07 · API2/API8/API10 · ASVS V2/V3/V14): - -- **Token validation without `timingSafeEqual` HMAC** → A02 · API2 · ASVS V2.4.1 (timing-attack safe compare) -- **Fail-closed gap in production** (Swagger UI mount + MCP fail-closed + OIDC skip path exact match) → A05 · API8 · ASVS V14.3.2 -- **CC whitelist enforcement** on MCP query-data (auto-inject `{ccField IN whitelist}`) → API1 · ASVS V4.2.1 -- **JWT validation** (no `alg: none`, no `kid` spoofing, issuer + audience checked) → A07 · ASVS V3.5.3 -- **Bearer token in URL/query** (logged → leak) → ASVS V2.2.5 (token in header only) - -### Agent S3 — File upload + parsing - -**Scope**: `srv/inbound/*.ts` + `srv/utils/xmlParser.ts` + `srv/extraction/**.ts` + `srv/dms/**.ts` + archive/documentHub actions - -**Looks for** (OWASP A03/A10 · API7/API10 · ASVS V12/V13): - -- **XXE injection** (xml2js / fast-xml-parser external entity processing) → A03 · ASVS V13.2.6 -- **Zip slip / path traversal** in archive extraction → A01 · ASVS V12.3.1 -- **Magic bytes bypass** (header NOT trusted, only magic bytes) → ASVS V12.1.1 -- **SSRF via Document Hub resolver** (host/protocol control) → A10 · API7 · ASVS V12.6.1 -- **Stream limit bypass** (size cap enforced BEFORE read, not after) → ASVS V12.1.3 -- **CC propagation** on streamDocument → API1 · ASVS V4.2.1 - -### Agent S4 — Deploy + k8s + secrets - -**Scope**: `Dockerfile*` + `k8s/*.yaml` + `xs-security.json` + `.github/workflows/*.yml` + `scripts/ci/*.sh` + `mta.yaml` - -**Looks for** (OWASP A05/A06/A08 · CIS K8s/Docker · NIST SC-7/SI-2): - -- **GH Actions injection** (`pull_request_target` + checkout PR head) → A08 · ASVS V14.2.1 -- **SHA pinning gap** (`actions/checkout@v4` non-SHA) → A08 · NIST SR-3 -- **Secret echo in workflow logs** → A02 · ASVS V2.10.4 -- **K8s secret mounted as env var** (HANA/S4/MCP/OIDC password) → CIS K8s 5.4.1 · ASVS V2.10.1 -- **NetworkPolicy egress ALLOW_ALL** → CIS K8s 5.3.2 · NIST SC-7 -- **Container runs as root** (no `runAsNonRoot`) → CIS K8s 5.2.6 · CIS Docker 4.1 -- **`tenant-mode: shared`** when single-tenant intended → API3 BOPLA · ASVS V4.3.1 -- **Dockerfile `FROM <image>:latest`** (image pinning by tag) → CIS Docker 4.2 - -### Agent S5 — Jobs + integration + adapters - -**Scope**: `srv/jobs/*.ts` + `srv/messaging/*.ts` + `srv/integration/**.ts` + `srv/notifications/**.ts` + `srv/audit/auditLogger.ts` - -**Looks for** (OWASP A02/A08/A09 · API2/API10 · ASVS V6/V9 · SAP-SOM L4/L8): - -- **Webhook signature missing** (Event Mesh, BPA callback, email receivers) → A08 · API10 · ASVS V14.5.3 -- **system-user role bypass** (used outside documented webhook endpoints) → API5 BFLA · ASVS V4.2.1 -- **Notification PII leak** (`_sanitizeNotificationComment` bypass on SMTP/Teams/Slack outbound) → A02 · GDPR Art. 5 · ASVS V8.3.4 -- **Adapter factory selection bypass** (SystemParameter `*_ADAPTER` writable by non-Admin) → API3 BOPLA · A05 -- **Idempotency key replay** (event format not validated) → API4 · ASVS V11.1.6 -- **SMTP TLS verification disabled** (`rejectUnauthorized: false`) → A02 · ASVS V9.1.1 -- **Audit log NOT append-only** at DB layer → A09 · SOX 404 · ASVS V7.3.2 -- **Encryption-at-rest missing** for `IsSecret=true` SystemParameter → A02 · ASVS V6.2.1 - -## Step 3: Agent O1 — OWASP/Industry Orthogonal - -SKIP if `scope=roles` / `auth-only`. - -**Scope**: `srv/server.ts`, `approuter/xs-app.json`, `srv/handlers/*.ts`, `package.json` + `package-lock.json`, `srv/utils/paramEncryption.ts`, `.env*` / `terraform/**/secrets*` - -**Categories** (cross-cutting): - -1. **Security headers** [OWASP A05 · ASVS V14.4]: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cookie SameSite+Secure+HttpOnly -2. **Cryptographic failures** [A02 · ASVS V6]: Hash algo (no MD5/SHA1), RNG (`crypto.randomBytes`), JWT signature pinned, encryption-at-rest AES-256-GCM, key derivation (PBKDF2 ≥600k iter / Argon2id) -3. **IDOR / BOLA horizontal** [A01 · API1]: action handlers receiving `id` parameter without `companyCode` filter in lookup -4. **SSRF host control** [A10 · API7 · ASVS V12.6]: destination URL parametrized + host whitelist (no `0.0.0.0`/`169.254.169.254`/`localhost`) -5. **Components & supply chain** [A06 · NIST SR-3]: `npm audit --production` critical/high residue, lockfile committed, frozen-lockfile in CI -6. **Insecure design** [A04 · ASVS V1]: abuse case missing (webhook flood, race condition, replay), trust boundary unclear -7. **ID & Auth** [A07 · ASVS V2/V3]: session fixation, cookie timeout > 8h, refresh token rotation, MFA option -8. **Logging adequacy** [A09 · NIST AU-2/AU-3 · ASVS V7]: `cds.log()` vs `console.*`, auth failure logged distinguishable, SuperAdmin trail, rate-limit hit logged -9. **Hardcoded secrets** [A02 · ASVS V2.10]: regex sweep for `(api[_-]?key|secret|password|token)\s*=\s*['"][a-zA-Z0-9]{16,}['"]` in srv/scripts/terraform -10. **API inventory & misconfig** [API9/API8]: OpenAPI spec up-to-date, debug/health endpoints behind auth, CORS `*` on sensitive endpoints - -## Step 4: Role + Role-Collection Coherence Matrix - -SKIP if `scope=security` / `cc-segregation`. - -For each canonical role identified in Step 1c, verify 4-layer declaration coherence: - -### Matrix verification - -| Layer | Source | Check | -|---|---|---| -| xs-security.json | `scopes[]` + `role-templates[]` + `role-collections[]` | Every canonical role has scope + template + collection | -| Keycloak realm | `roles.realm[]` + `groups[]` + `clients[].roleMappings` | Every canonical role has realm-role + matching group | -| services-auth.cds | `@restrict.to[]` + `@restrict.grant` | Every canonical role used in at least one `@restrict` | -| Handlers runtime | `req.user.is('<role>')` calls | Every role-name string matches canonical set | - -### Coherence findings - -| Drift type | Severity | Example | -|---|---|---| -| **Zombie role** | LOW | Role in xs-security but never in @restrict + never in handler check | -| **Orphan role check** | HIGH | Handler `req.user.is('Approver')` but no `Approver` scope in xs-security | -| **Naming drift** | HIGH | Role `Manager` in xs-security but handler checks `manager` (case mismatch) | -| **Keycloak gap** | HIGH | Role in xs-security but missing realm-role + group → cannot be assigned on-prem | -| **Collection over-bundle** | HIGH | Role collection includes mutex roles (e.g., `Approver` + `PostingOfficer` outside SOD-exempt) | - -### SoD (Segregation of Duties) policy verification - -For canonical role pairs declared as SoD-conflicting: - -```typescript -// Expected pattern in OIDCAuthStrategy / handlers: -const SOD_EXEMPT_ROLES = ['OperationalAdmin', 'SuperAdmin', 'system-user']; -const SOD_MUTEX_PAIRS = [['Approver', 'PostingOfficer'], ...]; -``` - -For each mutex pair, verify no role collection bundles both (unless in `SOD_EXEMPT_ROLES`). - -## Step 5: CC Segregation Audit - -SKIP if `scope=roles` / `auth-only` / `security`. - -For multi-tenant CAP apps where each tenant has its own `CompanyCode`: - -### 5a. Entity scope audit (services-auth.cds) - -For every entity with `CompanyCode` column in schema: - -```bash -# Find entities with CompanyCode field -grep -nE "CompanyCode\s*:\s*String" db/schema.cds 2>/dev/null - -# Verify their @restrict has CC where -grep -nE "@restrict|@grant" srv/services-auth.cds | grep -B1 -A2 "<EntityName>" -``` - -Expected: `where: "CompanyCode = \$user.attr.CompanyCode"` for every non-SuperAdmin grant. - -### 5b. Handler audit - -For every action handler accepting `req.data.companyCode`: - -```bash -grep -nE "req\.data\.companyCode" srv/handlers/*.ts 2>/dev/null -``` - -Each call must be followed (within a few lines) by `assertCompanyCodeAccess(req, ...)`. If not → finding HIGH (cross-CC mutation possible). - -### 5c. Repository query audit - -For repository functions: - -```bash -grep -rnE "(SELECT|UPDATE|DELETE).*from\s*\(" srv/repositories/ 2>/dev/null -``` - -Must include `companyCode` in WHERE clause when entity has CC column. Repository function signatures should expose `companyCode` parameter (not inferred from request context, to avoid leaks). - -### 5d. Cross-CC leak via association - -For entities with `Association to <ParentEntity>`: - -```bash -grep -rnE "Association to" db/schema.cds 2>/dev/null -``` - -Verify parent entity is also CC-scoped — otherwise a child query may leak parent data from other CC. - -## Step 6: Compliance Matrix Mapping - -For each finding from Steps 2-5, map to control framework. - -### Per-finding mapping table - -| ID | Severity | OWASP T10 | OWASP API | ASVS | NIST 800-53 | CIS | SAP SOM | GDPR | SOX | -|---|---|---|---|---|---|---|---|---|---| - -### Coverage rollup - -| Framework | Findings covered | Status | -|---|---|---| -| OWASP A01-A10 | N total | ✅ / ⚠️ / 🔴 | -| OWASP API1-API10 | N | ✅ / ⚠️ / 🔴 | -| ASVS L1 | N covered / total | ✅ | -| ASVS L2 | N covered / total | ✅ | -| ASVS L3 | N covered / total | ⚠️ | -| NIST CSF function | N control | — | -| CIS K8s/Docker | N control | — | -| SAP SOM 12 layer | N / 12 | — | - -### Gap analysis (controls NOT yet verified) - -Honest assessment: list framework areas the audit did NOT cover (e.g., ASVS V12.4 anti-virus integration, NIST AC-17 Remote Access). - -## Step 7: Output Report - -Save markdown to `docs/audit/<yyyy-mm-dd>-security-matrix.md`: - -```markdown -# Security Matrix Audit — <scope> — <yyyy-mm-dd> - -## Pre-flight -- Branch / sha / scope / mode - -## Discovery -- Security config files detected -- Canonical roles identified: <N> -- Mode dispatched: <area agents + role agent + CC agent> - -## Security Review (per area) -| Area | Status | Findings | Frameworks | - -## Role Definition Matrix -(N canonical × 4 layers) - -## Role Collection Matrix -(M collections × role inclusion × SoD conflicts) - -## CC Segregation Matrix -(Entity × @restrict CC where × handler guard × repository CC filter) - -## Findings (confidence ≥0.8) -### [HIGH] CATEGORY: file:line -- Description -- Exploit / Impact -- Framework mapping: OWASP A0X · API_Y · ASVS V_a.b · NIST CONTROL · CIS X.Y · SAP-SOM L_n -- Confidence: 0.X -- Fix: <recommendation> - -## Compliance Matrix -(per-finding + coverage rollup + gap analysis) - -## Checks Passed (verified clean) -<list> - -## Fix Plan / Fix Applied (mode=fix) -| Severity | Item | Effort | Auto-fixable | - -## Re-verification -- Cadence: monthly / quarterly / pre-deployment -- Command: `sap-cap-security-rbac-matrix` (this skill) -``` - -## Step 8: Apply Fixes (only when `mode=fix`) - -### Safe auto-applicable - -1. **`assertCompanyCodeAccess` injection** in handler that accepts `companyCode` from request (helper must already exist in `srv/utils/authGuards.ts`) -2. **`_sanitize*` wrap** on PII / notification output (helper must already exist) -3. **CDS `@restrict` CC where addition** in `services-auth.cds` for entities with CC column (CDS-only, additive) -4. **xs-security.json SoD bundle fix** — remove conflicting role from over-bundled collection -5. **Keycloak realm hardening** — add token TTL defaults, add missing realm-role for declared scope - -### NOT auto-applied (manual decision required) - -- Refactor handler signature -- Schema change (db/migrations) -- Action availability matrix change -- Cross-cutting adapter factory refactor -- Rename collection in xs-security (breaking change for existing bindings) -- Remove legacy role (architectural decision) - -### Verification after fix - -```bash -git diff --stat -npx cds compile srv app --service OrchestratorService --to edmx > /dev/null -node -e "JSON.parse(require('fs').readFileSync('xs-security.json','utf8'))" # valid JSON -``` - -## BTP vs On-Premise Differences - -| Aspect | BTP Cloud Foundry | Kyma | On-Premise | -|---|---|---|---| -| Auth | XSUAA | XSUAA + IAS | Keycloak / IAS / custom | -| Role mapping | xs-security.json + IAS groups | Same | Keycloak realm + groups | -| Token lifecycle | XSUAA-managed | Same | Keycloak-managed (TTL must be pinned) | -| Audit log | BTP Audit Log Service | Same | Custom adapter | -| Network isolation | Cloud Connector | NetworkPolicy + Connectivity Proxy | Firewall + VLAN | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| No `xs-security.json` found | Project uses different auth scheme | Skill skips xs-security checks, focuses on services-auth.cds + handlers | -| No canonical role catalog | Project lacks a centralized role definition | Auto-extract roles from xs-security.json scopes; warn user about role drift risk | -| Keycloak realm not found | BTP-only deployment | Skip Keycloak matrix check; flag if on-prem deployment is planned | -| Grep returns too many "matches" in handlers sweep | Permissive patterns | Tighten patterns; use `--scope srv/handlers/specific` for focused audit | -| `assertCompanyCodeAccess` helper not found | Project uses different CC guard pattern | Ask user for the helper function name + adjust check | -| Audit log entity not detected | Custom audit table name | Ask user to specify; default search pattern is `AuditLogEntry` / `audit_log` | - -## What This Skill Does NOT Do - -- **No penetration testing** — static audit; doesn't execute exploits -- **No runtime instrumentation** — analyzes source code, not running app -- **No SQL injection runtime test** — flags suspicious patterns, doesn't execute -- **No dependency CVE deep-scan** — basic `npm audit` only; for deep analysis use Snyk / Dependabot -- **No social engineering / phishing audit** — out of code-level scope -- **No third-party service security** (XSUAA / IAS internals) — assumes BTP managed services are secure -- **No compliance certification** — provides evidence; certification (SOC 2, ISO 27001) requires auditor sign-off - -## When to Use This Skill - -- **Pre-deployment gate** — quarterly compliance check before production release -- **Pre-acquisition audit** — assessing 3rd-party CAP app security posture -- **Post-incident review** — after security event, verify scope + identify similar gaps -- **Customer compliance request** — SOC 2 / ISO 27001 / GDPR evidence -- **Role refactor validation** — after adding/removing roles, verify coherence -- **OWASP/ASVS readiness** — pre-ASVS Level 2 verification audit - -## When NOT to Use This Skill - -- **Greenfield project before first deploy** — too early; come back after first iteration with concrete attack surface -- **Pure ABAP / non-CAP project** — use [sap-clean-core-atc](../sap-clean-core-atc/SKILL.md) instead -- **Single-user / personal project** — overkill; multi-tenant patterns don't apply -- **Real-time penetration test** — use specialized pen-test tooling - -## Follow-up - -After this skill produces the audit: - -- **HIGH findings**: fix immediately (auto-apply where safe, manual review otherwise) -- **MEDIUM findings**: track in pending list, fix in next sprint -- **LOW findings**: document in `docs/security-backlog.md`, address opportunistically -- **Role drift**: align across 4 layers (xs-security ↔ Keycloak ↔ services-auth ↔ handlers) -- **CC segregation gap**: prioritize — cross-CC leaks are GDPR / SOX critical -- **Re-run quarterly** — cadence depends on release velocity; weekly during active feature work - -Related skills: - -- [sap-cap-clean-core-enforce](../sap-cap-clean-core-enforce/SKILL.md) — S/4 API Clean Core compliance (complementary) -- [sap-cap-customizing-honor](../sap-cap-customizing-honor/SKILL.md) — customizing coverage audit (complementary) -- [migrate-custom-code](../migrate-custom-code/SKILL.md) — ABAP-side ATC fixes (companion for SAP custom code) - -## Battle-Tested Patterns Referenced - -This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md), which catalogs gotchas distilled from production deployments. The patterns most relevant to a security/RBAC audit: - -- **Category 4 (entire) — Security Defense-in-Depth**: the seven multi-layer patterns are the foundation for the audit's compliance mapping (OWASP/ASVS/NIST/CIS/SAP-SOM). -- **4.1 Audit log append-only — three layers** (CDS + handler + DB trigger): every entity with an audit log must satisfy all three. -- **4.2 PII sanitization before audit log**: cross-check that the project has a centralized `_sanitizePII` helper; if missing, flag as HIGH. -- **4.3 Magic bytes verification on upload**: cross-check file-upload handlers — never trust `Content-Type` header. -- **4.4 Rate limiters per endpoint class**: verify global + write + upload + jobs + integration limiters all exist. -- **4.5 Token rotation quarterly**: ensure a runbook exists for every long-lived token (job, MCP, integration). -- **4.6 OData `$expand` depth + `$top` cap**: verify request preprocessing enforces both. -- **2.7 `xs-security.json` tenant-mode discipline**: single-tenant `dedicated` by default; `shared` requires deliberate review of data-layer isolation. - -## Recommended Companion Plugins - -| Plugin / Skill | Why for security audits | -|---|---| -| `sap-cap-capire` | CDS `@restrict` semantics, authentication/authorization patterns reference | -| `sap-btp-audit-log` | BTP Audit Log Service integration — adopt as destination for local AuditLogEntry events | -| `sap-btp-cloud-logging` | Cloud Logging dashboards for security event correlation | -| `sap-btp-connectivity` | XSUAA scope wiring, principal propagation, destination service auth | -| `sap-docs` | SAP Notes search for security advisories on consumed S/4 APIs | -| `context7` | OWASP / ASVS / NIST reference lookup for compliance mapping rationale | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. - -## References - -- [OWASP Top 10 2021](https://owasp.org/Top10/) -- [OWASP API Security Top 10 2023](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) -- [OWASP ASVS 4.0.3](https://github.com/OWASP/ASVS/tree/v4.0.3) -- [NIST CSF 2.0](https://www.nist.gov/cyberframework) -- [NIST SP 800-53 rev5](https://csrc.nist.gov/projects/risk-management/sp800-53-controls) -- [CIS Kubernetes Benchmark 1.9](https://www.cisecurity.org/benchmark/kubernetes) -- [CIS Docker Benchmark 1.6](https://www.cisecurity.org/benchmark/docker) -- [SAP Secure Operations Map](https://help.sap.com/docs/secure-operations-map) -- [CAP `@restrict` + XSUAA documentation](https://cap.cloud.sap/docs/guides/authorization) diff --git a/skills/sap-cap-stack-audit-full/SKILL.md b/skills/sap-cap-stack-audit-full/SKILL.md deleted file mode 100644 index 94cadee4..00000000 --- a/skills/sap-cap-stack-audit-full/SKILL.md +++ /dev/null @@ -1,462 +0,0 @@ ---- -name: sap-cap-stack-audit-full -description: Orchestrator audit that runs the full SAP CAP + Fiori Elements + BTP stack of checks in parallel — UI5 linter, manifest validation, CDS compile, TypeScript typecheck, hardcoded-customizing sweep, test suite, plus optional specialized agent reviews (UI5 code quality, CAP performance, project architecture, security, deployment readiness) — and consolidates everything into a single deduplicated report. Use when asked to "run a full audit", "audit the whole stack", "pre-release check", "production readiness audit", "do a comprehensive SAP audit", or to dispatch multiple specialized audits in one shot. ---- - -# SAP CAP Stack — Full Audit Orchestrator - -Run **every applicable check** on a SAP CAP + Fiori Elements + BTP project in **six phases**, parallelize the work aggressively, deduplicate findings, and emit one consolidated report. The skill is **read-only**: it never modifies files and never opens PRs. Its purpose is situational awareness before a release, a hand-off, or a critical merge. - -The skill orchestrates: -- **Static analysis tools** (UI5 linter, manifest schema, CDS compile, TypeScript) -- **Test suite** scoped to the project's existing configuration -- **Specialized audit skills** in this repository ([`sap-cap-clean-core-enforce`](../sap-cap-clean-core-enforce/SKILL.md), [`sap-cap-customizing-honor`](../sap-cap-customizing-honor/SKILL.md), [`sap-cap-security-rbac-matrix`](../sap-cap-security-rbac-matrix/SKILL.md), [`sap-fiori-app-audit`](../sap-fiori-app-audit/SKILL.md), [`sap-cap-text-polish`](../sap-cap-text-polish/SKILL.md)) -- **MCP tooling** when available (Fiori Tools, SAP Docs, UI5 Tooling, CAP/cds-mcp) - -It does **not** invent new checks; it composes existing ones. - -## v1 Guardrails - -- **Idempotent and read-only.** No file modification, no commits, no PRs. -- **Timeouts.** Each sub-audit caps at 10 min wall-clock; on timeout the result is marked `TIMEOUT` and the run continues. -- **Parallelism over speed.** Phases 2 and 3 fan out aggressively (one message, many tool calls). Phase 4 and 5 run sequentially because they depend on Phase 2/3 outputs. -- **Deduplication.** If two sub-audits report the same finding (same `file:line`, same category), keep the description with the most evidence and link to the other audit. -- **Cite `file:line`.** Every finding must have a clickable evidence pointer. - -## Smart Defaults (apply silently, do NOT ask) - -| Aspect | Default | Why | -| --- | --- | --- | -| Mode | Full audit (all phases) | The point of this skill is comprehensive coverage | -| Output language | English (Italian if project requires) | Match project convention | -| Output destination | `docs/audit/<yyyy-mm-dd>-stack-audit.md` | Stable filename convention | -| Test scope | Project's `npm test` script with `--runInBand` if jest, or fallback to project's existing CI test command | Do not invent a new test invocation | -| Agent budget | Max 6 specialized agents in parallel | Keep cost predictable | -| Skip flag | `quick` skips agent reviews (Phase 3); CLI static checks only | ~2 min run instead of ~10 min | - -## Input - -Single optional argument: - -| Argument | Behavior | -| --- | --- | -| (empty) | Full audit of the whole project | -| `ui5` | Skip CAP/BTP-specific phases; only UI5/Fiori checks | -| `cap` | Skip UI5 phases; only CAP backend | -| `btp` | Only BTP best practices + deployment readiness + connectivity | -| `quick` | All phases EXCEPT Phase 3 (agent reviews). ~2 min | -| `<app-name>` or `<sub-folder>` | Filter to a specific app or directory | - -Examples: (no arg) → full · `ui5` · `cap` · `quick` · `manager-ui` · `srv/handlers`. - -## Step 0: Ask the deployment target - -**Before any other audit step**, the orchestrator MUST identify the deployment target. Findings, recommendations and manifest checks differ substantially between targets; running the audit without knowing the target produces inapplicable advice. - -### 0a — Try to auto-detect from project state - -```bash -# BTP CF signature -test -f mta.yaml && echo "Detected: BTP Cloud Foundry (mta.yaml present)" - -# BTP Kyma signature -ls k8s/*.yaml 2>/dev/null | head -1 && echo "Detected: BTP Kyma or On-Prem Kyma (k8s/ manifests present)" - -# Discriminator within Kyma flavors -grep -lE "kyma-project\.io/v1beta1|gateway\.kyma-project\.io" k8s/*.yaml 2>/dev/null | head -1 && \ - echo " → APIRule CRD used: likely BTP Kyma or Gardener/Rancher with Kyma module" -grep -lE "ingress\.kubernetes\.io|networking\.k8s\.io" k8s/*.yaml 2>/dev/null | head -1 && \ - echo " → vanilla Ingress used: likely on-prem k3d / OpenShift / vanilla Kubernetes" - -# CDS profile clues (.cdsrc.json or package.json) -grep -oE '"(production|production-pg|k8s|k8s-hana|k8s-onprem|onprem)"' .cdsrc*.json package.json 2>/dev/null | sort -u - -# Auth indicator -test -f xs-security.json && echo " → XSUAA (BTP CF)" -ls k8s/onprem/keycloak-realm.json k8s/keycloak/*.json 2>/dev/null && echo " → Keycloak (On-Prem Kyma)" -``` - -### 0b — Ask the user when ambiguous - -If auto-detection is inconclusive or yields multiple candidates, ask the user explicitly: - -> **Which deployment target are we auditing?** (Default: **BTP Cloud Foundry** if you're unsure — most widely-adopted SAP-managed runtime) -> -> 1. **BTP Cloud Foundry** *(default)* — XSUAA + HANA HDI + html5-apps-repo + mta.yaml. Best for: managed-services-only customers; lowest operational ceremony. -> 2. **BTP Kyma** — XSUAA/IAS via OIDC + PostgreSQL in-cluster or HANA Cloud + APIRule CRD + Kyma BTP Operator. Best for: customers who want Kubernetes operational model; container-native eventing. -> 3. **On-Premise Kyma** — Customer IdP (Keycloak) + HANA on-prem or PostgreSQL on-prem + cluster flavor (k3d / Rancher / Gardener / OpenShift) + `*.svc.cluster.local` remotes. Best for: data sovereignty mandates; existing Kubernetes investment. - -**Not supported**: On-Premise Cloud Foundry (SAP Cloud Foundry On-Premise reached end of maintenance — the orchestrator refuses to plan deployment-readiness against an EOL runtime). If auto-detection identifies an on-prem CF environment, emit a strategic finding recommending migration to BTP CF (managed) or BTP Kyma, and stop. - -Do **not** guess. The audit's per-target Phase 3 dispatches differ; getting this wrong wastes the agent budget. When the user defers ("I don't know yet"), default to **BTP Cloud Foundry** and emit a finding recommending the customer confirm the target before deployment-readiness sign-off. - -### 0c — Persist the target for downstream phases - -Record the chosen target as `$TARGET` (one of `btp-cf`, `btp-kyma`, `onprem-kyma`). Every subsequent phase consults `$TARGET`: - -| Phase | What changes with target | -|---|---| -| Phase 2 static checks | UI5 linter / manifest validation always; on Kyma also check APIRule / NetworkPolicy / HPA / PDB | -| Phase 3 specialized agents | BTP CF → BTP best-practices + deployment-readiness against mta.yaml; Kyma → Kyma readiness + APIRule audit + Bitnami PG verification; On-prem → Keycloak realm coherence + sizing tier match | -| Phase 4 CAP-specific | Profile discovery filters to relevant profiles (drop CF-only profiles if target is Kyma, etc.) | -| Phase 5 best-practice cross-check | Different DO/DON'T sets cited (e.g. Free Plan CF-only warning is irrelevant on a Kyma audit if customer is pay-tier) | - -## Step 1: Pre-flight - -Always runs, ~30 s. - -```bash -git status --short && git log -1 --oneline -git branch --show-current -``` - -Detect project shape: - -```bash -# CAP signature -test -f srv/server.ts || test -f srv/server.js || test -f srv/index.ts || ls srv/*.cds 2>/dev/null | head -1 - -# Fiori apps detection (multiple layouts supported) -APPS=$(ls -d app/*/webapp 2>/dev/null | wc -l) -test "$APPS" -eq 0 && APPS=$(ls -d apps/*/webapp 2>/dev/null | wc -l) -echo "Detected $APPS Fiori app(s)" - -# CDS compile sanity (entry-point auto-discovery) -SRV=$(grep -lE "^service\s+\w+" srv/*.cds | head -1) -test -n "$SRV" && npx cds compile srv app --service "$(grep -oE '^service\s+\w+' "$SRV" | head -1 | awk '{print $2}')" --to edmx > /tmp/audit-svc.edmx 2>&1 && echo "CDS OK" || echo "CDS FAIL" - -# Clean Core Level A pre-flight (always; this is a deployment gate, not optional) -test -f srv/integration/s4*Policy* || \ - echo "WARN: no S/4 compatibility catalog found — Clean Core Level A audit will run discovery-mode only (see Step 3 dispatch of sap-cap-clean-core-enforce)" -``` - -Output of Phase 1: branch SHA, dirty files, project signature (CAP yes/no, Fiori apps count, CDS compile status, primary service name), **deployment target** chosen in Step 0, Clean Core catalog presence flag. - -If CDS compile fails, **stop the audit** and emit a single-finding report — no point auditing on top of a broken model. - -## Step 2: Static analysis (parallel) - -All commands in Phase 2 are independent; dispatch them via **multiple Bash tool calls in a single message**. - -### 2a — UI5 Linter per app - -```bash -for app in $(ls -d app/*/webapp 2>/dev/null | cut -d/ -f2); do - dir="app/$app" - result=$(cd "$dir" && npx -y @ui5/linter 2>&1 | grep -E "[0-9]+ problems" | tail -1) - echo "$app: $result" -done -``` - -### 2b — Manifest schema validation - -Prefer MCP tool when available: - -``` -mcp__plugin_sapui5_ui5-tooling__run_manifest_validation app/<each>/webapp/manifest.json -``` - -Fallback: JSON Schema validation via `ajv` or similar against the manifest schema bundled in `@sap-ux/manifest-validation-tool` if installed. - -### 2c — Fiori app discovery - -``` -mcp__plugin_sap-fiori-tools_fiori-tools__list_fiori_apps (searchPath = cwd) -``` - -Record anomalies: duplicate app IDs, non-standard `appPath`, `odataVersion` mismatch with the backend service. - -### 2d — Test suite - -```bash -# Discover the test runner -if grep -q '"jest"' package.json && grep -q '"test":' package.json; then - npm test 2>&1 | tail -10 -elif test -f vitest.config.ts; then - npx vitest run 2>&1 | tail -10 -elif grep -q '"mocha"' package.json; then - npm test 2>&1 | tail -10 -fi -``` - -Skip in `quick` mode. - -### 2e — TypeScript typecheck per app and srv - -```bash -# Backend -test -f srv/tsconfig.json && npx -p typescript tsc --noEmit -p srv/tsconfig.json 2>&1 | tail -5 - -# Each app -for app in $(ls -d app/*/ 2>/dev/null); do - test -f "$app/tsconfig.json" && (cd "$app" && npx -p typescript tsc --noEmit 2>&1 | head -5) -done -``` - -### 2f — Hardcoded customizing sweep - -A quick regex pass to surface obvious hardcoded business decisions. The deep audit lives in [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md); this is a fast cross-check. - -```bash -grep -rnE ">=\s*[0-9]{2,}|<=\s*[0-9]{2,}|setTimeout\([^,]+,\s*[0-9]{4,}" srv/ --include="*.ts" --include="*.js" 2>/dev/null \ - | grep -vE "\.test\.|//|^.*\*|HTTP|status:|\.length|substring" \ - | head -20 -``` - -## Step 3: Specialized audits (parallel agents) - -Skip entirely in `quick` mode. Otherwise dispatch the agents below **in parallel** (one message, multiple Agent / Skill invocations). - -Pick agents based on scope: - -| When | Skill / Agent | Purpose | -| --- | --- | --- | -| Always (if CAP) | [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) | Clean Core Level A compliance audit | -| Always (if customizing pattern detected) | [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) | Bidirectional CSV ↔ code audit | -| Always | [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) | OWASP / ASVS / NIST / RBAC matrix | -| Per Fiori app (scope = `ui5` or full) | [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) `<app>` | Per-app UI/UX + frontend/backend contract | -| Optional polish round | [`../sap-cap-text-polish/SKILL.md`](../sap-cap-text-polish/SKILL.md) `dry-run` | User-visible text quality | -| If MCP `sap-btp-best-practices` exists | (project-specific agent) | BTP destination, XSUAA, audit log, resilience | -| Pre-deploy | (project-specific deployment-readiness agent) | mta.yaml / Kyma manifests / Dockerfile / health endpoints | - -Each agent runs **read-only** and reports back ≤500 words. If an agent times out or is unavailable, mark it `TOOL UNAVAILABLE` and move on. - -Detection heuristics for whether to invoke an optional agent: - -```bash -# Customizing pattern detected? -test -f "db/data/$(ls db/data/ 2>/dev/null | grep -iE 'systemparam|setting|config' | head -1)" && echo "customizing detected" - -# Clean Core (Tier-2 S/4 proxies) detected? -grep -lE "@cds\.external|extend service.*S4|cds\.connect\.to\(['\"]S/4" srv/ -r --include="*.cds" --include="*.ts" 2>/dev/null | head -1 - -# BTP deployment artifacts? -test -f mta.yaml || ls k8s/*.yaml 2>/dev/null | head -1 || test -f Dockerfile -``` - -## Step 4: CAP-specific deep checks - -Skip if scope = `ui5`. - -### 4a — CDS model integrity - -Use the `cds-mcp` MCP server if installed: - -``` -mcp__plugin_cds-mcp_cds-mcp__search_model query="list all entities" -mcp__plugin_cds-mcp_cds-mcp__search_model query="list all services" -``` - -Fallback: `npx cds compile srv app --to json > /tmp/model.json` and inspect entities/services from JSON. - -### 4b — Profile / configuration audit - -```bash -# Discover all CDS profiles -grep -nE "\[(production|onprem|development|live|mocked|k8s|hybrid)" .cdsrc*.json package.json 2>/dev/null | head -20 -``` - -For each profile detected, check: -- Does it set `requires.auth`? -- Does it set `requires.db.kind`? -- Are remote bindings referenced (`requires.<name>.kind: odata-v[24]` + `credentials.destination`)? - -### 4c — Build dry-run - -```bash -grep -q '"build"' package.json && npm run build 2>&1 | tail -20 -test -f mta.yaml && which mbt && mbt build --mode=verbose 2>&1 | tail -20 || echo "mbt not available" -``` - -## Step 5: Best-practice cross-check - -Invoke (via Skill tool) any general best-practice skill present in the user's environment to cross-validate Phase 2/3 findings. Examples (only if available — do not invent): - -- A BTP developer-guide skill -- A BTP cloud-logging best-practice skill -- A BTP connectivity / destination skill -- A CAP deployment-checklist skill - -For each, extract the **DO / DON'T** highlights and compare against Phase 2/3 findings. Anything that violates a DO/DON'T is upgraded to `PRINCIPLE MISMATCH` and added to the high-priority section of the final report. - -## Step 6: Optional formal code review - -Run **only if** the current branch is not `main`/`master` and has a non-trivial diff: - -```bash -BR=$(git rev-parse --abbrev-ref HEAD) -test "$BR" != "main" -a "$BR" != "master" && git diff --stat origin/main..."$BR" | tail -1 -``` - -If applicable, invoke a code-review skill / agent (project-specific) with scope "all changes on current branch vs main". Otherwise, write "No active feature branch — code-review skipped". - -## Output — consolidated report - -Always written to `docs/audit/<yyyy-mm-dd>-stack-audit.md`. Final structure: - -```markdown -# SAP Stack Audit — <yyyy-mm-dd> — branch <name> - -## Summary -- Overall grade: **A | B | C | D** (based on count + severity of findings) -- Critical: N -- High: N | Medium: N | Low: N -- Build status: OK / FAIL -- Tests: passed / total - -## Infrastructure -- CDS compile: OK / FAIL -- TypeScript (per scope): … -- Test suite: … -- Hardcoded residue: N findings - -## UI5 / Fiori (per app) -| App | Linter | Manifest | TypeScript | Notes | -| --- | --- | --- | --- | --- | - -## CAP Backend -- Model: … -- Service layer: … -- Performance flags: … - -## BTP / Deploy -- … - -## Critical (P1) -1. … - -## High (P2) -1. … - -## Quick Wins (P3) -- … - -## Sub-audit reports -- Clean Core: [link] -- Customizing: [link] -- Security RBAC: [link] -- Fiori App (per app): [link x N] -- Text Polish: [link] - -## Tools unavailable -- … - -## Raw findings (deduplicated) -[per phase, citable file:line] -``` - -### Severity rollup - -Grade calculation (default; adjust to project's conventions if `CLAUDE.md` specifies): - -| Grade | Trigger | -| --- | --- | -| A | 0 Critical · 0 High · ≤5 Medium | -| B | 0 Critical · ≤2 High | -| C | 0 Critical · 3-5 High | -| D | ≥1 Critical OR ≥6 High | - -### Deduplication - -Two findings are duplicates when **all** of these match: -- Same file path -- Same line range (or overlapping by ±2 lines) -- Same root cause category - -Merge: keep the description with more evidence; cross-link the other audit's reference. - -## BTP vs On-Premise Differences - -| Aspect | BTP | On-Premise | -| --- | --- | --- | -| Test suite | Often `npm test` with mocked profile | May require live S/4 connectivity (`live` profile) | -| Deployment dry-run | `mbt build` or `kubectl apply --dry-run=client` | Less standardized; may need manual smoke test | -| MCP tooling availability | Generally rich | Limited; expect more `TOOL UNAVAILABLE` markers | -| Connectivity check | Destination service + XSUAA | SAP Cloud Connector / Reverse Proxy | - -The orchestrator logic is identical; expect more `TOOL UNAVAILABLE` flags on-premise. - -## Error Handling - -| Symptom | Likely cause | Action | -| --- | --- | --- | -| CDS compile fails in Phase 1 | Schema error | Stop the audit, emit single-finding report | -| Test suite cannot be invoked | No `test` script / unsupported runner | Mark Phase 2d "SKIPPED (no test runner)" and continue | -| Agent times out (Phase 3) | Long-running sub-audit | Mark `TIMEOUT`, attach partial output, continue | -| MCP tool unavailable | Plugin not installed | Mark `TOOL UNAVAILABLE`, fall back to non-MCP method if any | -| Two agents report the same finding | Expected | Deduplicate per the rule above | -| Test suite produces flaky results | Single-writer SQLite, in-memory DB locks | Note in report; do not retry | - -## What This Skill Does NOT Do - -- Does **not** modify any file. -- Does **not** open PRs. -- Does **not** run destructive commands. -- Does **not** redesign architecture (use a design skill). -- Does **not** invent new checks; only composes existing skills/tools. -- Does **not** download remote artifacts beyond what the project already references. - -## When to Use This Skill - -- Before a major release / cut-over. -- When taking over an unfamiliar project, to get a baseline. -- Before a Clean Core / production-readiness review meeting. -- As a sanity check after a large refactor or merge. -- When preparing a status report for stakeholders. - -## When NOT to Use - -- For a single targeted question (use the specific skill directly). -- When the project is mid-broken state — fix the breakage first. -- For continuous CI execution (too expensive; use [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) instead). -- For learning the codebase from scratch (use a code-exploration skill). - -## Follow-up - -- The consolidated report points to each sub-audit's report. Open the relevant one for deep findings. -- Findings tagged `PRINCIPLE MISMATCH` should be triaged first — they violate established best-practice conventions. -- Findings tagged `TOOL UNAVAILABLE` indicate gaps in the local tooling, not in the project. -- Critical (P1) findings should block release; High (P2) should have owners assigned; Medium (P3) belongs in the backlog. - -## Battle-Tested Patterns Referenced - -This skill orchestrates the full audit stack and consults [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) as its **gotcha catalog**. The catalog's eight categories map to the orchestrator's phases: - -- **Category 1 (UI5 / FE V4 traps)** ↔ Phase 2a-2b (UI5 linter, manifest validation) and Phase 3 ([`sap-fiori-app-audit`](../sap-fiori-app-audit/SKILL.md)). -- **Category 2 (CAP / TypeScript pitfalls)** ↔ Phase 2e (TS typecheck), Phase 4 (CAP-specific deep checks). -- **Category 3 (BTP / Kyma deployment)** ↔ Phase 3 deployment-readiness agent. -- **Category 4 (Security defense-in-depth)** ↔ Phase 3 ([`sap-cap-security-rbac-matrix`](../sap-cap-security-rbac-matrix/SKILL.md)). -- **Category 5 (Customizing-driven)** ↔ Phase 3 ([`sap-cap-customizing-honor`](../sap-cap-customizing-honor/SKILL.md)). -- **Category 6 (Lifecycle/process discipline)** ↔ Phase 4 model integrity check. -- **Category 7 (Events/messaging)** ↔ Phase 3 architecture agent. -- **Category 8 (Ecosystem plugin landscape)** ↔ Phase 5 best-practice cross-check. - -Findings from any phase that match a battle-tested pattern should be cross-referenced in the consolidated report — link to the specific pattern by anchor (e.g. "matches battle-tested pattern 1.2 — `@UI.Hidden` on `OperationAvailable`"). - -## Recommended Companion Plugins - -The orchestrator works best when **all relevant companion plugins are installed** because Phase 3 specialized agents and Phase 5 best-practice cross-check rely on them. Adapt to what's available; degrade gracefully (mark `TOOL UNAVAILABLE`) for absent ones. - -| Plugin / Skill | Used in phase | -|---|---| -| `sap-cap-capire` | Phase 4 (model integrity, profile audit) | -| `sapui5` | Phase 2a (UI5 linter), Phase 3 (FE app audit) | -| `sap-fiori-tools` | Phase 2b (manifest validation), Phase 2c (Fiori app discovery) | -| `sap-btp-cloud-platform` | Phase 3 BTP best-practice agent | -| `sap-btp-connectivity` | Phase 3 (XSUAA / destination audit) | -| `sap-btp-cloud-logging` | Phase 3 logging-pattern check | -| `sap-btp-integration-suite` | Phase 3 (if iFlow consumption detected) | -| `sap-btp-audit-log` | Phase 3 audit-logging strategy review | -| `sap-pce-expert` | Phase 4 (if S/4HANA PCE consumed) | -| `sap-docs` | Phase 1 / Phase 3 SAP Notes search | -| `context7` | Phase 2e (non-SAP dependencies) | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map and `sap-btp-*` family entries. - -## References - -- [SAP CAP — Production-Readiness Guide](https://cap.cloud.sap/docs/guides/deployment/) -- [SAP UI5 — Linter](https://github.com/SAP/ui5-linter) -- [SAP Fiori Tools — Manifest Validation](https://help.sap.com/docs/SAP_FIORI_tools) -- [SAP BTP — Reference Architecture](https://help.sap.com/docs/btp/sap-btp-neo-environment/reference-architectures) -- [OWASP — Application Security Verification Standard (ASVS)](https://owasp.org/www-project-application-security-verification-standard/) diff --git a/skills/sap-cap-text-polish/SKILL.md b/skills/sap-cap-text-polish/SKILL.md deleted file mode 100644 index 45f63e26..00000000 --- a/skills/sap-cap-text-polish/SKILL.md +++ /dev/null @@ -1,404 +0,0 @@ ---- -name: sap-cap-text-polish -description: Audit and rewrite all user-visible text across a SAP CAP + Fiori Elements project — backend reject/throw messages, helper rejects, frontend toasts/dialogs/notifications, i18n bundles, CDS labels, CodeList descriptions, and CSV master-data text — to a consistent professional tone. Detects ten anti-patterns (colloquial, accusatory, tech-jargon, fragmented, mixed-language, missing ICU placeholders, PII leak, etc.) and applies only safe additive rewrites on a dedicated branch. Use when asked to "polish UI text", "improve error messages", "review user-visible messages", "fix tone of notifications", "audit i18n bundle", or to raise the language quality of a CAP application before a release. ---- - -# SAP CAP Text Polish - -Audit every piece of **user-visible text** produced by a CAP + Fiori Elements V4 application and rewrite the ones that look unprofessional, unclear, accusatory, or leak technical detail. Output is a structured before/after report; in `fix` mode the skill applies only **safe additive** rewrites (typos, ICU placeholders, missing fallbacks, PII masking, missing i18n keys). Semantic rewrites of legal/policy text and CodeList seed changes are always flagged for human review, never applied. - -The skill is **language-agnostic for detection** but **language-aware for rewriting**: it reads the project's primary locale from `CLAUDE.md`/`AGENTS.md` or from the i18n bundle filename pattern (`i18n_de.properties`, `i18n_it.properties`, ...) and rewrites in the same locale, preserving any non-primary locale variants. - -## v1 Guardrails - -- **Read-only by default.** `fix` is opt-in and limited to additive edits. -- **Never rewrite legal text.** GDPR notices, ToS, consent dialogs are flagged with `LEGAL_REVIEW_REQUIRED`, never edited. -- **Never change semantics.** Polishing is a form-level operation; if the original message is logically wrong, file it as `CONTENT_BUG` for human review. -- **Never rename i18n keys** that are already referenced from CDS annotations. Adding a new key is safe; renaming may break the annotation lookup. -- **PII must be masked** before any user-visible context. The skill detects unmasked IBAN, fiscal codes, VAT numbers, email addresses and either masks them via the project's existing sanitize helper or proposes the helper call as a fix. -- **Cite `file:line`** on every finding. - -## Smart Defaults (apply silently, do NOT ask) - -| Aspect | Default | Why | -| --- | --- | --- | -| Mode | `dry-run` (report only) | Safer; user opts into `fix` | -| Tone profile | `formal` | Enterprise SAP audience; `friendly` opt-in via argument | -| Primary locale | Detected from i18n filename pattern or project doc | Avoid asking; project state is authoritative | -| Output destination | `docs/audit/<yyyy-mm-dd>-text-polish.md` | Committed for traceability | -| Branch name | `audit/text-polish-<scope>-<yyyy-mm-dd>` | One audit per branch | -| Tone profile rules | Impersonal voice · active voice · action-oriented closure · canonical locale · ≤120 char errors · ≤250 char dialog body · ≤80 char toast · first-word + proper-noun capitalization · ICU placeholders always | Codified in this skill | -| i18n key naming | `snake_case` with category prefix (`msg_`, `dlg_`, `act_`, `lbl_`, `tooltip_`, `err_`) | Match common CAP convention | -| Diff cap per file | ≤50 lines | Keep fixes reviewable | - -## Input - -Single argument with format `<scope> [mode] [tone:<profile>]`: - -| Argument | Meaning | -| --- | --- | -| `<scope>` | `all` (default), `<app-name>` (e.g. `manager-ui`), `srv-only` (backend reject/throw), `i18n-only` (only `webapp/i18n/i18n*.properties`) | -| `mode` | `dry-run` (default, no edits) · `fix` (apply safe rewrites on a branch) | -| `tone:formal` | Formal enterprise tone (default) | -| `tone:friendly` | Friendly-but-professional tone (allows one exclamation, slightly warmer wording) | - -Examples: `all`, `manager-ui fix`, `srv-only dry-run tone:friendly`, `i18n-only fix`. - -## Step 1: Discover text sources - -The skill scans **eight categories** of user-visible text. For each category it produces a list of `{file, line, original_text, classification, suggested_fix}` rows. - -### 1a — Backend reject messages - -```bash -grep -rnE "req\.reject\([0-9]+\s*,\s*['\"`]" srv/ --include="*.ts" --include="*.js" -grep -rnE "req\.error\([0-9]+\s*,\s*['\"`]" srv/ --include="*.ts" --include="*.js" -``` - -### 1b — Backend throws with user-visible message - -```bash -grep -rnE "throw new (Error|CdsError|RequestError)\(['\"`][A-Za-z]" srv/ -``` - -Distinguish: -- **Internal-only** (caught by a top-level error handler and translated) → leave alone, but flag the throw site if the message itself is non-professional. -- **User-visible** (escapes to the OData response) → audit. - -### 1c — Centralized reject helper - -```bash -# Discover the project's centralized reject helper (varies by project) -grep -rnE "rejectSafe\(|safeReject\(|rejectWith\(" srv/ --include="*.ts" --include="*.js" | head -5 -# Then scan all call sites -grep -rnE "<discovered-helper>\([^,]+,\s*[0-9]+,\s*['\"`][^'\"`]+['\"`]" srv/ -``` - -### 1d — Frontend notification helpers - -```bash -grep -rnE "_t\([^,]+,\s*['\"]\w+['\"],\s*['\"][^'\"]+['\"]" app/ -grep -rnE "MessageBox\.(show|confirm|error|warning|information)\(['\"`]" app/ -grep -rnE "MessageToast\.show\(['\"`]" app/ -grep -rnE "req\.notify\(['\"`]" srv/ -``` - -### 1e — i18n bundles - -```bash -ls app/*/webapp/i18n/i18n*.properties -ls app/*/i18n/*.properties -``` - -Read each bundle; one row per key. - -### 1f — CDS labels and titles - -```bash -grep -rnE "@title:\s*['\"`]|@Common\.Label:\s*['\"`]|@Core\.Description:\s*['\"`]" db/ srv/ app/ -``` - -Distinguish hardcoded string literals from `{i18n>key}` bindings. Only the former are in scope here. - -### 1g — CodeList descriptions - -```bash -ls db/data/*_texts.csv 2>/dev/null -ls db/data/sap.*-*.csv 2>/dev/null -``` - -For each CodeList CSV with a `descr` or `name` column, list rows where the description matches an anti-pattern. - -### 1h — CSV master-data labels - -```bash -# Discover CSV files with label/description columns -for csv in db/data/*.csv; do - head -1 "$csv" | grep -qE "(name|label|description|descr)" && echo "$csv" -done -``` - -## Step 2: Classify every text - -For every text found, classify it as one of: - -| Classification | Meaning | Action in `fix` mode | -| --- | --- | --- | -| **OK** | Already professional | No change | -| **POLISH** | Minor refinement (punctuation, capitalization, article) | Auto-apply | -| **REWRITE** | Substantial reword needed | Propose, require review (do not auto-apply unless it's a clear typo) | -| **PII_RISK** | Contains sensitive data unmasked | Auto-apply masking helper call | -| **TECH_LEAK** | Technical detail exposed (stack trace, error code raw, SQL error) | Auto-apply mask + downgrade to operational message; log technical via `LOG.error` | -| **LEGAL_REVIEW_REQUIRED** | Privacy / consent / ToS text | Never edit; flag only | -| **CONTENT_BUG** | Message is logically wrong (says A when code does B) | Never edit; flag for human review | - -## Step 3: Detect the ten anti-patterns - -Apply these heuristics to every text. A row may match multiple anti-patterns; classify by the highest severity. - -| # | Anti-pattern | Pattern | Severity | -| --- | --- | --- | --- | -| 1 | **Colloquial / Telegram** | "oops", "uh", "kk", "ok dai", smiley faces in non-marketing contexts | POLISH | -| 2 | **Accusatory tone** | "you did wrong", "you can't", "you must" (second person imperative) | REWRITE | -| 3 | **Unexplained tech jargon** | "404", "null pointer", "SQL error", raw stack class names, server URLs | TECH_LEAK | -| 4 | **Fragmented / incomplete** | "Error." with no context, "Failed", "Invalid" without operand | REWRITE | -| 5 | **All-caps / multiple exclamations** | `[A-Z]{5,}`, `!{2,}`, `\?{2,}` | POLISH | -| 6 | **Unexpanded acronyms** | BP, CC, SDI, FE, PO etc. used without first-occurrence expansion | POLISH | -| 7 | **Mixed locale** | EN words inside an IT/DE/etc. message (e.g. "Errore: invalid input") | REWRITE | -| 8 | **Missing ICU placeholders** | Concatenation with `+` or template literals that should be `{0}` / `{0,number,#,##0.00}` | POLISH (auto-apply) | -| 9 | **Inconsistent punctuation** | Missing final period in complete sentence, redundant punctuation | POLISH | -| 10 | **PII leak** | Unmasked IBAN, fiscal code, VAT number, full email | PII_RISK (auto-apply mask) | - -Detection regex examples: - -```bash -# Anti-pattern 5: all-caps or multiple exclamations -grep -rnE "[A-Z]{5,}|!{2,}|\?{2,}" app/*/webapp/i18n/ - -# Anti-pattern 8: concatenation that should be ICU -grep -rnE "['\"`][^'\"`]+['\"`]\s*\+\s*\w+\s*\+\s*['\"`]" srv/ app/ - -# Anti-pattern 10: PII -grep -rnE "IT[0-9]{2}[A-Z][0-9]{10}[0-9A-Z]{12}" srv/ app/ # IBAN IT -grep -rnE "[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]" srv/ app/ # IT fiscal code -grep -rnE "\b[\w.-]+@[\w.-]+\.\w+\b" srv/ app/ | grep -v "_sanitize\|@example\|@test" -``` - -## Step 4: Rewrite per tone profile - -For each row classified `POLISH` or `REWRITE`, propose a rewrite that: - -1. **Preserves intent.** The user must understand what happened and what to do next. -2. **Applies tone profile.** `formal` is the default: - - Impersonal voice ("The operation cannot be completed") not second person ("You can't do this"). - - Active voice, direct ("Retry in a few minutes"). - - Action-oriented closure where applicable (give a concrete next step). - - First-word capitalization only; locale-canonical punctuation. - - Length caps: ≤120 char errors, ≤250 char dialog body, ≤80 char toast. -3. **Preserves placeholders.** Existing `{0}` / `{0,number,...}` / `{0,date,...}` survive; concatenations are converted to placeholders. -4. **Generates i18n key if missing.** If the text is inline (`_t(view, "key", "fallback")` with key absent from bundle), propose the new key in `snake_case` with category prefix. -5. **Localizes only the primary locale.** Other locales are noted as "needs translation"; the skill does not invent translations. - -### Before / after examples - -Backend error: -```diff -- req.reject(409, 'Stato fattura non valido per questa azione'); -+ req.reject(409, "L'operazione non è disponibile per lo stato corrente della fattura ({0}). Verificare la fase di processo prima di riprovare."); -``` - -Frontend toast: -```diff -- MessageToast.show("Ok!"); -+ MessageToast.show(_t(this, "msg_action_completed", "Operazione completata.")); -``` - -Dialog confirmation: -```diff -- title: "Sei sicuro?", -- message: "Vuoi davvero eliminare?" -+ title: _t(this, "dlg_confirmDelete_title", "Conferma eliminazione"), -+ message: _t(this, "dlg_confirmDelete_msg", "L'eliminazione non è reversibile. Continuare?") -``` - -i18n bundle entry: -```diff -- err_invalid_input=Errore: input invalido!!! -+ err_invalid_input=Il valore inserito non è valido. Controllare il formato e riprovare. -``` - -PII leak fix: -```diff -- LOG.warn(`Notifica fallita per ${user.email}`); -+ LOG.warn(`Notifica fallita per ${_sanitizePII(user.email)}`); -``` - -ICU placeholder fix: -```diff -- MessageToast.show("Hai " + count + " messaggi"); -+ MessageToast.show(_t(this, "msg_unread_count", "Hai {0} messaggi", [count])); -+ # bundle entry: -+ msg_unread_count=Hai {0} messaggi -``` - -## Step 5: Output report - -Write to `docs/audit/<yyyy-mm-dd>-text-polish.md`: - -```markdown -# Text Polish Report — <scope> — <yyyy-mm-dd> - -## Summary -- Total scanned: <N> sources -- OK: <N> | POLISH: <N> | REWRITE: <N> | PII_RISK: <N> | TECH_LEAK: <N> | LEGAL_REVIEW: <N> | CONTENT_BUG: <N> - -## PII / Tech Leak (urgent) -| File:line | Original | Issue | Suggested fix | -| --- | --- | --- | --- | - -## REWRITE (substantial) -| File:line | Original | Rewritten | i18n key | Note | -| --- | --- | --- | --- | --- | - -## POLISH (refinement) -| File:line | Before | After | Type | -| --- | --- | --- | --- | - -## Bundle gaps (i18n) -| Key | Primary locale | Other locales | Used in | -| --- | --- | --- | --- | - -## Stats -- Most polished file: <path> -- Tone consistency score: <0-100>% -- ICU placeholder coverage: <N/M> - -## LEGAL_REVIEW (do not edit) -| File:line | Text | Reason | -| --- | --- | --- | - -## CONTENT_BUG (human review) -| File:line | Text | Suspected logic mismatch | -| --- | --- | --- | -``` - -## Step 6: Safe fix (mode = `fix` only) - -Apply **only** these safe edits: - -✅ Allowed: -- Typo / capitalization fix in i18n bundle. -- Add ICU placeholder where concatenation existed; bundle key gets the new placeholder syntax. -- Mask PII with the project's `_sanitizePII` / `_sanitize*` helper. If no helper exists, propose adding one but do not edit logs yet. -- Add missing fallback in `_t(view, key, fallback)` calls. -- Add missing i18n key (bundle entry creation). -- Add final period where the sentence is complete and missing it. -- Normalize multiple exclamations / question marks down to one. -- Lowercase all-caps non-acronym words. - -❌ Forbidden (always defer to human): -- Substantial REWRITE. -- Rewrite legal / privacy / consent text. -- Rename an i18n key already referenced from CDS annotations. -- Full translation across locales (the skill does primary-locale rewrites only). -- Modify CodeList CSV seed values (downstream migrations / sap-common conventions may depend on them). -- Rewrite stack-trace or technical log strings going to stdout/cloud-logging (those are operator-visible, different audience). -- Touch text inside test fixtures (`*.test.ts`, `__fixtures__/`). - -### Verification after fixes - -```bash -# CSV lint (if project has one) -npm run lint:csv 2>&1 | tail -10 || true - -# CDS compile sanity -npx cds compile srv app > /dev/null && echo "CDS OK" - -# TS typecheck scoped to apps touched -for app in $(git diff --name-only HEAD~1 -- 'app/*/webapp/' | cut -d/ -f1-2 | sort -u); do - test -f "$app/tsconfig.json" && (cd "$app" && npm run ts-typecheck 2>&1 | tail -5) -done - -# Scoped jest if backend reject messages touched -git diff --name-only HEAD~1 -- 'srv/' | grep -E '\.(ts|js)$' | xargs -I {} npx jest --findRelatedTests {} --runInBand 2>&1 | tail -10 -``` - -## Step 7: Commit + branch (mode = `fix` only) - -```bash -git checkout -b "audit/text-polish-<scope>-$(date +%Y-%m-%d)" -git add -A -git commit -m "fix(text-polish): <scope> — <N> messages reformulated [skip ci] - -- <category-A>: <N> fixes -- <category-B>: <N> fixes -- PII masked: <N> sites -- ICU placeholders added: <N> -- Bundle keys added: <N> - -Report: docs/audit/<yyyy-mm-dd>-text-polish.md -" -git push -u origin HEAD -``` - -Then optionally open a PR via `gh pr create`. Never push to `main` directly. - -## BTP vs On-Premise Differences - -| Aspect | BTP (CF / Kyma) | On-Premise | -| --- | --- | --- | -| Operator-visible log target | BTP Cloud Logging / Kyma Loki — JSON structured | NetWeaver SLG1 / file system | -| User-visible message channel | OData fault + UI5 MessageBox | Same | -| PII helper convention | Often `_sanitizePII` co-located with audit logger | Often inline regex, harder to discover | -| i18n bundle path | `webapp/i18n/i18n.properties` | Same | - -The audit logic is identical; only the discovery of the PII helper varies. - -## Error Handling - -| Symptom | Likely cause | Action | -| --- | --- | --- | -| Primary locale cannot be inferred | No `CLAUDE.md` hint and ambiguous bundle filenames | Pick English by default, mark report with "locale assumption: en" | -| PII helper not found | Project lacks centralized sanitize | Flag the gap, propose adding the helper, do not edit logs yet | -| Diff > 50 lines in a single file | Bug in detection or over-eager rewriter | Stop, emit "diff cap exceeded" finding, defer to manual review | -| CSV change requested | Out of scope for safe-fix mode | Flag in report, never auto-apply | -| Test fixture touched | Should never happen | Revert that file's changes, log it as a guardrail violation | - -## What This Skill Does NOT Do - -- Does **not** rewrite legal text (privacy, ToS, consent). -- Does **not** change message semantics. Form-level only. -- Does **not** rename keys already referenced by CDS annotations. -- Does **not** translate across locales (only rewrites the primary locale; non-primary locales are flagged). -- Does **not** modify CodeList CSV seeds. -- Does **not** modify test fixtures. - -## When to Use This Skill - -- Before a release, to raise overall language quality. -- After a translation/localization phase, to verify the primary locale stays consistent. -- After adding a new app, to bring its tone in line with the rest of the suite. -- When a stakeholder complains "the app sounds amateur". -- As a PII safety net before going live with audit logging. - -## When NOT to Use - -- For functional / logic bug fixing (use a code-review skill). -- For full localization to a new language (use a translation tool / professional translator). -- For marketing copy (different audience, different tone profile). -- For real-time / per-keystroke validation messages (separate UX concern, may need product input). - -## Follow-up - -- Pair with [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) — Step 2g of that audit produces a list of hardcoded strings; this skill fixes them. -- Pair with [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) — its PII findings hand off to this skill's PII_RISK class. -- Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to gate the regression of PII leaks and missing ICU placeholders in CI. - -## Battle-Tested Patterns Referenced - -This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). The patterns most relevant to text polish: - -- **1.6 `i18n>{model>keyPath}` dynamic binding does not work** — common reason for blank text findings; flag as REWRITE with controller-side resolution. -- **1.9 `manifest.json` must declare the i18n model explicitly** — bundle gap diagnosis (Step 1e). -- **2.4 `cds.log(...)` everywhere; never `console.*`** — when the polish target includes operator-visible log strings, prefer `cds.log` calls. -- **2.5 Centralized reject helper (`rejectSafe`) — never expose `err.message`** — tied to TECH_LEAK classification (Step 2). -- **4.2 PII sanitization before audit log** — direct source of PII_RISK class; flag missing `_sanitizePII` helper as gap-to-add-helper. - -## Recommended Companion Plugins - -| Plugin / Skill | Why for text polish | -|---|---| -| `sap-cap-capire` | i18n model setup, localized messages, `cds.i18n` runtime reference | -| `sapui5` | `ResourceModel`, `_t` helper, `MessageBox` / `MessageToast` API details | -| `context7` | ICU MessageFormat placeholder syntax (`{0,number,#,##0.00}`, `{0,date,medium}`) | -| `sap-docs` | SAP Fiori Design Guidelines — Writing tone conventions | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. - -## References - -- [SAP UI5 — `ResourceModel` and `_t` Helper Pattern](https://sapui5.hana.ondemand.com/sdk/#/topic/91f217ce374d4dec8a4b08c8e4c0b3a4) -- [ICU MessageFormat — Placeholder Syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages/) -- [SAP CAP — Localized Messages](https://cap.cloud.sap/docs/guides/i18n) -- [SAP Fiori Design Guidelines — Writing](https://experience.sap.com/fiori-design-web/writing/) -- [OWASP — Sensitive Data Exposure](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/) diff --git a/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md b/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md index becfdcac..deeac67c 100644 --- a/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md +++ b/skills/sap-erp-clean-core-refactor/INTEGRATIONS.md @@ -94,7 +94,7 @@ Use this document to: |---|---|---|---|---|---| | 7a — ATC final check | `SAPLint(action="run_atc")` (whole package) | — | — | — | Cumulative regression | | 7b — Unit test full run | `SAPDiagnose(action="run_unit_tests")` (whole package) | — | — | — | All tests including pre-existing | -| 7c — Cross-check against CAP audit | — | [`sap-cap-clean-core-enforce`](../sap-cap-clean-core-enforce/SKILL.md) (other branch) | — | — | Verify BTP-side compliance | +| 7c — Cross-check against CAP audit | — | [`sap-cap-clean-core-enforce`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md) (other branch) | — | — | Verify BTP-side compliance | | 7d — Session learnings | — | **`analyze-chat-session`** | — | — | Propose new skill traps for future runs | ## Coverage assessment @@ -168,6 +168,6 @@ Plugins **NOT** used by this skill (out of scope): - [`./SKILL.md`](./SKILL.md) — main protocol; this document is the integration deep-dive. - [`./SOURCES.md`](./SOURCES.md) — authoritative SAP source catalog (Tier 1-4). -- [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) — broader companion plugin map (Category 8) for the CAP-side toolkit. +- [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md) — broader companion plugin map (Category 8) for the CAP-side toolkit. - [ARC-1 README](https://github.com/marianfoo/arc-1) — full MCP capability reference. - [secondsky/sap-skills](https://github.com/secondsky/sap-skills) — 32-plugin SAP skill catalog. diff --git a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md b/skills/sap-erp-clean-core-refactor/PATTERNS.md similarity index 93% rename from skills/sap-cap-fiori-battle-tested-patterns/SKILL.md rename to skills/sap-erp-clean-core-refactor/PATTERNS.md index e2d79814..b74ac38b 100644 --- a/skills/sap-cap-fiori-battle-tested-patterns/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/PATTERNS.md @@ -1,13 +1,12 @@ ---- -name: sap-cap-fiori-battle-tested-patterns -description: Knowledge base of battle-tested patterns and gotchas for SAP CAP + Fiori Elements V4 + BTP (Cloud Foundry / Kyma) projects — distilled from production deployments. Eight categories covering UI5/FE V4 traps, CAP/TypeScript pitfalls, Kyma deployment lessons, security defense-in-depth, customizing-driven patterns, lifecycle/process discipline, post-commit events/messaging, and the companion-plugin ecosystem landscape. Use when asked to "review CAP best practices", "diagnose Fiori Elements bug", "fix CAP runtime issue", "harden deployment", "review draft behavior", "audit my CAP project", or as a knowledge reference linked from other skills. Not a runnable skill — it is a curated reference catalog. ---- - # SAP CAP + Fiori Elements V4 — Battle-Tested Patterns +> **This is a reference document**, not a runnable skill. It ships alongside [`SKILL.md`](./SKILL.md) inside the `sap-erp-clean-core-refactor` skill so the refactor skill is **self-contained** — it doesn't need an external repository to consult deployment patterns when generating side-by-side scaffolds. +> +> The same content is also published as a standalone reference skill in the companion [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit) repository, where it's discoverable as `sap-cap-fiori-battle-tested-patterns`. If you're working on a CAP project unrelated to ABAP refactoring, install that repo instead. + A reference catalog of patterns and gotchas that have surfaced repeatedly across production SAP CAP + Fiori Elements V4 + BTP deployments. Each entry distills a real-world failure mode into a generic pattern: **symptom** observed by users or operators, **root cause** in the framework / runtime / deployment layer, and a **remedy** that is portable across CAP projects. -This skill is **not a runner** — it never executes commands. It is a curated reference invoked either directly (when the user asks for best practices) or cross-linked from operational skills like [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md), [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md), [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md), and similar. +This document is **not a runner** — it never executes commands. It is a curated reference consulted by [`SKILL.md`](./SKILL.md) during refactor planning (Step 1c target resolution, Step 6b side-by-side scaffold), referenced from [`INTEGRATIONS.md`](./INTEGRATIONS.md) for the companion plugin map, and available for direct reading by humans. Patterns are organized into **eight categories**. Each category lists the most load-bearing patterns first; lighter-weight items follow. Where multiple frameworks expose the same gotcha (e.g. `@UI.Hidden` interaction with `@Core.OperationAvailable`), the entry points to the framework documentation rather than reproducing it. @@ -246,7 +245,7 @@ Add `npm run typecheck:strict`. Promote folders one at a time, lock them in CI w **Root cause.** `cap-js/*` plugins are powerful but each one extends the runtime (e.g. `@cap-js/audit-logging` registers a BTP-emit layer; `@cap-js/change-tracking` hooks every update). Adding them silently leaves the team unaware of the new coupling. -**Remedy.** Maintain a matrix doc (an ADR is sufficient) listing every `@cap-js/*` plugin: `Adopted` / `Deferred` / `Not-applicable`. CI gate verifies that `package.json` ↔ matrix doc match (see [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection) Pattern 4). +**Remedy.** Maintain a matrix doc (an ADR is sufficient) listing every `@cap-js/*` plugin: `Adopted` / `Deferred` / `Not-applicable`. CI gate verifies that `package.json` ↔ matrix doc match (see [`sap-cap-ci-gates-pattern`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection) Pattern 4). --- @@ -284,7 +283,7 @@ The default recommendation for a new project is **BTP Cloud Foundry** — it is **Root cause.** `xs-security.json` declares scopes; CAP handlers must `@(restrict: [{grant: '...', to: '<scope-or-role>'}])` to enforce. Mismatch between xs-security and `services-auth.cds` is silent. -**Remedy.** Treat `xs-security.json` as the single source of truth for scope names. Match every `services-auth.cds` `to:` clause to a declared scope. Add a CI gate (see [`../sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 4) that catches drift. +**Remedy.** Treat `xs-security.json` as the single source of truth for scope names. Match every `services-auth.cds` `to:` clause to a declared scope. Add a CI gate (see [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md#pattern-4--convention--matrix-drift-detection`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md) Pattern 4) that catches drift. #### 3.A.3 — Destination service for S/4HANA Tier-2 proxies @@ -649,7 +648,7 @@ Map `livenessProbe` → `/health/Live`, `readinessProbe` → `/health/Ready`. 3. **The project's compatibility catalog** (e.g. `srv/integration/s4CompatibilityPolicy.js`): the project's *declared* edition × service matrix with `availability[]` and `probeObject` fields. The CI gate verifies this catalog matches sources 1 + 2; any drift is a HARD FAIL (cannot ship). -Use [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) to discover consumption, build the matrix, detect drift. Use [`../sap-cap-ci-gates-pattern/SKILL.md#pattern-3--released-state--api-availability-drift-detection`](../sap-cap-ci-gates-pattern/SKILL.md) Pattern 3 to enforce the gate on every PR. +Use [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md) to discover consumption, build the matrix, detect drift. Use [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md#pattern-3--released-state--api-availability-drift-detection`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md) Pattern 3 to enforce the gate on every PR. Compliance contract: - **Level A** (Released APIs only): every consumed Tier-2 S/4 service is in the released catalog for ALL deployment-target editions. Zero RFC/BAPI/SQL-direct usage. Zero modifications to standard tables. Zero user-exits. @@ -764,7 +763,7 @@ const value = params.MY_KEY // canonical || process.env.MY_KEY // env var fallback || DEFAULT_VALUE; // hardcoded last-resort ``` -Mark the order in code comments. Audit ensures every adapter uses this chain (see [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md)). +Mark the order in code comments. Audit ensures every adapter uses this chain (see [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-customizing-honor/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-customizing-honor/SKILL.md)). ### 5.3 — Per-tenant override pattern (catalog → company override) @@ -791,7 +790,7 @@ Runtime resolution: override → catalog → hardcoded default. **Root cause.** No enforced contract between CSV seed and code consumer. -**Remedy.** A CI gate (see [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md#pattern-1--bidirectional-csv--code-consistency)) that fails the build on **inverse orphans** (code reads, CSV missing) or **forward orphans** (CSV seeds, no consumer). Allowlist for legitimate external consumers. +**Remedy.** A CI gate (see [`sap-cap-ci-gates-pattern`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-ci-gates-pattern/SKILL.md#pattern-1--bidirectional-csv--code-consistency)) that fails the build on **inverse orphans** (code reads, CSV missing) or **forward orphans** (CSV seeds, no consumer). Allowlist for legitimate external consumers. --- @@ -1017,11 +1016,11 @@ When the user asks "what's the best practice for X in CAP / Fiori Elements?", se ### As a cross-link target Other skills in this repository cross-link to specific patterns by anchor: -- [`../sap-fiori-app-audit/SKILL.md`](../sap-fiori-app-audit/SKILL.md) refers to Category 1 for FE V4 traps. -- [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) refers to Category 4 for defense-in-depth. -- [`../sap-cap-customizing-honor/SKILL.md`](../sap-cap-customizing-honor/SKILL.md) refers to Category 5 for customizing patterns. -- [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) refers to Category 3.8 for multi-region deployment. -- [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) acts as orchestrator and consults this catalog when consolidating findings. +- [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-fiori-app-audit/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-fiori-app-audit/SKILL.md) refers to Category 1 for FE V4 traps. +- [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-security-rbac-matrix/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-security-rbac-matrix/SKILL.md) refers to Category 4 for defense-in-depth. +- [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-customizing-honor/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-customizing-honor/SKILL.md) refers to Category 5 for customizing patterns. +- [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md) refers to Category 3.8 for multi-region deployment. +- [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md) acts as orchestrator and consults this catalog when consolidating findings. ### As a teaching index @@ -1030,7 +1029,7 @@ A developer new to CAP + Fiori Elements V4 can read Categories 1 → 8 in order. ## When NOT to use - For project-specific advice that depends on domain semantics (the patterns here are deliberately generic). -- For runnable diagnostics (use the operational skills like [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md)). +- For runnable diagnostics (use the operational skills like [`https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md)). - As a replacement for SAP official documentation — this catalog distills production lessons, but the authoritative spec lives in the SAP help portal. ## Recommended Companion Plugins diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index 4afeddf6..f2134ff0 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -36,7 +36,7 @@ This skill does NOT ship a pre-built KB. It ships: | Cache location | `.cache/sap-clean-core/<topic-hash>/` (gitignored) | Per-project local; not committed | | Output destination | `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md` | Committed; serves as the decision record | | Target Clean Core level | `A` | Most restrictive; works for all cloud targets | -| Side-by-side target | Asked once at session start | Reuses Step 0 of [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | +| Side-by-side target | Asked once at session start | Reuses Step 0 of [`sap-cap-stack-audit-full`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | | Level B handling | `keep_at_level_b` by default (compliant; documented) | B is already Clean-Core compliant. Pushing to A is opt-in via `--aggressive` / `--push-to-a` / `--target-level=A` because escalation adds cost and may be wrong choice (most B objects use SAP-recommended patterns) | | Level C target | A (via released-API rewrite) when equivalent exists, else B (via BAdI substitution), else side-by-side (ERP becomes A) | Cost-minimizing default. Override with `--target-level=B` to settle for B even when A is cheaper | | Level D target | B (via BAdI / enhancement-point rewrite) when feasible, else side-by-side (ERP becomes A) | Cost-minimizing default. D objects usually have business logic that genuinely needs an extension surface; BAdI-rewrite is the SAP-recommended path. Override with `--target-level=A` to prefer side-by-side over BAdI | @@ -92,9 +92,9 @@ If unavailable, the skill degrades to **manual mode**: it produces a plan with e ### 1c — Resolve the side-by-side deployment target -The plan's "extract to BTP" suggestions differ per target (BTP CF vs Kyma vs on-prem). If `--target` wasn't passed, ask the user once (same dialog as [`../sap-cap-stack-audit-full/SKILL.md`](../sap-cap-stack-audit-full/SKILL.md) Step 0). +The plan's "extract to BTP" suggestions differ per target (BTP CF vs Kyma vs on-prem). If `--target` wasn't passed, ask the user once (same dialog as [`sap-cap-stack-audit-full`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md) Step 0). -Record the target as `$TARGET`. The plan's side-by-side suggestions consult the matching section of [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-3--btp--kyma--on-premise-deployment-lessons`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). +Record the target as `$TARGET`. The plan's side-by-side suggestions consult the matching section of [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md). ### 1d — Initialize the local cache @@ -294,7 +294,7 @@ Object starting level = B (Eligible)? - D → goes to B (default via BAdI) OR to A via side-by-side (when BAdI not feasible OR `--target-level=A` is on) OR accept_as_d (last resort with explicit sign-off). - Unused → removed (no level applies). -**Side-by-side outcome — clarification**: when an object is extracted to BTP, the **ERP-side artefact disappears**. The ERP no longer contains the custom code, so by absence the ERP is at Level A for that domain. The logic continues to live on BTP as a CAP extension (not classified by ABAP Clean Core levels — BTP-side compliance is governed by the deployment-target gate, see [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md)). +**Side-by-side outcome — clarification**: when an object is extracted to BTP, the **ERP-side artefact disappears**. The ERP no longer contains the custom code, so by absence the ERP is at Level A for that domain. The logic continues to live on BTP as a CAP extension (not classified by ABAP Clean Core levels — BTP-side compliance is governed by the deployment-target gate, see [`sap-cap-clean-core-enforce`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md)). ### 4e — Level B → Level A escalation (aggressive mode) @@ -500,7 +500,7 @@ For projects with abapGit / gCTS, [`SAPGit`](https://github.com/marianfoo/arc-1) After any execute action: - **ATC regression check** (`SAPLint(run_atc)`): the gate that blocks the execute loop if findings count regresses. - **Unit test regression** (`SAPDiagnose(run_unit_tests)`): runs the tests generated in Step 6-pre PLUS pre-existing tests. Any failure aborts the loop. -- **Cross-check against the audit catalog** used by [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. +- **Cross-check against the audit catalog** used by [`sap-cap-clean-core-enforce`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. - **Compare ATC counts before/after**; any net regression aborts the execute loop and surfaces a finding. - **Optional**: invoke [`../analyze-chat-session/SKILL.md`](../analyze-chat-session/SKILL.md) at session end to capture learnings (which rewrite patterns worked, which findings recurred) and propose new skill traps for the team. @@ -514,7 +514,7 @@ The decision tree is the same; what differs is the **side-by-side target framewo | BTP Kyma | CAP Node.js / TypeScript | PostgreSQL in-cluster or HANA Cloud | BTP Event Mesh or Kyma-native NATS | | On-Premise Kyma | CAP Node.js / TypeScript | HANA on-prem or PostgreSQL on-prem | Kyma-native NATS | -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-3--btp--kyma--on-premise-deployment-lessons`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) Category 3 for per-target deployment patterns. +See [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md) Category 3 for per-target deployment patterns. ## Cost model @@ -574,7 +574,7 @@ For projects with no Apify budget, the skill operates in **manual mode**: it emi ## Battle-Tested Patterns Referenced -This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md): +This skill builds on [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md): - **3.0 Deployment target decision matrix** — selecting the right BTP target for the extension. - **3.A.* / 3.B.* / 3.C.* / 3.D.*** — target-specific deployment patterns. @@ -652,11 +652,18 @@ The plugins below split into **MUST** (the skill is materially less useful witho | [`convert-ui5-to-fiori-elements`](../convert-ui5-to-fiori-elements/SKILL.md) | Step 6b (Fiori Elements UI on top of new CAP service) | | [`analyze-chat-session`](../analyze-chat-session/SKILL.md) | Step 7 (session-end learnings capture) | -See [`./INTEGRATIONS.md`](./INTEGRATIONS.md) for the full step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × secondsky plugin. See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the broader companion plugin map across CAP audit skills. +See [`./INTEGRATIONS.md`](./INTEGRATIONS.md) for the full step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × secondsky plugin. See [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md) for the broader companion plugin map across CAP audit skills. ## See also -- [`./SOURCES.md`](./SOURCES.md) — curated list of authoritative SAP documentation sources consulted by this skill (just the URLs + when-to-use guidance, no crawled content). +The skill ships **four files** so it is fully self-contained: + +- [`./SKILL.md`](./SKILL.md) — this protocol document +- [`./SOURCES.md`](./SOURCES.md) — curated catalog of 23 authoritative SAP documentation sources (the URLs + when-to-use guidance, no crawled content) +- [`./PATTERNS.md`](./PATTERNS.md) — ~70 production-distilled battle-tested patterns in 8 categories (UI5/FE V4 traps, CAP/TS pitfalls, BTP/Kyma/OnPrem deployment, security, customizing, lifecycle, events, ecosystem plugins). Consulted during Step 1c target resolution + Step 6b side-by-side scaffold. Same content is also available as a standalone skill in [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit) for CAP-only audiences +- [`./INTEGRATIONS.md`](./INTEGRATIONS.md) — step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 native skill × [secondsky/sap-skills](https://github.com/secondsky/sap-skills) plugin × external sources + +External cross-links to the CAP audit toolkit are operational ("see also" / coordinate with), not critical dependencies of this skill. ## References diff --git a/skills/sap-fiori-app-audit/SKILL.md b/skills/sap-fiori-app-audit/SKILL.md deleted file mode 100644 index 4078be27..00000000 --- a/skills/sap-fiori-app-audit/SKILL.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -name: sap-fiori-app-audit -description: Operational audit of a single SAP Fiori Elements V4 application — verifies user journey, frontend/backend contract coherence, manifest + annotations + EDMX alignment, i18n coverage, action availability, draft behavior, and applies only safe quick-win fixes on a dedicated branch. Use when asked to "audit Fiori app X", "verify Fiori Elements app", "check the manager/governance/billing app", "review the UI/UX of a Fiori app", "find action gaps in Fiori app", or to validate that a Fiori Elements V4 app is production-ready. ---- - -# SAP Fiori App Audit - -Audit one Fiori Elements V4 app end-to-end. The audit covers the **operational journey** (filter → list → detail → action → refresh) and the **frontend/backend contract** (UI flag → CDS annotation → `@restrict` grant → handler implementation), then optionally applies **safe quick wins** on a dedicated branch. - -The skill targets projects with a typical CAP + Fiori Elements V4 layout: a CAP service on the backend exposing entities and bound actions, and one or more Fiori Elements V4 apps under `app/<app-name>/webapp/` (or `apps/<app-name>/`), with `manifest.json`, `webapp/ext/` extensions, `webapp/i18n/` bundles, and CDS annotations split across `app/annotations*.cds`. - -## v1 Guardrails - -- **Single app per invocation.** `all` is supported but emits N reports (one per app), not a merged one. -- **Read-only by default.** `fix` is opt-in; applies only safe additive UX fixes. -- **Never touch dirty files**, unless they're inside the requested scope and the user has explicitly authorized. -- **Branch-isolated fixes.** Each audit-fix run creates `audit/fiori-<app>-<yyyy-mm-dd>`. -- **Cite `file:line`** on every finding. - -## Smart Defaults (apply silently, do NOT ask) - -| Aspect | Default | Why | -| --- | --- | --- | -| Mode | `report` | Safer default; user opts into `fix` | -| Output language | English (Italian if `CLAUDE.md`/`AGENTS.md` requires it) | Match the project's primary language | -| App-discovery roots | `app/`, `apps/`, `webapp/` | Most common layouts | -| Service introspection | `npx cds compile srv app --service <Service> --to edmx` | Single source of truth for action/property availability at runtime | -| UI5 version | The version declared in `manifest.json:sap.platform.cf` or `manifest.json:sap.ui5.dependencies.minUI5Version` | Don't bump it as a side effect | -| Test scope | Only files touched by the audit-fix branch | Keep run time under 30s | - -## Input - -Single argument with format `<app-name-or-scope> [mode]`: - -| Argument | Meaning | -| --- | --- | -| `<app-name>` | App directory name (e.g. `manager-ui`, `governance`, `billing`). The skill resolves to `app/<app-name>` or `apps/<app-name>` | -| `all` | Iterate every Fiori app found under the app roots | -| `mode` (optional) | `report` (default) · `fix` (apply safe quick wins) · `pending-only` (only verify existing pending findings) | - -Examples: `manager-ui`, `governance fix`, `all pending-only`. - -## Step 1: Pre-flight - -### 1a — Resolve the app - -```bash -# Resolve target directory -for root in app apps; do - test -d "$root/<app-name>" && echo "Found: $root/<app-name>" && break -done -``` - -If the app cannot be resolved, list all apps found and stop. - -### 1b — Git state - -```bash -git status --short -git branch --show-current -git log -1 --oneline -``` - -Classify dirty files: -- **Inside scope** → treat as in-flight work, do not overwrite in `fix` mode. -- **Outside scope** → ignore. - -### 1c — Identify the file map - -For the chosen app, locate: - -| Artifact | Typical location | -| --- | --- | -| Manifest | `app/<app>/webapp/manifest.json` | -| Extensions / custom controllers | `app/<app>/webapp/ext/**/*.{ts,js,xml}` | -| i18n bundles | `app/<app>/webapp/i18n/i18n*.properties` | -| Annotations (global) | `app/annotations.cds` | -| Annotations (per-app) | `app/annotations/<app>.cds` if it exists | -| Backend service | `srv/*Srv.cds` (look for `@(path:'...')` matching the manifest `dataSources` URI) | -| Action handlers | `srv/handlers/*.ts` referenced by the service | -| Auth declarations | `srv/services-auth.cds`, `xs-security.json` | - -### 1d — Pending findings (if registry exists) - -If the project keeps an audit-pending registry (e.g. `docs/audit/APP_AUDIT_PENDING.md`): - -1. Read every entry whose scope matches the chosen app and status is `Pending` or `Recheck`. -2. For each, re-verify the evidence against the current code AND against the **runtime EDMX**: - -```bash -npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx -``` - -Source-level fix without EDMX evidence is `Partially Fixed`, not `Closed`. - -3. Classify outcomes: `Closed`, `Still Open`, `Partially Fixed`, `Superseded` (with reason). - -If `pending-only` mode, stop here and emit the verification table. - -## Step 2: End-to-end user journey - -Walk the canonical journey of the app and audit each station. Document gaps as you go. - -### 2a — Entry point (List Report / landing) - -- Does the manifest declare a landing page with sensible defaults (`navigation.list.detail.routing`, `initialLoad`)? -- Are filter variants persisted (`flexEnabled: true` + `supportedLocales`)? -- Is `liveMode: true` enabled only on **small** datasets (CodeLists, master data)? On large entities it triggers a `$batch` per keystroke — performance regression. - -### 2b — Filter bar and variants - -- For every filter field that points to master data (FK to CodeList / Categories / etc.), is there a `@Common.ValueList` annotation? Free-text input on a master-data field is a P2 finding. -- For required filters, is `@UI.SelectionFields` aligned with backend `@assert.range` constraints? -- Validate `@Common.ValueListWithFixedValues` is **not duplicated** on the same field — MDC will crash with "Invalid property definition". - -### 2c — ObjectPage navigation - -- Is the routing target reachable? `routing.routes` must declare every ObjectPage referenced by the List Report. -- Does the ObjectPage have a meaningful HeaderInfo (Title / TypeName / Description)? Missing HeaderInfo → "Object" generic title. -- For draft-enabled entities (`@odata.draft.enabled: true`): - - Is the active/inactive transition handled in the controller's `before('EDIT', ...)`? - - Are computed flags re-evaluated on draft activation (otherwise stale `Can*` flags persist into the active row)? - -### 2d — Sections and Facets - -- Audit every `@UI.Facets[].$Type` is one of `UI.ReferenceFacet`, `UI.CollectionFacet`, `UI.ReferenceURLFacet`. -- Detect duplicate Facet IDs — Fiori Elements generates auto-Facets for HeaderInfo; custom Facets with the same ID will collide. -- For each table Facet (`UI.LineItem` on a composition target), verify the navigation property is reachable from the parent entity. - -### 2e — Primary and secondary actions - -For every action exposed via `UI.Identification` or `UI.LineItem`: - -| Check | Pattern | -| --- | --- | -| Backend action exists | `grep -nE "action <name>" srv/*Srv.cds` | -| `@Core.OperationAvailable` references a flag | `Can<name>` flag is computed by a handler | -| Flag is NOT `@UI.Hidden` | FE v4 with `autoExpandSelect: true` skips hidden properties in `$select` → flag undefined → button stuck disabled | -| `@restrict` grants the action to expected roles | `srv/services-auth.cds` | -| Handler implementation present | `srv/handlers/*.ts` | - -### 2f — Refresh after action - -- Does the action use `@Common.SideEffects` to refresh affected fields/sections? -- Does the handler return the **full entity** (no `.columns()` filter)? Returning a partial entity breaks edit-mode refresh in FE v4. -- For batched/list actions, are `TargetProperties` and `TargetEntities` declared so FE knows what to invalidate? - -### 2g — Error handling, empty states, i18n - -- Backend errors: action handlers should use a centralized reject helper (e.g. `rejectSafe`) — never leak `err.message` to the client. -- Empty states: does the table have a `noData` text in i18n bundle? -- i18n bundle present in `webapp/i18n/i18n.properties` + at least one localized variant? Hardcoded user-visible text in TS/XML is a P2 finding. -- Manifest declares `i18n` model under `sap.ui5.models` with `bundleName` matching the actual file path? - -### 2h — Responsive / mobile - -- Does the table declare `condensedTableLayout: true`? -- Are column widths or visibility tuned with `@UI.Importance` (`#High` / `#Medium` / `#Low`)? -- For long text columns, is `wrap` or `maxLines` set to avoid overflow? - -## Step 3: Frontend ↔ backend contract chain - -For every primary action exposed by the app, trace the full chain: - -``` -Manifest / annotation / fragment - → flag Can* / CanUse* - → READ enrichment or projection CDS - → @restrict in services-auth.cds - → action handler backend - → SideEffects / refresh UI - → test exists or test missing -``` - -Compile findings into a table: - -| Action | Surface | Flag | Backend grant | Handler | Refresh | Test | Gap | -| --- | --- | --- | --- | --- | --- | --- | --- | - -Common gaps to record: - -1. **UX-positive-false**: UI shows action, backend rejects with 403/409. Always P1 if blocks the only path forward, else P2. -2. **UX-negative-false**: UI hides action that the role would be granted to invoke. -3. **Flag computed with state-only logic, grant is user-aware** (or vice versa). -4. **Company-code / tenant scope mismatch** between UI query, enrichment, and handler. -5. **Draft vs active behavior divergence** for the same action. - -## Step 4: Automated checks (scoped) - -Run only the targets relevant to the scope. - -```bash -# CDS compile and EDMX dump -npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx - -# TypeScript typecheck (if app has tsconfig.json) -test -f "app/<app>/tsconfig.json" && (cd "app/<app>" && npm run ts-typecheck 2>&1 | tail -20) - -# UI5 build (if app has build script) -test -f "app/<app>/package.json" && grep -q '"build"' "app/<app>/package.json" && (cd "app/<app>" && npm run build 2>&1 | tail -10) - -# UI5 linter (if installed) -which @ui5/linter || npx -y @ui5/linter --version -(cd "app/<app>" && npx -y @ui5/linter 2>&1 | tail -20) - -# Manifest validation (via UI5 MCP if available, fallback to JSON Schema) -# mcp__plugin_sapui5_ui5-tooling__run_manifest_validation app/<app>/webapp/manifest.json -``` - -If a tool is not available, record it as "TOOL UNAVAILABLE" and proceed — never silently skip. - -## Step 5: Quick wins (mode = `fix` only) - -A finding is a quick win when **all** of these hold: - -- Local impact, reversible. -- Touches ≤3 application files. -- No DB migration, no contract OData change, no functional policy decision. -- Scoped test/compile available to verify. -- Does not require real SAP/BTP credentials. - -Apply these: - -✅ Allowed: -- Annotation moved into the correct file (per-app vs global). -- `@Core.OperationAvailable` added (referring to an **already-computed** `Can*` flag). -- `@Common.SideEffects` added (when navigation target already exists). -- Fragment custom-handler short dotted path → fully-qualified extension name. -- i18n key added in `webapp/i18n/i18n*.properties` (NOT renamed). -- `@Common.ValueList` added for a master-data field on the filter bar (NOT on the edit form — edit-form value list may require cascading-filter context). -- Manifest binding fix when the target flag already exists. -- Contract test added for an action handler that has none. - -❌ Forbidden: -- Process redesign. -- State machine change. -- Role / SoD change. -- Schema or migration. -- New OData contract that hasn't been agreed. -- Ambiguous security policy decision. -- Wide refactoring. - -## Step 6: Output and verification - -### 6a — Report - -```markdown -# Fiori App Audit — <app> — <yyyy-mm-dd> - -## Pending verification -| ID | Status | Evidence | Action | -| --- | --- | --- | --- | - -## Applied fixes (mode=fix) -| Area | File:line | Description | Test | -| --- | --- | --- | --- | - -## New findings -1. [P1] Title - - File: … - - Impact: … - - Evidence: … - - Required action: … - - Quick win: yes/no - -## PR -- Branch: -- Commit: -- PR URL: - -## Verifications -- CDS compile: PASS/FAIL -- Typecheck/build: PASS/FAIL -- Scoped tests: PASS/FAIL -- Browser check: performed / skipped (reason) - -## Residual risk -- … -``` - -### 6b — Verification - -After fixes: - -```bash -# Recompile -npx cds compile srv app --service <Service> --to edmx > /tmp/<service>.edmx - -# Scoped TS check -test -f "app/<app>/tsconfig.json" && (cd "app/<app>" && npm run ts-typecheck) - -# Scoped tests (only files touched) -npx jest <files> --runInBand -``` - -If the project has a UI dev-server, attempt to start it and verify in a browser **only if the user explicitly requested a visual check** — otherwise record "browser check skipped (not requested)". - -## BTP vs On-Premise Differences - -| Aspect | BTP (CF / Kyma) | On-Premise | -| --- | --- | --- | -| Manifest `sap.platform` | `cf` or `kyma` | typically empty or `abap` | -| Auth claims source | XSUAA / IAS | Keycloak / NetWeaver IdP | -| Approuter path rewriting | Required (CSRF, CORS) | Often absent (same-origin) | -| FLP shell | Standalone with `init.ts` registering `ShellUIService` | Real FLP shell from on-prem launchpad | -| UI5 version source | `https://ui5.sap.com/<version>/` | `/sap/public/bc/ui5_ui5/` | - -The audit logic is identical; expect different manifest sap.platform values and slightly different bootstrap. - -## Error Handling - -| Symptom | Likely cause | Action | -| --- | --- | --- | -| App directory not resolved | Wrong scope name | Print discovered apps and stop | -| `npx cds compile` fails | Schema error or missing model | Don't proceed with EDMX-dependent checks; record P1 and stop | -| Tests fail before fixes | Pre-existing breakage | Record baseline, don't attempt to fix | -| Tests fail after fixes | Audit introduced a regression | Revert the offending fix, escalate to manual review | -| Manifest validation skipped (MCP unavailable) | Tooling not installed | Fall back to JSON Schema check | - -## What This Skill Does NOT Do - -- Does **not** redesign the user journey. -- Does **not** change the state machine. -- Does **not** modify backend grants or auth boundaries. -- Does **not** modify CodeList CSV seeds. -- Does **not** translate or rewrite user-facing text (use `sap-cap-text-polish`). -- Does **not** audit the role/scope layer in depth (use `sap-cap-security-rbac-matrix`). -- Does **not** run the full repository test suite. - -## When to Use This Skill - -- Before merging a Fiori app PR. -- When a user reports "button is missing" or "button does nothing". -- After a UI5 version bump, to verify nothing regressed. -- Before quarterly releases, as a regression-prevention audit. -- When validating pending audit findings (`pending-only` mode). - -## When NOT to Use - -- For greenfield design of a new Fiori app (use `convert-ui5-to-fiori-elements` or design skills). -- For backend-only changes (use a CAP code review skill). -- For pure performance tuning (different skill). -- For cross-app navigation strategy review — this is single-app focused. - -## Follow-up - -- Pair with [`../sap-cap-security-rbac-matrix/SKILL.md`](../sap-cap-security-rbac-matrix/SKILL.md) to validate role/scope layer surfaced by Step 3. -- Pair with [`../sap-cap-text-polish/SKILL.md`](../sap-cap-text-polish/SKILL.md) to clean up i18n gaps surfaced by Step 2g. -- Pair with [`../sap-cap-clean-core-enforce/SKILL.md`](../sap-cap-clean-core-enforce/SKILL.md) when the app consumes Tier-2 S/4 proxies. -- Pair with [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) when the audit reveals patterns that should be migrated to Fiori Elements V4. -- Pair with [`../sap-cap-ci-gates-pattern/SKILL.md`](../sap-cap-ci-gates-pattern/SKILL.md) to add CI gates that prevent regressions of the high-severity findings. - -## Battle-Tested Patterns Referenced - -This skill builds on [`../sap-cap-fiori-battle-tested-patterns/SKILL.md`](../sap-cap-fiori-battle-tested-patterns/SKILL.md). The patterns most relevant to a Fiori app audit: - -- **1.1 Pin the UI5 minor version explicitly** — Step 2a `manifest.json` check; missing pin is a P2. -- **1.2 `@UI.Hidden` on `@Core.OperationAvailable` operand silently disables buttons** — Step 2e common UX-positive-false root cause. -- **1.3 Actions returning `Entity` must NOT use `.columns(...)`** — Step 2f refresh after action. -- **1.4 `liveMode: true` triggers `$batch` per keystroke on large entities** — Step 2a/2b filter audit. -- **1.5 Composition vs Association for audit/log child entities** — Step 2c draft behavior check. -- **1.6 `i18n>{model>keyPath}` dynamic binding does not work** — Step 2g i18n coverage. -- **1.7 Custom Facets must not collide with HeaderInfo auto-Facet IDs** — Step 2d Facets audit. -- **1.8 `@Common.ValueListWithFixedValues` duplicated crashes MDC** — Step 2b filter bar audit. -- **1.9 `manifest.json` must declare the i18n model explicitly** — Step 2g. -- **1.10 `@odata.draft.enabled` master data: per-case decision** — Step 2c draft audit per entity. -- **1.11 Status fields: use `sap.common.CodeList` not `String enum`** — Step 2b for filterable status columns. -- **3.10 `cds-plugin-ui5` is dev-only; ship UI separately** — Step 4 build verification. - -## Recommended Companion Plugins - -| Plugin / Skill | Why for Fiori app audits | -|---|---| -| `sapui5` | UI5 API explorer — verify control APIs, events, properties when validating annotations | -| `sap-fiori-tools` | Manifest validation MCP tool, Fiori app discovery, page-template alignment | -| `sap-cap-capire` | CAP service annotations reference (`@cds.redirection.target`, `@Common.SideEffects`) | -| `sap-docs` | Search SAP Notes for FE V4 / UI5 issues that match the audit findings | -| `playwright` | Browser smoke test for the "did the fix actually work in the UI" verification step | -| `context7` | Look up non-SAP libraries used in `webapp/ext/` (npm packages, build tools) | - -See [`../sap-cap-fiori-battle-tested-patterns/SKILL.md#category-8--ecosystem-plugin-landscape`](../sap-cap-fiori-battle-tested-patterns/SKILL.md) for the full companion plugin map. - -## References - -- [SAP Fiori Elements V4 — Guidance](https://sapui5.hana.ondemand.com/sdk/#/topic/03265b0408e2432c9571d6b3feb6b1fd) -- [SAP CAP — Fiori Service Annotations](https://cap.cloud.sap/docs/advanced/fiori) -- [OData V4 — Common Annotations](http://docs.oasis-open.org/odata/odata-vocabularies/v4.0/cs01/vocabularies/Org.OData.Capabilities.V1.md) -- [SAP UI5 — `flexEnabled` and Variant Management](https://sapui5.hana.ondemand.com/sdk/#/topic/465f01dcf1cd49b08230e7d3b53b29ed) -- [SAP UI5 — `@Core.OperationAvailable` and Action Visibility](https://sapui5.hana.ondemand.com/sdk/#/topic/cbcb1f3b9c6b4d24aaf5db9447eafa92) From 36d9289a03bebe542af47b2b5662db68cb57db83 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 18:15:54 +0200 Subject: [PATCH 17/18] chore(docs): remove residual NOVA project name from generic examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces project-specific names that had leaked into generic skill documentation with neutral terminology: - `NOVAEvents` declarative service → `BusinessEvents` (in PATTERNS.md Category 7 declarative event service pattern + connection cache example) - `emitNovaEvent(...)` helper → `emitBusinessEvent(...)` - "the target NOVA-style project" → "the target project" (in modernize-abap-to-btp-cap Step describing CI gate detection) - Prometheus metric names `nova_events_emit_total` / `nova_events_replay_total` → `business_events_emit_total` / `business_events_replay_total` (in outbox replay mirror pattern) The skills are designed to be 100% generic. Project-specific names shouldn't appear in examples even when they reflect production patterns — pick neutral domain words instead. Verified clean: grep -rniE 'nova' returns no matches in the refactor skill directory or modernize-abap-* skills. [skip ci] --- skills/modernize-abap-to-btp-cap/SKILL.md | 2 +- skills/sap-erp-clean-core-refactor/PATTERNS.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/skills/modernize-abap-to-btp-cap/SKILL.md b/skills/modernize-abap-to-btp-cap/SKILL.md index 5509eec7..b18a39f6 100644 --- a/skills/modernize-abap-to-btp-cap/SKILL.md +++ b/skills/modernize-abap-to-btp-cap/SKILL.md @@ -237,7 +237,7 @@ npx cds compile srv --service all --to edmx > /dev/null npx cds compile db/schema.cds --to sql > /dev/null ``` -If the target NOVA-style project has `scripts/ci/check-s4-compat-coverage.sh` (or equivalent Clean Core CI gate), invoke it. +If the target project has `scripts/ci/check-s4-compat-coverage.sh` (or equivalent Clean Core CI gate), invoke it. Emit final summary report at `<target>/.target-cap-staging/docs/migration-summary.md`: diff --git a/skills/sap-erp-clean-core-refactor/PATTERNS.md b/skills/sap-erp-clean-core-refactor/PATTERNS.md index b74ac38b..96c36b7d 100644 --- a/skills/sap-erp-clean-core-refactor/PATTERNS.md +++ b/skills/sap-erp-clean-core-refactor/PATTERNS.md @@ -881,12 +881,12 @@ This way the audit log captures the pre-dispatch state. **Remedy.** Declare events in a service: ```cds -service NOVAEvents @(path: '/odata/v4/events') { +service BusinessEvents @(path: '/odata/v4/events') { event InvoiceApproved : { eDocumentGuid: UUID; companyCode: String; approver: String; approvedAt: Timestamp }; // … } ``` -Emit via `cds.connect.to('NOVAEvents').emit('InvoiceApproved', payload)`. AsyncAPI auto-generated from the declaration. +Emit via `cds.connect.to('BusinessEvents').emit('InvoiceApproved', payload)`. AsyncAPI auto-generated from the declaration. ### 7.2 — Post-commit fire-and-forget emit @@ -897,7 +897,7 @@ Emit via `cds.connect.to('NOVAEvents').emit('InvoiceApproved', payload)`. AsyncA **Remedy.** Emit from `req.on('succeeded')`: ```javascript req.on('succeeded', async () => { - try { await emitNovaEvent('InvoiceApproved', payload); } + try { await emitBusinessEvent('InvoiceApproved', payload); } catch (err) { LOG.warn({ err }, 'emit failed'); } }); ``` @@ -913,7 +913,7 @@ The user-facing tx commits; emit failure logs but doesn't propagate. ```typescript let cached: Promise<Service> | null = null; function getEvents(): Promise<Service> { - if (!cached) cached = cds.connect.to('NOVAEvents'); + if (!cached) cached = cds.connect.to('BusinessEvents'); return cached; } ``` @@ -933,7 +933,7 @@ First call starts the connect; all concurrent calls await the same promise. **Root cause.** No replay path for emissions that failed silently. -**Remedy.** Mirror emit events into `cds.outbox.Messages` (or a project-specific table). A periodic job re-emits unacknowledged entries. Distinguish first-emit from replay-emit in the metrics (`nova_events_emit_total` vs `nova_events_replay_total`). +**Remedy.** Mirror emit events into `cds.outbox.Messages` (or a project-specific table). A periodic job re-emits unacknowledged entries. Distinguish first-emit from replay-emit in the metrics (`business_events_emit_total` vs `business_events_replay_total`). --- From 931586a3dcb63bd20f7ce73212eb79921fdfeab0 Mon Sep 17 00:00:00 2001 From: Gabriele Rendina <gabriele@MacBook-Neo-di-Gabriele.fritz.box> Date: Wed, 13 May 2026 20:44:14 +0200 Subject: [PATCH 18/18] =?UTF-8?q?docs(skills):=20simplify=204=20SKILL.md?= =?UTF-8?q?=20=E2=80=94=20cut=20verbosity=20from=201901=20to=20547=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Significant simplification of the 4 protocol files in the refactor PR. The original SKILL.md files were process-heavy with deep step-by-step pseudo-code that read like a NASA launch checklist. They worked but were intimidating to humans and largely redundant with the agent's own ability to infer steps from the structured tables. Strategy: - Lead with a 1-line description + a 3-row "what's in here" table. - Replace verbose step prose with compact tables (decision trees, type mappings, flag → effect, caller pattern → service shape). - Move every implementation detail that was duplicated across sections to a single canonical place (the table). - Keep all critical contracts: the per-object decision tree, the 3 modes, the ARC-1 MCP call patterns, the cross-skill delegation chain, the cost model, the gates. - Remove repeated "Why this matters" prose — the user landing here already knows. Per-file deltas: | Skill | Before | After | Δ | |-----------------------------------|--------|-------|--------| | sap-erp-clean-core-refactor | 675 | 161 | -76% | | modernize-abap-to-btp-cap | 362 | 118 | -67% | | modernize-abap-cap-schema | 375 | 117 | -69% | | modernize-abap-cap-service | 489 | 151 | -69% | | TOTAL SKILL.md | 1901 | 547 | -71% | Detail kept in reference docs (unchanged): - SOURCES.md (84 lines) — 23-source catalog - PATTERNS.md (1065 lines) — 70 battle-tested patterns - INTEGRATIONS.md (173 lines) — MCP × skill × secondsky mapping The agent reads the lean SKILL.md for the protocol, then consults the reference docs only when needed for a specific lookup. Same end-to-end behavior, much easier to navigate. [skip ci] --- skills/modernize-abap-cap-schema/SKILL.md | 408 ++--------- skills/modernize-abap-cap-service/SKILL.md | 534 +++------------ skills/modernize-abap-to-btp-cap/SKILL.md | 392 ++--------- skills/sap-erp-clean-core-refactor/SKILL.md | 722 +++----------------- 4 files changed, 351 insertions(+), 1705 deletions(-) diff --git a/skills/modernize-abap-cap-schema/SKILL.md b/skills/modernize-abap-cap-schema/SKILL.md index 5a068c4b..4c550491 100644 --- a/skills/modernize-abap-cap-schema/SKILL.md +++ b/skills/modernize-abap-cap-schema/SKILL.md @@ -1,375 +1,117 @@ --- name: modernize-abap-cap-schema -description: Generate a CAP CDS data model (db/schema.cds) from a Z* package's ABAP database tables (TABL). Applies DDIC→CDS type mapping (24 types), infers Associations/Compositions from foreign-key references, auto-applies cuid/managed aspects when applicable. Use when asked to "convert this Z table to CDS entity", "generate CAP schema from ABAP TABL", "reverse-engineer Z tables to CDS", or as sub-skill of modernize-abap-to-btp-cap. +description: Generate a CAP CDS data model (`db/schema.cds`) from a Z* package's ABAP database tables. Maps DDIC types to CDS, infers Associations/Compositions from foreign keys, auto-applies `cuid` / `managed` aspects. Use when asked to "convert Z tables to CDS entities", "generate CAP schema from ABAP TABL", "reverse-engineer Z tables", or as sub-skill of `modernize-abap-to-btp-cap`. --- -# Modernize ABAP CAP Schema +# Modernize ABAP → CAP Schema -Generate a CAP CDS data model (`db/schema.cds`) from a Z* package's database tables (`TABL`) and structures. Maps DDIC types to CDS types, infers associations from foreign-key references, applies `cuid` / `managed` aspects automatically, and emits a single namespace-scoped schema ready for `cds compile`. +Produces `<target>/db/schema.cds` from a Z* package's tables (`TABL`). Greenfield CDS for the **CAP runtime** (`@sap/cds`), NOT ABAP CDS DDL. -Sub-skill of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) but also usable standalone when only data-model migration is needed (e.g., reverse-engineer a Z table inventory into CAP CDS for a fresh project). - -This is greenfield CDS *for CAP runtime* (`@sap/cds`) — NOT ABAP CDS DDL. The two share syntax fragments but have different semantics, type systems, and tooling. - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Output file | `<target>/db/schema.cds` | CAP convention | -| Namespace | derived from package: `com.example.<package_lower>` | Customer-namespace by default; user can override | -| Aspect: cuid | Auto-applied when primary key is `sysuuid_x16` (RAW(16)) | RAP convention; cleanest mapping to CAP `cuid` | -| Aspect: managed | Auto-applied when table has any of `crusr` / `crdat` / `cruzt` / `chusr` / `chdat` / `chuzt` fields | Standard ABAP timestamp / user audit columns | -| Aspect: temporal | NOT auto-applied | Temporal modeling rarely 1:1 from ABAP; flag for user review | -| Default key | First field with key flag set in DDIC | DDIC primary-key inheritance | -| Type mapping | DDIC → CDS table (see Step 3) | Lossless where possible; documented where ambiguous | -| Associations | Inferred from foreign-key references (`KEY` table relations) | Composition vs Association heuristic in Step 4 | -| Currency / Quantity reference | `@Semantics.amount.currencyCode` / `@Semantics.quantity.unitOfMeasure` | Required by CAP runtime for proper formatting | -| Comments | Preserved from DDIC table short text + field labels | `@Common.Label: '<text>'` annotation | -| Output format | Pretty-printed, 2-space indent | CAP standard | +Sub-skill of [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). Also usable standalone for table-only reverse-engineering. ## Input -The user provides: - -- **Z package name** (required) — e.g., `ZSALES_PKG` -- **Target directory** (required) — where `db/schema.cds` will be written - -Optionally: - -- **Namespace override** — e.g., `com.acme.sales` (overrides default) -- **Object scope** — comma list of TABL names to include (default: all in package) -- **Skip aspects** — disable `cuid`/`managed` auto-application -- **No-associations** — emit flat entities without inferred Associations - -## Step 1: Enumerate Database Tables - -### 1a. List TABL objects in package - ``` -SAPRead(type="DEVC", name="<package>") +<Z-package> <target-dir> [--namespace=com.example.foo] ``` -Filter results to keep only `objectType=TABL` entries. Sort by name. - -For very large packages, partition by name pattern if user provides one (e.g., `ZSALES_TBL*` for header tables only). - -### 1b. Skip non-transparent table types - -Read the DDIC `tabClass` for each candidate; keep only: - -- `TRANSP` — Transparent table (most common, maps directly to CDS entity) -- `POOL` / `CLUSTER` — Skip with warning (pooled/cluster tables don't map cleanly to CDS — recommend redesign) -- `VIEW` — Skip (treat as DDLS via `modernize-abap-clean-core-gap`) -- `STRU` / `INTTAB` — Include but emit as `type` (not `entity`) - -### 1c. Read each table - -For each TABL retained: - -``` -SAPRead(type="TABL", name="<table_name>") -``` - -The response includes field list with: `name`, `dataElement`, `domain`, `dataType`, `length`, `decimals`, `key`, `notNull`, `checkTable` (foreign-key target), `searchHelp`, `shortText`. - -## Step 2: Read Domain + Data Element Details (where needed) - -For fields where the DDIC type is custom (`Z*_DTE` / `Z*_DOM`): - -``` -SAPRead(type="DTEL", name="<data_element>") -SAPRead(type="DOMA", name="<domain>") -``` - -Extract: - -- `dataType` (numeric, character, decimal) -- `length` and `decimals` -- `fixedValues` (for domains with value list — useful for CDS `enum` generation) -- `conversionExit` (e.g., `MATN1`, `ALPHA` — flag in comment, NOT auto-applied because CAP doesn't have built-in conversion exits) - -Cache results — same domain often referenced from many tables. - -## Step 3: DDIC → CDS Type Mapping - -Apply the standard DDIC → CDS type table. **Implement these as canonical mapping; do NOT improvise**: - -| DDIC type | DDIC length | CDS type | Notes | -|---|---|---|---| -| `CHAR` | 1..N | `String(N)` | If N = 1 and domain is boolean-like (`XFELD`, `BOOLE_D`), map to `Boolean` | -| `NUMC` | 1..N | `String(N)` | NOT `Integer` — preserve leading zeros via String | -| `DEC` | N,M | `Decimal(N, M)` | Both precision + scale preserved | -| `INT1` | 1 | `Integer` (8-bit) | CDS doesn't have 8-bit; use `Integer` and `@assert.range: [0, 255]` | -| `INT2` | 2 | `Integer` (16-bit) | `Integer` + `@assert.range: [-32768, 32767]` | -| `INT4` | 4 | `Integer` | Direct map | -| `INT8` | 8 | `Integer64` | CAP-native | -| `FLTP` | 8 | `Double` | IEEE-754 | -| `DATS` | 8 | `Date` | YYYYMMDD source; CAP normalizes | -| `TIMS` | 6 | `Time` | HHMMSS source | -| `TZNTSTMPS` | 14 | `Timestamp` | UTC timestamp | -| `TZNTSTMPL` | 21 | `Timestamp` | Long timestamp with milliseconds | -| `UTCLONG` | 8 | `Timestamp` | New ABAP type; map directly | -| `CURR` | N,M | `Decimal(N, M) @Semantics.amount.amount` | Must have `currencyCode` reference field | -| `QUAN` | N,M | `Decimal(N, M) @Semantics.quantity.value` | Must have `unitOfMeasure` reference field | -| `LANG` | 1 | `String(2)` | Language key (ISO 639-1 in CAP) | -| `RAW` | N | `Binary(N)` | Direct map | -| `RAWSTRING` | unlimited | `LargeBinary` | Streaming blob | -| `LRAW` | N | `LargeBinary` | Same as `RAWSTRING` | -| `STRING` | unlimited | `LargeString` | Streaming text | -| `LCHR` | N | `LargeString` | Same as `STRING` | -| `CUKY` | 5 | `String(5)` + `@Semantics.amount.currencyCode` if referenced from a CURR field | Currency code | -| `UNIT` | 3 | `String(3)` + `@Semantics.quantity.unitOfMeasure` if referenced from a QUAN field | Unit of measure | -| `CLNT` | 3 | OMIT | CAP is single-tenant per service; `mandt` should NOT appear in target | -| `MANDT` | 3 | OMIT | Same as CLNT | -| `XSEQUENCE` | N | `Binary(N)` | Numbering sequence | -| `XFELD` | 1 | `Boolean` | Boolean flag (X = true, ' ' = false) | -| `BOOLE_D` | 1 | `Boolean` | Boolean data element | -| `SYSUUID_X16` | 16 | (key only) → triggers `cuid` aspect | Special: don't emit field, apply `cuid` | - -### Edge cases - -- **Multi-currency tables** — a table with two `CURR` fields needs two `CUKY` references; preserve both -- **Computed fields** (`KEYFLAG = 'X'` for header amount totals) — flag as virtual: `virtual amount: Decimal(15, 2);` -- **Append structures** — read separately via `SAPRead(type="TABL", name="<APPEND>")` and merge fields into target entity -- **Include structures** — same as append but with `INCLUDE` keyword in DDIC; merge fields inline - -## Step 4: Infer Associations - -For each field with `checkTable` set (foreign-key reference): - -### 4a. Determine cardinality - -- **1:N** — checkTable is parent → emit `Association to one <ParentEntity>` on child with FK field name -- **N:1** — fields on the target table reference back → emit `Composition of many <ChildEntity>` on parent - -Heuristic: - -- If field is part of primary key AND the source table has additional non-key fields → composition (child) -- If checkTable points to a customizing table (`T*` namespace, customizing tab class) → Association -- If checkTable is in same package → Composition unless explicitly modeled otherwise - -### 4b. Emit association definition - -```cds -entity Order : cuid, managed { - customer : Association to one Customer on customer.ID = customerId; - customerId : String(10); - items : Composition of many OrderItem on items.parent = $self; - totalAmount : Decimal(15, 2) @Semantics.amount.amount; - currency : String(5) @Semantics.amount.currencyCode; -} -``` - -### 4c. Flag ambiguous mappings - -If the FK relationship is unclear (e.g., shared key fields with no `checkTable` declared), emit a CDS comment: - -```cds -// REVIEW: foreign-key relationship implied but not declared in DDIC. -// Source table: ZSALES_ITEM target hint: ZSALES_HEADER -customerId : String(10); -``` - -## Step 5: Apply Aspects (cuid / managed / temporal) - -### 5a. `cuid` aspect - -If the table has a single primary key of type `SYSUUID_X16`: - -- Remove the primary-key field from the entity body -- Add `: cuid` after the entity name - -```cds -entity Customer : cuid { - name : String(80); - // ID is provided by cuid aspect -} -``` +## Defaults -### 5b. `managed` aspect +| Aspect | Default | +|---|---| +| Output | `<target>/db/schema.cds` | +| Namespace | derived from package: `com.example.<package_lower>` | +| `cuid` aspect | auto-applied if PK is `sysuuid_x16` (RAW(16)) | +| `managed` aspect | auto-applied if table has any of `crusr` / `crdat` / `cruzt` / `chusr` / `chdat` / `chuzt` | +| `temporal` aspect | NOT auto-applied (rare 1:1 mapping; flag for user review) | +| Key | first DDIC key field | +| Currency / Quantity | `@Semantics.amount.currencyCode` / `@Semantics.quantity.unitOfMeasure` | +| Comments | DDIC short text + field labels → `@Common.Label` | -If the table has timestamp / user audit fields (`crusr`/`crdat`/`cruzt`/`chusr`/`chdat`/`chuzt`): +## DDIC → CDS type mapping (24 mappings) -- Remove those fields from entity body -- Add `, managed` after `cuid` (or directly if no `cuid`) - -```cds -entity Customer : cuid, managed { - name : String(80); - // createdAt, createdBy, modifiedAt, modifiedBy provided by managed aspect -} -``` - -### 5c. Skip temporal aspect - -CAP `temporal` aspect requires `validFrom` / `validTo` with specific semantics; do NOT auto-apply even if such fields exist. Flag for user review: - -```cds -entity ContractVersion { - // REVIEW: this table has validFrom/validTo — consider applying ': cuid, managed, temporal' if business semantics match. - ... -} -``` - -## Step 6: Generate db/schema.cds - -Emit a single file with structure: - -```cds -namespace <namespace>; +| DDIC | CDS | Notes | +|---|---|---| +| CHAR(n) | `String(n)` | preserve length | +| NUMC(n) | `String(n)` | leading-zero numeric; CAP-side validation up to user | +| DEC(p,s) / CURR(p,s) / QUAN(p,s) | `Decimal(p,s)` | currency/quantity get semantic annotation | +| INT1 / INT2 / INT4 | `Integer` / `Int16` / `Int32` | | +| INT8 | `Int64` | | +| FLTP | `Double` | | +| RAW(n) / SSTRING / STRING | `Binary(n)` / `String` / `LargeString` | | +| RAW(16) | `UUID` (auto via `cuid`) | UUID convention | +| RAWSTRING | `LargeBinary` | | +| DATS | `Date` | | +| TIMS | `Time` | | +| TZNTSTMPS | `Timestamp` | with timezone | +| LCHR / LRAW | `LargeString` / `LargeBinary` | | +| UNIT (CUKY) | `Association to Currencies` | when associated with amount field | +| MEINS | `Association to Units` | when associated with quantity field | +| LANG (SPRAS) | `Association to Languages` | | +| CLNT | `String(3)` | client field — usually omitted in BTP CAP | -using { sap.common.CodeList } from '@sap/cds/common'; -using { cuid, managed, temporal } from '@sap/cds/common'; +## Workflow -@Common.Label: '<package short text>' -entity <Entity1> : cuid, managed { - // fields - // associations -} +### Step 1 — Enumerate tables -@Common.Label: '<table short text>' -entity <Entity2> : cuid { - // fields -} +`SAPSearch(tadir_lookup, devclass=<pkg>, object='TABL')` → list of Z tables. -// REVIEW: <flagged items> -``` +### Step 2 — Read DDIC details (per table) -### Naming +`SAPRead(type='TABL', name=<table>)` — fields + types + keys + foreign keys + domain references. -- DDIC table `ZSALES_ORDER_H` → CDS entity `Order` (drop `Z`, drop `_H` suffix, CamelCase) -- DDIC field `KUNNR` → CDS field `customer` (use ABAP semantic name if `dataElement` provides one) OR keep ABAP name lower-cased: `kunnr` -- Default heuristic: if `dataElement` matches a known SAP standard (`KUNNR` → customer, `MATNR` → material, `BUKRS` → companyCode), apply standard name; otherwise lower-case ABAP field name +For complex domains / data elements: drill in only when the DDIC type alone is insufficient (e.g. fixed value lists → `@assert.range`). -Track ABAP → CDS naming map in a comment at the top of the file: +### Step 3 — Apply type mapping + aspects -```cds -// Field naming map (ABAP source → CDS target): -// ZSALES_ORDER_H.KUNNR → Order.customer -// ZSALES_ORDER_H.MATNR → Order.material -// ZSALES_ORDER_H.WAERS → Order.currency -// ZSALES_ORDER_H.NETWR → Order.netAmount -``` +Walk each field through the table above. For each table, decide aspects: +- `cuid` if RAW(16) primary key +- `managed` if audit columns present +- `localized` if `texts` companion table detected (`<table>T`) -## Step 7: Validate +### Step 4 — Infer Associations -### 7a. CDS compile +From DDIC foreign-key references: -```bash -npx cds compile <target>/db/schema.cds --to edmx > /dev/null -``` - -Must succeed. Common errors: - -| Error | Cause | Fix | +| FK cardinality | Heuristic | CAP relation | |---|---|---| -| `Cannot resolve association target` | FK reference points to a table not in scope | Add the parent table to package scope or remove the association | -| `Type 'X' is not supported` | DDIC type without mapping | Add missing entry in Step 3 table, regenerate | -| `Duplicate field name` | Append structure collision | Manual review; rename or merge fields | -| `Currency reference missing` | CURR field without CUKY companion | Add `@Semantics.amount.currencyCode` reference | -| `Namespace conflict` | Namespace mismatches between schema and using-imports | Verify `namespace` header matches CAP namespace conventions | - -### 7b. CDS-to-SQL generation - -```bash -npx cds compile <target>/db/schema.cds --to sql > /dev/null -``` - -Validates that the schema can be deployed to an actual database (HANA + SQLite both). - -### 7c. Lint (optional) - -If the target project has CAP eslint configured: +| Child table with parent FK + parent owns lifecycle | parent has stronger semantics | `Composition of many <Child>` on parent | +| Lookup / master data (Currencies, Units, Languages) | shared catalog | `Association to <Master>` | +| Generic FK to entity not owned | independent lifecycle | `Association to <Entity>` | -```bash -npx eslint <target>/db/schema.cds -``` - -## Step 8: Emit Migration Notes - -Append at end of `db/schema.cds`: - -```cds -// ============================================================================ -// Migration notes (ABAP source: <package> → CAP CDS schema) -// ============================================================================ -// -// - <N> entities generated from <M> source TABL objects -// - <P> associations inferred from FK references -// - <Q> tables with cuid aspect applied (SYSUUID_X16 primary key) -// - <R> tables with managed aspect applied (audit timestamps) -// - <S> tables flagged for REVIEW (manual mapping required) -// -// REVIEW items: -// - <list> -// -// SKIPPED items: -// - <list of POOL/CLUSTER tables with reason> -// ============================================================================ -``` +Bidirectional inference: write both sides (`<parent>.items : Composition of many <child>` AND `<child>.parent : Association to <parent>`). -## Re-validate +### Step 5 — Emit + validate -After user edits the REVIEW items: +Write `<target>/db/schema.cds` in CAP pretty-print format. Validate via: ```bash -npx cds compile <target>/db/schema.cds --to edmx -npx cds compile <target>/db/schema.cds --to sql +npx cds compile <target>/db/schema.cds --to edmx > /dev/null && echo "OK" ``` -## BTP vs On-Premise Differences - -| Aspect | BTP target | On-prem CAP (out of scope v1) | -|---|---|---| -| HANA deployment | HANA Cloud + HDI container | HANA on-prem + HDI | -| `mandt` / `CLNT` | Always OMIT — single-tenant per service | Same: CAP target is multi-tenant via service tenancy, not table field | -| Reserved words | CAP CDS reserves: `entity`, `aspect`, `type`, `service`, `action`, `function` — rename collisions | Same | -| Currency / Unit | Same semantics annotations | Same | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| `SAPRead TABL` returns empty | Table doesn't exist or wrong type | Verify with `SAPSearch query="<name>"` | -| Append structure not detected | Append linked via `APPEND` flag | Re-read with explicit DDIC dictionary scan | -| Field name collision (e.g., two `ORDER_ID` fields) | Append merged from multiple sources | Manual rename; flag in REVIEW section | -| Foreign key target outside scope | checkTable points outside Z package | Choice: extend scope, omit association, or use `String` FK reference | -| Currency without companion | DDIC table has `CURR` without referenced `CUKY` field | Manual fix; emit entity with `// REVIEW: missing currency reference` | -| `cds compile` fails on `Decimal(31, 0)` | DDIC `DEC(31)` exceeds CAP max | Cap at `Decimal(34, 2)` or use `Double` if precision not critical | - -## What This Skill Does NOT Do - -- **No table data migration** — generates schema only, NOT INSERT scripts -- **No HANA-specific syntax** — generates portable CAP CDS, not HDI artifacts (CAP `cds build` does that) -- **No append-structure deep recursion** — handles direct appends, not nested appends-of-appends -- **No CDS view migration** — that's [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) territory (Z CDS views → released SAP views OR keep as Z) -- **No semantic merge** — if two tables represent the same business entity (rare), no auto-merge; manual modeling needed -- **No table-function (`TF`) handling** — function-based tables are skipped with warning - -## When to Use This Skill - -- As Step 3 of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md) -- Standalone: greenfield CAP project that needs to mirror existing Z table inventory -- Standalone: refactor an existing CAP schema after schema drift (regenerate baseline, compare with current) -- During architecture spike: get a quick CDS skeleton from a Z package for evaluation +### Step 6 — Migration notes -## When NOT to Use This Skill +Write `<target>/docs/schema-notes.md` with: +- Tables migrated + their CAP entity name + namespace +- Aspects auto-applied +- Associations inferred + cardinality reasoning +- Manual review items: temporal candidates, ambiguous FKs, `LCHR` / `LRAW` size limits -- Source tables are pooled / clustered → redesign required, this skill skips them -- Tables are in standard SAP namespace (T*, V*, M*) → use released SAP CDS views via `@sap/cds-types-released`; don't regenerate -- Target is HANA HDI native (not CAP) → use `hana-cli` or HANA Web IDE Cloud generators +## Gotchas -## Follow-up +- **MANDT (client) field**: drop on BTP CAP (no client concept). Flag if data needs cross-client merge. +- **`@AbapCatalog` annotations** (DDIC): do NOT carry over; CAP has its own (`@cds.persistence.skip`, `@assert.range`, etc.). +- **Domain fixed values**: map to `@assert.range` if ≤10 values, otherwise emit a CodeList entity. +- **Hierarchical / parent-child Z tables**: review `Composition` choice — sometimes Association is safer (no cascade delete). -After this skill produces the schema: +## When NOT to use -- [modernize-abap-cap-service](../modernize-abap-cap-service/SKILL.md) — generate service definitions on top of these entities -- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) — generate unit tests for the entities with calculations -- Manual: review REVIEW items, refine type mappings, decide on association policies (Association vs Composition) +- Greenfield CDS design from scratch → use [`../generate-rap-service/SKILL.md`](../generate-rap-service/SKILL.md) or hand-write +- View-only entities (no underlying TABL) → not the target of this skill +- Multi-package data model with cross-package FK → split into multiple invocations, then merge manually ## References -- [CAP CDS reference](https://cap.cloud.sap/docs/cds/cdl) -- [CAP common aspects](https://cap.cloud.sap/docs/cds/common) -- [CAP semantic annotations](https://cap.cloud.sap/docs/cds/annotations) -- [DDIC type reference](https://help.sap.com/docs/abap-cloud/abap-rap-cloud-development-tools/data-types) +- [SAP CAP — CDS Modeling](https://cap.cloud.sap/docs/cds/cdl) +- [CAP — Common Reuse Aspects](https://cap.cloud.sap/docs/cds/common) (`cuid`, `managed`, `temporal`, `localized`) +- DDIC types → CDS types: [SAP — ABAP CDS Types](https://help.sap.com/docs/abap-cloud/abap-data-types) (cross-reference for ABAP CDS vs CAP CDS) diff --git a/skills/modernize-abap-cap-service/SKILL.md b/skills/modernize-abap-cap-service/SKILL.md index 4d49b3b6..9ea1ddc0 100644 --- a/skills/modernize-abap-cap-service/SKILL.md +++ b/skills/modernize-abap-cap-service/SKILL.md @@ -1,489 +1,151 @@ --- name: modernize-abap-cap-service -description: Generate CAP service definitions (srv/*.cds) and TypeScript handler stubs (srv/handlers/*.ts) from a Z* package's reports (PROG), function modules (FUNC), and classes (CLAS). Maps ABAP signatures to OData V4 action signatures, classifies bound vs unbound actions, embeds ABAP source excerpts as TODO context. Use when asked to "convert this FM to CAP action", "generate CAP service from ABAP", "scaffold CAP handlers from Z program", or as sub-skill of modernize-abap-to-btp-cap. +description: Generate a CAP service definition (`srv/service.cds` + handler skeletons) from a Z* package's function modules, function groups, and reports. Maps ABAP signatures to CAP service actions, classifies bound vs unbound, generates TypeScript handler stubs with ABAP source context as TODOs. Use when asked to "convert this ABAP FM to a CAP service", "generate CAP service from Z report", "expose Z business logic as OData V4", or as sub-skill of `modernize-abap-to-btp-cap`. --- -# Modernize ABAP CAP Service +# Modernize ABAP → CAP Service -Generate CAP service definitions (`srv/*.cds`) and TypeScript handler stubs (`srv/handlers/*.ts`) from a Z* package's reports (`PROG`), function modules (`FUNC`), and behavior-relevant classes (`CLAS`). Maps ABAP procedural / object-oriented logic to CAP intent: entities exposed as projections, action signatures derived from FM parameters, handler scaffolds with TODO markers for business logic. +Produces `<target>/srv/service.cds` + handler skeletons from a Z* package's function modules (`FUGR`), reports (`PROG`), and class methods. Greenfield CAP service exposing equivalent business logic as OData V4 actions. -Sub-skill of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md). Assumes the schema sub-skill [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) has produced `db/schema.cds` (entities + associations exist) before this skill runs. - -This skill produces a **scaffold + plan**, not production-ready business logic. Handler bodies are stubs with `// TODO: implement` comments and parameter passing wired up; the user is expected to translate ABAP business logic in a second pass. - -## v1 Guardrails (fast path) - -- **Read-only ARC-1** — no SAPWrite calls -- **CAP runtime: Node.js** — TypeScript handlers -- **OData V4 only** -- **One service per package by default** — single `srv/service.cds` covers the whole package; user can split later -- **One handler file per service** — `srv/handlers/<service>.ts` aggregates all action handlers -- **No CDS query rewriting** — ABAP SELECT statements appear in handler comments, not auto-translated to CDS-QL - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Service file | `<target>/srv/service.cds` | Single-service per package | -| Service name | derived: `<NamespaceCamelCase>Service` (e.g., `ZSALES_PKG` → `SalesService`) | Drops Z prefix, CamelCase | -| Service namespace | matches schema namespace | Coherent with [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) | -| Entity exposure | All entities from schema projected READ-only | Safe default; user adds CRUD where needed | -| Action mapping | FM → unbound action (with first ENTITY param → bound action) | OData V4 convention | -| Handler runtime | Node.js TypeScript | CAP-native | -| Handler file pattern | `srv/handlers/<service>.ts` (one per service) | Easier discovery | -| Action body | `// TODO: implement` stub + parameter destructuring | Compile-clean, runtime-rejected with `req.reject(501)` | -| Error handling | `rejectSafe` pattern | Don't leak `err.message` to client | -| Audit | `@audit-log` annotation on write actions | BTP-native | -| Validation | `@assert.range` / `@assert.format` mirrored from DDIC | Inherit DDIC-level checks | -| Authorization | placeholder `@(restrict: [{ to: 'authenticated-user' }])` | Refined later by [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md) | +Sub-skill of [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). Usable standalone when only the service layer needs migration (existing CAP schema, new service on top). ## Input -The user provides: - -- **Z package name** (required) — same package used in schema step -- **Target directory** (required) — must already contain `db/schema.cds` - -Optionally: - -- **Service split** — `--split-by entity` (one service per entity) | `--split-by submodule` (use sub-package boundaries) -- **Action policy** — `--actions-only` (skip projections) | `--projections-only` (skip actions) -- **Object scope** — comma list of PROG/FUNC/CLAS names to include -- **Skip classes** — `--skip-class` if user has already migrated class logic separately - -## Step 1: Verify Prerequisite - -### 1a. Ensure schema exists - -```bash -test -f <target>/db/schema.cds ``` - -If missing, stop and recommend running [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) first. - -### 1b. Parse entity list from schema - -Read `<target>/db/schema.cds` and extract entity names + their associations. Use these for: - -- Projection generation in service.cds -- Action parameter typing (e.g., `customer: Association to Customer` ↔ `customerID: UUID` in action signature) - -## Step 2: Enumerate Source Objects - -### 2a. List PROG/FUNC/CLAS in package - -``` -SAPRead(type="DEVC", name="<package>") +<Z-package> <target-dir> [--service-name=MyService] [--ts|--js] ``` -Filter to keep `objectType in (PROG, FUNC, FUGR, CLAS)`. For FUGR (function group), traverse to list contained FUNC objects: +## Defaults -``` -SAPRead(type="FUGR", name="<group_name>") -``` +| Aspect | Default | +|---|---| +| Service definition | `<target>/srv/service.cds` | +| Handler file | `<target>/srv/service.ts` (TypeScript; `--js` to opt out) | +| Service exposure | OData V4 | +| Bound vs unbound | bound if FM signature has a "key" parameter; unbound otherwise | +| Auth | `@requires: 'authenticated-user'` (refine in `services-auth.cds` later) | +| Logging | `cds.log('<service-name>')` per handler | +| Error handling | central `rejectSafe` helper (created if absent) | -### 2b. Classify by intent +## ABAP → CAP signature mapping -| ABAP object | Heuristic | CAP target | +| ABAP | CAP service | Handler | |---|---|---| -| `FUNC` RFC-enabled with `IMPORTING`/`EXPORTING` params | Likely entry-point | Service action (unbound or bound) | -| `FUNC` local (no RFC) | Helper/utility | Handler private function (NOT exposed in service) | -| `PROG` (executable report) | Batch / one-shot job | Service action `runReport(...)` or cron job | -| `PROG` (module pool) | UI flow + business logic | Multiple actions decomposed from flow logic + Fiori-mapped UI | -| `CLAS` with `STATIC` factory methods | Business logic | Handler import + method-by-method to action | -| `CLAS` instance with state | Stateful logic | Refactor: extract pure functions into handler | -| `CLAS` with `ENH_*` prefix | Enhancement | Skip (Clean Core violation; flag in [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md)) | - -### 2c. Read source for analysis +| `FUNCTION ZFM_X IMPORTING iv_id` | `action callX(id : String) returns Result;` | `srv.on('callX', async (req) => …)` | +| `FUNCTION ZFM_X CHANGING ct_items` | `action callX(items : array of Item) returns array of Item;` | mutating param → returned shape | +| `FUNCTION ZFM_X RAISING zcx_my_error` | `action callX() returns Result;` + `req.reject(…)` | exception class → HTTP code (table below) | +| Report `ZRPT_X SUBMIT` | `function callX() returns Report;` | trigger via Kyma CronJob or BTP Job Scheduler | +| `METHOD <class>.<method>` | bound action `action <method>(…)` | route via class wrapper | -For each retained object: +Exception class → HTTP code defaults: -``` -SAPRead(type="<type>", name="<name>") -``` - -Cache responses — same object may be analyzed twice (e.g., as entry-point and as called helper). - -## Step 3: Extract Action Signatures from Function Modules - -For each FUNC retained as entry-point: - -### 3a. Parse FM signature - -ARC-1 `SAPRead(type="FUNC", name="<name>")` returns a structured response including: - -- `importing`: array of `{name, type, optional, default}` -- `exporting`: array of `{name, type}` -- `changing`: array of `{name, type, optional}` -- `tables`: array of `{name, type, structure}` -- `exceptions`: array of `{name, description}` -- `rfcEnabled`: boolean - -Build the action signature according to these rules: - -#### Importing → action parameters - -```cds -// ABAP FM signature (example): -// IMPORTING iv_customer_id TYPE kunnr -// iv_company_code TYPE bukrs -// is_options TYPE zsales_options -// -// CAP action signature: -action getCustomerOrders( - customerId: String(10), - companyCode: String(4), - options: SalesOptionsType -) returns array of Order; -``` +| ABAP exception | HTTP | +|---|---| +| `CX_SY_*` (system) | 500 | +| `ZCX_NOT_AUTHORIZED` | 403 | +| `ZCX_NOT_FOUND` | 404 | +| `ZCX_BUSINESS_*` (business rule) | 409 | +| `ZCX_INVALID_INPUT` | 400 | -#### Type mapping for parameters +## Workflow -Same DDIC → CDS type table as [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) Step 3. Additionally: +### Step 1 — Enumerate -| ABAP FM parameter type | CAP action target | -|---|---| -| Single field (`TYPE kunnr`) | `: String(10)` (or whatever DDIC maps to) | -| Structure (`TYPE zsales_header`) | `: { ... }` inline type OR `: TypeName` from `srv/types.cds` | -| Internal table (`TABLE OF ...`) | `: array of { ... }` | -| Returning table (in `EXPORTING`) | `returns array of <Entity>` | -| Returning single | `returns <Entity>` | -| Returning structure | `returns <Type>` | -| OK / FAIL pattern (`E_RETURN TYPE bapiret2`) | NOT a return — use `req.reject` or `req.error` pattern in handler | +`SAPSearch(tadir_lookup, devclass=<pkg>, object IN ('FUGR','PROG','CLAS'))`. -#### Exceptions → CAP error pattern +Filter out: +- Helper / utility classes already covered by `migrate-custom-code` (those rewrite in-place, not exposed). +- Pure data classes (no business method). +- Test classes (`ZCL_*_TEST`). -ABAP exceptions don't have direct CAP equivalents. Map to handler error handling: +### Step 2 — Read source + signatures -```typescript -// ABAP FM raised exception NOT_FOUND -// CAP handler equivalent: -if (!customer) { - return req.reject(404, `Customer ${customerId} not found`); -} -``` +Per FM / method / report: +- `SAPRead(type='FUNC', name=<fm>)` — I/O parameters + raising classes. +- `SAPRead(type='CLAS', name=<cls>)` — public methods + types. +- `SAPContext(action='impact', target=<obj>)` — caller fan-in. -### 3b. Decide bound vs unbound action +### Step 3 — Decide service shape -| Heuristic | Decision | +| Caller pattern | Decision | |---|---| -| First parameter is an entity key (e.g., `iv_customer_id` for `Customer`) | **Bound** action on that entity: `entity Customer { ... } actions { action getOrders() returns array of Order; }` | -| All parameters are filters / config | **Unbound** action: `action runReport(...)` | -| Returns a single instance of a known entity | **Bound** action that returns `$self` after modification, or unbound with explicit return | +| Called only by UI / external system | Expose as OData action | +| Called only by other Z code | Don't expose; inline into the consuming action OR keep ABAP-side | +| Heavy read, light return | OData function (read-only) instead of action | +| Mutating + returns updated entity | Bound action | +| Pure utility (no entity context) | Unbound action under service root | -### 3c. Emit action in service.cds +### Step 4 — Generate `srv/service.cds` ```cds -service SalesService { - // Entity projections - entity Customers as projection on schema.Customer; - entity Orders as projection on schema.Order; - - // Bound actions on Customer - extend entity Customers with actions { - @audit-log - action getOrders(filter: SalesFilterType) returns array of Orders; - } +using { com.example.<pkg> as my } from '../db/schema'; - // Unbound actions - @audit-log - action runMonthlyReport(month: Integer, year: Integer) returns array of ReportLine; -} +@path: '/odata/v4/<service-name>' +service <ServiceName> @(requires: 'authenticated-user') { + entity Items as projection on my.Items actions { + action approve(reason : String) returns Items; + action reject (reason : String not null) returns Items; + }; -// Inline types declared in srv/types.cds -type SalesFilterType : { - fromDate : Date; - toDate : Date; - customerId : String(10) @assert.format: 'KUNNR-pattern'; -}; - -type ReportLine : { - customerId : String(10); - customerName: String(80); - netAmount : Decimal(15, 2) @Semantics.amount.amount; - currency : String(5) @Semantics.amount.currencyCode; -}; + function searchVendors(query : String) returns array of Vendor; + action importBatch (items : array of Item) returns BatchResult; +} ``` -## Step 4: Generate Handler Scaffolds - -### 4a. Handler file structure +Carry `@Common.Label` from FM short text. `@Core.LongDescription` from FM detailed documentation if present. -For each service, emit `srv/handlers/<service>.ts`: +### Step 5 — Generate `srv/service.ts` handler skeletons ```typescript import cds from '@sap/cds'; -import type { Service, Request } from '@sap/cds'; - -const LOG = cds.log('sales-service'); - -export default class SalesServiceImpl extends cds.ApplicationService { - async init() { - const { Customers, Orders } = this.entities; - - // Bound action: getOrders on Customer - this.on('getOrders', Customers, async (req: Request) => { - const customerId = req.params[0]?.ID; - const { filter } = req.data; - - LOG.info(`getOrders called for customer=${customerId}, filter=${JSON.stringify(filter)}`); - - // TODO: implement business logic - // ABAP source: FUNCTION Z_GET_CUSTOMER_ORDERS (package: ZSALES_PKG) - // ABAP signature: - // IMPORTING iv_customer_id TYPE kunnr - // is_filter TYPE zsales_filter - // EXPORTING et_orders TYPE TABLE OF zsales_order - // EXCEPTIONS not_found -> req.reject(404) - // bad_input -> req.reject(400) - // - // ABAP business logic excerpt (first 20 lines): - // SELECT * FROM zsales_order_h - // INTO TABLE @et_orders - // WHERE kunnr = @iv_customer_id - // AND erdat BETWEEN @is_filter-from_date AND @is_filter-to_date. - // IF sy-subrc <> 0. - // RAISE not_found. - // ENDIF. - // - // CAP equivalent suggestion (CDS-QL): - // return SELECT.from(Orders).where({ customer_ID: customerId, ... }); - - return req.reject(501, 'Not yet implemented'); - }); - - // Unbound action: runMonthlyReport - this.on('runMonthlyReport', async (req: Request) => { - const { month, year } = req.data; - - LOG.info(`runMonthlyReport called month=${month} year=${year}`); - - // TODO: implement business logic - // ABAP source: PROGRAM Z_MONTHLY_REPORT - // Excerpt: ... - - return req.reject(501, 'Not yet implemented'); - }); - - await super.init(); - } -} -``` -### 4b. ABAP source excerpt injection +const LOG = cds.log('<service-name>'); -For each TODO comment, include a 10-20 line excerpt from the ABAP source to give the user immediate context when implementing: - -- The FM signature (parsed) -- The first 15-20 lines of the FORM/METHOD/main logic -- Any `RAISE` / `EXIT` / `MESSAGE E` statements (these become `req.reject`) -- Major `SELECT` statements (suggest CDS-QL translation in comment) - -### 4c. Common ABAP → CAP handler pattern mappings - -Embed these as inline guidance comments where relevant: - -| ABAP pattern | CAP TypeScript handler | -|---|---| -| `SELECT ... FROM ... INTO TABLE ... WHERE ...` | `await SELECT.from(<Entity>).where({ ... })` | -| `MODIFY <Entity> FROM TABLE ...` | `await UPDATE(<Entity>).set(...).where(...)` or `cds.update(<Entity>, key).with(...)` | -| `INSERT <Entity> FROM TABLE ...` | `await INSERT.into(<Entity>).entries(...)` | -| `DELETE FROM <Entity> WHERE ...` | `await DELETE.from(<Entity>).where(...)` | -| `LOOP AT ... ASSIGNING <fs>` | `for (const item of items) { ... }` | -| `READ TABLE ... WITH KEY ... ` | `items.find(it => it.key === ...)` | -| `CALL FUNCTION 'Z_HELPER_FM'` | Refactor: inline function OR if released SAP API: `await cds.connect.to('<destination>').<method>(...)` | -| `RAISE EXCEPTION TYPE cx_*` | `throw new Error(...)` OR `req.reject(code, message)` | -| `MESSAGE E001(zsales)` | `req.reject(400, '<localized text>')` | -| `COMMIT WORK.` | implicit in CAP: `req.on('succeeded', ...)` post-commit hooks | -| `ROLLBACK WORK.` | `req.on('failed', ...)` | -| `AUTHORITY-CHECK OBJECT 'Z_AUTH'` | `if (!req.user.is('ScopeName')) return req.reject(403)` | -| `WRITE: / ULINE.` | NOT supported — UI rendered by Fiori app, not handler | -| `CALL SCREEN xxx.` | NOT supported — UI flow → Fiori Elements routing | - -### 4d. Class method extraction - -For `CLAS` retained: - -- Each PUBLIC instance/static method that mutates state → handler action or private helper -- Each PRIVATE method → handler private function -- Constructor logic → handler `init()` setup -- Class attributes → handler module-scope state (warn: avoid stateful handlers) - -Example for `ZCL_SALES_CALCULATOR.compute_total`: +export default class <ServiceName>Service extends cds.ApplicationService { + override async init() { + this.on('approve', this.approveItem); + this.on('reject', this.rejectItem); + this.on('searchVendors', this.searchVendors); + this.on('importBatch', this.importBatch); + return super.init(); + } -```typescript -// Imported from class ZCL_SALES_CALCULATOR.compute_total -async function computeTotal(orderId: string): Promise<{ amount: number; currency: string }> { - // TODO: implement - // ABAP method signature: - // compute_total IMPORTING iv_order_id TYPE zsales_order-id - // RETURNING VALUE(rs_result) TYPE zsales_total. - // - // ABAP excerpt: - // SELECT SUM( netwr ) AS amount, waers AS currency - // FROM zsales_item - // INTO @rs_result - // WHERE order_id = @iv_order_id - // GROUP BY waers. - - throw new Error('computeTotal not yet implemented'); + async approveItem(req: cds.Request) { + // TODO: ported from ZFM_APPROVE_ITEM + // Original: IMPORTING iv_id TYPE … EXPORTING es_result TYPE … + // Logic notes: <copied from FM documentation> + LOG.info('approve called', { id: req.params[0] }); + return req.reject(501, 'Not yet implemented'); + } + // … one stub per action } ``` -## Step 5: Generate srv/types.cds (if needed) - -If any action signature references a complex inline type, extract to `srv/types.cds`: - -```cds -namespace <namespace>; - -type SalesFilterType : { - fromDate : Date; - toDate : Date; - customerId : String(10); -}; - -type ReportLine : { - customerId : String(10); - customerName : String(80); - netAmount : Decimal(15, 2) @Semantics.amount.amount; - currency : String(5) @Semantics.amount.currencyCode; -}; -``` - -Import in `srv/service.cds`: - -```cds -using { <namespace>.SalesFilterType, <namespace>.ReportLine } from './types'; -``` - -## Step 6: Validate - -### 6a. CDS compile - -```bash -npx cds compile <target>/srv --service all --to edmx > /dev/null -``` - -Must succeed. Common errors: - -| Error | Cause | Fix | -|---|---|---| -| `Cannot resolve entity 'X' in projection` | Entity name mismatch with schema.cds | Re-read schema.cds; verify namespace and exact entity name | -| `Action parameter type 'Y' not found` | Inline type referenced but not declared | Move type definition to `srv/types.cds` | -| `Duplicate action 'foo'` | Same FM name appears in two FUGR | Disambiguate: prefix with FUGR name or refactor | -| `Bound action requires entity context` | First param doesn't match an entity key | Re-evaluate bound vs unbound classification | - -### 6b. TypeScript compile - -```bash -cd <target> && npx tsc --noEmit srv/handlers/*.ts -``` - -If errors → most likely typing issue with action parameters. Add explicit type annotations or import types from `@cap-js/cds-types` (auto-generated). - -### 6c. Lint (CAP eslint if configured) - -```bash -cd <target> && npx eslint srv/ -``` - -## Step 7: Emit Migration Notes - -Append at end of `srv/service.cds`: - -```cds -// ============================================================================ -// Migration notes (ABAP source: <package> → CAP service) -// ============================================================================ -// -// - <N> entities exposed as projections -// - <M> bound actions (derived from <P> Function Modules) -// - <Q> unbound actions (derived from <R> reports) -// - <S> handler files generated with TODO stubs -// -// Handlers requiring manual implementation: -// - <list of action names with ABAP source ref> -// -// ABAP objects SKIPPED: -// - <list with reason: state-ful class, ENH_* enhancement, etc.> -// -// Suggested follow-up: -// 1. Review each TODO in srv/handlers/*.ts -// 2. Translate ABAP business logic per the inline pattern hints -// 3. Run modernize-abap-fiori-elements to generate UI for these entities -// 4. Run modernize-abap-auth-mapping to refine the @restrict placeholders -// ============================================================================ -``` - -## Re-validate - -After user implements handler logic: - -```bash -cd <target> -npx cds compile srv --service all --to edmx -npx tsc --noEmit srv/handlers/*.ts -npm test -``` - -## BTP vs On-Premise Differences - -| Aspect | BTP target | On-prem CAP (out of scope v1) | -|---|---|---| -| Handler runtime | Node.js on Cloud Foundry | Node.js on-prem or Cloud Foundry on-prem | -| Logging | `cds.log()` → BTP Application Logging | `cds.log()` → stdout (caller-defined sink) | -| Audit | `@audit-log` → BTP Audit Log Service | `@audit-log` → custom adapter | -| Remote calls | `cds.connect.to('<destination>')` via BTP Destination Service | Same — Destination Service is portable | -| Transactions | `cds.tx(req)` — auto-commit on `req.on('succeeded')` | Same | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| `SAPRead FUNC` returns empty signature | FM has no parameters (only TABLES or LOCAL types) | Treat as unbound action; flag in TODO | -| FM uses `BAPIRET2` for error return | Standard SAP error pattern | Map to `req.reject` in handler; document in TODO | -| Class method calls a deprecated SAP API | Clean Core gap (Level B/C/D) | Skill emits TODO with `// REVIEW: replace with released API per modernize-abap-clean-core-gap report` | -| Program has SY-* dependency (sy-uname, sy-datum) | Implicit context | Map to: `sy-uname` → `req.user.id`; `sy-datum` → `new Date().toISOString().slice(0, 10)` | -| Function group has internal tables shared across FMs | State leakage | Refactor: handler module state is generally unsafe; flag for redesign | -| Recursive call to `Z_HELPER_FM` | Infinite recursion risk | Skill doesn't translate logic, just adds comment; user must avoid in handler | - -## What This Skill Does NOT Do - -- **No business logic translation** — handlers are stubs with TODO comments + ABAP excerpts for context -- **No SELECT → CDS-QL auto-conversion** — suggests CDS-QL in comments; manual translation needed -- **No PERFORM → handler function auto-conversion** — flagged in comments -- **No screen flow / module pool decomposition** — UI lives in Fiori Elements ([modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md)) -- **No CALL SCREEN handling** — UI flow doesn't map to CAP backend -- **No FM tables-parameter migration** — flagged as deprecated pattern; user redesigns to entities/actions -- **No ABAP exception class hierarchy mapping** — single error pattern via `req.reject` / `Error` -- **No replacement of deprecated APIs** — that's [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) - -## When to Use This Skill +Every stub includes a TODO with the source ABAP object name so the developer doing the port has the audit trail. -- As Step 4 of [modernize-abap-to-btp-cap](../modernize-abap-to-btp-cap/SKILL.md), after schema generation -- Standalone: extend an existing CAP service with new actions derived from Z FMs -- Onboarding: get a quick map of "what ABAP entry-points exist in this package" without running the orchestrator +### Step 6 — Migration notes -## When NOT to Use This Skill +Write `<target>/docs/service-notes.md` with: +- FM / method → CAP action mapping table. +- Exception class → HTTP code mapping applied. +- Caller-pattern decisions (exposed vs inlined vs kept ABAP-side). +- Manual-port items: complex business rules requiring re-implementation in TypeScript. -- ABAP package uses CFW (Control Framework) heavily → UI logic doesn't port; redesign UI in Fiori Elements first -- ABAP code uses dynamic call (`PERFORM ... IN PROGRAM`, `CALL FUNCTION DESTINATION DYNAMIC`) → handler scaffolds will not capture indirect dispatch; manual analysis required -- Source contains BSP applications → use [convert-ui5-to-fiori-elements](../convert-ui5-to-fiori-elements/SKILL.md) or equivalent UI skill +## Gotchas -## Follow-up +- **Modifying SAP standard tables in the FM**: CAP runtime cannot. Flag the FM as `extract_to_side_by_side_with_event_subscription` — the new CAP action subscribes to S/4 events and writes to CAP storage. +- **`SUBMIT … RETURN` reports**: refactor to action with explicit parameters. List-display reports → `function` returning `array of`. +- **AMDP / native HANA SQL inside FM**: do NOT port as embedded SQL — model as CDS view + CAP query. +- **Locks / SAP LUW**: explicit ENQUEUE → `cds.tx` + `forUpdate()`. -After this skill produces the service scaffold: +## When NOT to use -- [modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md) — generate Fiori Elements V4 UI for the new service -- [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md) — refine `@restrict` annotations from AUTHORITY-CHECK in source -- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) — generate tests for service projections with CASE/computed fields -- Manual: implement handler TODOs by translating ABAP business logic +- Schema-only migration → [`../modernize-abap-cap-schema/SKILL.md`](../modernize-abap-cap-schema/SKILL.md). +- Full ABAP rewrite in source system → [`../migrate-custom-code/SKILL.md`](../migrate-custom-code/SKILL.md). +- New RAP service in the source ABAP system → [`../generate-rap-service/SKILL.md`](../generate-rap-service/SKILL.md). ## References -- [CAP service definitions](https://cap.cloud.sap/docs/cds/cdl#services) -- [CAP TypeScript handlers](https://cap.cloud.sap/docs/node.js/typescript) -- [CAP CDS-QL](https://cap.cloud.sap/docs/node.js/cds-ql) -- [OData V4 bound/unbound actions](https://cap.cloud.sap/docs/cds/cdl#actions-and-functions) -- [CAP error handling](https://cap.cloud.sap/docs/node.js/best-practices#error-handling) +- [SAP CAP — Providing Services](https://cap.cloud.sap/docs/guides/providing-services) +- [SAP CAP — Custom Handlers (Node.js)](https://cap.cloud.sap/docs/node.js/core-services) +- [CAP capire — OData V4 Actions / Functions](https://cap.cloud.sap/docs/advanced/odata) diff --git a/skills/modernize-abap-to-btp-cap/SKILL.md b/skills/modernize-abap-to-btp-cap/SKILL.md index b18a39f6..84052bad 100644 --- a/skills/modernize-abap-to-btp-cap/SKILL.md +++ b/skills/modernize-abap-to-btp-cap/SKILL.md @@ -1,362 +1,118 @@ --- name: modernize-abap-to-btp-cap -description: End-to-end migration orchestrator from classic ABAP Z* packages to a BTP-native CAP application (CAP Node.js + Fiori Elements V4 + Cloud Foundry). Chains together specialized modernize-abap-* sub-skills (clean-core-gap, schema, service, fiori, auth-mapping, mta). Use when asked to "port this Z package to BTP CAP", "modernize this ABAP code to CAP", "generate a CAP scaffold from this Z package", or "migrate this custom code to BTP greenfield". +description: End-to-end migration orchestrator from classic ABAP Z* packages to a BTP-native CAP application (Node.js + Fiori Elements V4 + Cloud Foundry). Chains specialized `modernize-abap-cap-*` sub-skills to produce a target CAP project. Use when asked to "port this Z package to BTP CAP", "modernize ABAP to CAP", "generate a CAP scaffold from this Z package", or "migrate custom code to BTP greenfield". --- -# Modernize ABAP to BTP CAP +# Modernize ABAP → BTP CAP -End-to-end migration orchestrator from classic ABAP custom code (Z* packages) to a BTP-native CAP application — Fiori Elements V4 frontend, Node.js backend, Cloud Foundry deployment artifacts. +Orchestrator that takes an ABAP Z* package and produces a CAP project under `<target>/.target-cap-staging/`. Leaves the ABAP system untouched. -This skill chains together specialized `modernize-abap-*` skills (Clean Core gap analysis → CDS schema → CAP service → Fiori Elements → auth mapping → MTA deployment) to produce a complete target CAP project. Combines ARC-1 (source / dependency / lint via ADT) with `mcp-sap-docs` (Clean Core release state via [`SAP/abap-atc-cr-cv-s4hc`](https://github.com/SAP/abap-atc-cr-cv-s4hc)). - -Different from [migrate-custom-code](../migrate-custom-code/SKILL.md): that skill fixes ATC findings inside an ABAP system; this one **leaves the ABAP system untouched** and produces a side-by-side BTP CAP target project that consumes released S/4HANA APIs instead. - -## v1 Guardrails (fast path) - -- **Single Z* package per run** — split very large packages (> 200 objects) into sub-packages first -- **CAP Node.js target** — Java target deferred to v2 -- **Fiori Elements V4 only** — Freestyle UI5 deferred -- **Cloud Foundry deployment** — Kyma deferred to v2 -- **Read-only ARC-1 access** — no `SAPWrite` needed; can run against any ARC-1 deployment without `--allow-writes` -- **Sandbox output** — generates everything under `./target-cap-staging/`; user reviews + activates with explicit `--apply` flag -- **Scaffold + plan, not auto-deploy** — `cf push` and HANA service binding stay manual - -For research-first, multi-package, or production-quality migrations, run each sub-skill individually with explicit configuration instead of this orchestrator. - -## Smart Defaults (apply silently, do NOT ask) - -| Setting | Default | Rationale | -|---|---|---| -| Target system type | `public_cloud` | Clean Core Level A is the migration goal | -| ATC variant | `ABAP_CLOUD_READINESS` if available, else system default | Cloud-target focus | -| CAP runtime | Node.js | Default for greenfield BTP CAP; broader ecosystem | -| OData version | V4 | Current SAP standard; required for Fiori Elements V4 | -| Fiori app pattern | List Report + Object Page (LROP) | Most common CRUD pattern | -| Auth provider | XSUAA | BTP-native; Clean Core L-A compliant | -| DB target | HANA Cloud (prod) + SQLite (dev) | CAP-native; multi-target via `cds.requires` | -| Output mode | Sandbox (`./target-cap-staging/`) | Reversible review before apply | -| Output language | English (i18n bundle) | Default; user can add IT/DE/ES bundles later | -| Sub-skill chain | All 6 sequential | Full end-to-end | -| MTA build tool | `mbt` (Multitarget Application Build Tool) | SAP standard for BTP CF | +Different from [`../migrate-custom-code/SKILL.md`](../migrate-custom-code/SKILL.md): that skill fixes ATC findings *inside* an ABAP system. This one produces a *side-by-side BTP target* that consumes released S/4HANA APIs instead. ## Input -The user provides: - -- **Z package name** (required) — e.g., `ZSALES_PKG`, `Z_FI_CUSTOM` -- **Target directory** (required) — where CAP project will be generated (must not exist or must be empty) - -Optionally: - -- **ATC variant override** — `S4HANA_2023`, `ABAP_CLOUD_READINESS`, system default -- **CAP runtime override** — `node` (default) | `java` (v2 only) -- **Skip sub-skills** — comma list, e.g., `--skip fiori,mta` if user wants only schema + service -- **Fiori app namespace** — default derived from package name (`zsales_pkg` → `com.example.zsalespkg`) -- **Output mode** — `sandbox` (default) | `apply` (write directly to target dir) - -If only the package name is provided, ask for the target directory once and proceed with all defaults. - -## Step 1: System Probe + Target Setup - -### 1a. Verify ARC-1 + ADT availability - -``` -SAPManage(action="probe") -``` - -**Critical gate:** If probe reports CDS/RAP unavailable, stop — modernization needs the source system for inventory. If the system is BTP and the package is in `$TMP`, warn the user that the migration target should use a transportable package on the source side first. - -### 1b. Validate target directory - -Ensure the user-provided target directory either does not exist, is empty, or contains only a previous staging output. Do NOT overwrite an existing CAP project without explicit confirmation. - -### 1c. Initialize CAP skeleton - -Create the target structure (sandbox): - ``` -<target>/.target-cap-staging/ -├── db/ -│ └── schema.cds (empty — filled by Step 3) -├── srv/ -│ ├── service.cds (empty — filled by Step 4) -│ └── handlers/ (empty — filled by Step 4) -├── app/ (empty — filled by Step 5) -├── xs-security.json (empty — filled by Step 6) -├── mta.yaml (empty — filled by Step 7) -├── package.json (CAP boilerplate) -├── .cdsrc.json -├── .gitignore -└── README.md (porting plan + ADRs) -``` - -The skeleton mirrors a `cds init` output; the orchestrator owns it. Generate `package.json` with: - -```json -{ - "name": "<derived-from-package>", - "version": "0.1.0", - "dependencies": { - "@sap/cds": "^9", - "@cap-js/sqlite": "^2", - "@cap-js/hana": "^2", - "express": "^5" - }, - "scripts": { - "start": "cds-serve", - "watch": "cds watch", - "build": "cds build --production", - "test": "cds test" - } -} +<Z-package> <target-dir> [--cap-runtime=node|java] [--skip=…] ``` -## Step 2: Run modernize-abap-clean-core-gap (sub-skill) - -Hand off to [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) with the same package + target directory. - -**Expected output**: `<target>/.target-cap-staging/docs/clean-core-gap.md` containing: +Examples: +- `ZSALES_PKG ./out` — full default chain +- `ZSALES_PKG ./out --skip=fiori,mta` — only schema + service, no UI / deploy artifacts -- Per-object Clean Core level (A/B/C/D) for every `Z*` object in scope -- Cross-edition compliance matrix (`public_cloud` × `private_cloud` × `on_premise`) -- Replacement suggestions for Level B/C/D references -- Risk assessment: per-object migration effort estimate +## Defaults (applied silently) -**Gate**: if more than 30% of objects are Level C/D, the skill warns the user that a "lift-and-shift" approach may be infeasible and recommends [migrate-custom-code](../migrate-custom-code/SKILL.md) first to fix ATC findings before retrying modernization. +| Aspect | Default | +|---|---| +| Target | `public_cloud` (Clean Core L-A goal) | +| CAP runtime | Node.js (Java deferred to v2) | +| OData | V4 | +| Fiori pattern | List Report + Object Page (LROP) | +| Auth | XSUAA | +| DB | HANA Cloud prod + SQLite dev | +| Output mode | Sandbox `./target-cap-staging/` (reversible) | +| MTA build | `mbt` | +| Output language | English i18n bundle | -## Step 3: Run modernize-abap-cap-schema (sub-skill) +## Chain -Hand off to [modernize-abap-cap-schema](../modernize-abap-cap-schema/SKILL.md) with the same package + target directory. +The orchestrator runs the 6 sub-steps below sequentially. Each can be re-run independently via the linked sub-skill. -**Expected output**: `<target>/.target-cap-staging/db/schema.cds` containing: - -- Namespace derived from package (e.g., `namespace com.example.zsalespkg;`) -- One CDS entity per `TABL` in the source package -- DDIC → CDS type mapping applied (DEC → Decimal, CHAR → String, DATS → Date, …) -- Associations / compositions from foreign-key references -- `@assert` annotations for NOT NULL fields -- `@Common.Label` from ABAP table short text -- `cuid` / `managed` aspects auto-applied where keys are sysuuid_x16 or fields named `created_*`/`changed_*` +| Step | Sub-skill | Produces | +|---|---|---| +| 1 | (this orchestrator) Pre-flight + skeleton | CAP `cds init`-style structure under `<target>/.target-cap-staging/`, package.json with `@sap/cds ^9` + `@cap-js/hana` + `@cap-js/sqlite` | +| 2 | Clean Core gap analysis | `docs/clean-core-gap.md` with per-object A/B/C/D level + replacement suggestions. **Gate**: > 30% C/D → recommend `migrate-custom-code` first | +| 3 | [`../modernize-abap-cap-schema/SKILL.md`](../modernize-abap-cap-schema/SKILL.md) | `db/schema.cds` from Z-tables / DDIC structures | +| 4 | [`../modernize-abap-cap-service/SKILL.md`](../modernize-abap-cap-service/SKILL.md) | `srv/service.cds` + handler stubs from FMs / programs | +| 5 | Fiori Elements V4 scaffold | `app/<namespace>/` LROP with annotations | +| 6 | Auth + MTA | `xs-security.json` + `mta.yaml` for `cf deploy` | -**Validation**: +## Pre-flight -```bash -npx cds compile <target>/.target-cap-staging/db/schema.cds --to edmx ``` - -Must succeed. If errors → log + stop + show user the offending entities. - -## Step 4: Run modernize-abap-cap-service (sub-skill) - -Hand off to [modernize-abap-cap-service](../modernize-abap-cap-service/SKILL.md) with the same package + target directory. - -**Expected output**: -- `<target>/.target-cap-staging/srv/service.cds` with entity projections + action signatures derived from Z* function modules + reports -- `<target>/.target-cap-staging/srv/handlers/*.ts` TypeScript stub files with `// TODO` placeholders for business logic -- One handler file per service action - -**Validation**: - -```bash -npx cds compile <target>/.target-cap-staging/srv --to edmx +SAPManage(action="probe") # verify ARC-1 + ADT + CDS/RAP availability ``` -## Step 5: Run modernize-abap-fiori-elements (sub-skill, optional) - -Skip if `--skip fiori` was provided. - -Hand off to [modernize-abap-fiori-elements](../modernize-abap-fiori-elements/SKILL.md). +If CDS/RAP unavailable → stop (cannot inventory). If package in `$TMP` → warn user to use a transportable package first. -**Expected output**: `<target>/.target-cap-staging/app/<entity>/` for each main entity, containing: +Validate `<target-dir>` is empty or contains a previous staging output only. Refuse to overwrite an existing CAP project without explicit confirmation. -- `webapp/manifest.json` with Fiori Elements V4 LROP route configuration -- `webapp/Component.ts` -- `annotations/annotations.cds` with `UI.LineItem` + `UI.HeaderInfo` + `UI.FieldGroup` + `UI.Facets` -- `webapp/i18n/i18n.properties` (default EN) +## Gates -## Step 6: Run modernize-abap-auth-mapping (sub-skill, optional) +- **Clean Core gap > 30% C/D objects** → warn, recommend `migrate-custom-code` first, then retry +- **No released equivalent found for a critical object** → flag in `clean-core-gap.md`; user decides whether to extract custom logic to CAP side-by-side or block +- **Target dir not empty / not staging** → refuse without `--apply` confirmation -Skip if `--skip auth` was provided. +If the source project has a Clean Core CI gate (`scripts/ci/check-s4-compat-coverage.sh` or equivalent), invoke it after staging — drift detection against `SAP/abap-atc-cr-cv-s4hc`. -Hand off to [modernize-abap-auth-mapping](../modernize-abap-auth-mapping/SKILL.md). +## Output -**Expected output**: -- `<target>/.target-cap-staging/xs-security.json` with scopes derived from `AUTHORITY-CHECK` statements -- `@restrict` annotations injected into `srv/service.cds` -- Role-templates aligned with ABAP authorization objects - -## Step 7: Run modernize-abap-btp-mta (sub-skill, optional) - -Skip if `--skip mta` was provided. - -Hand off to [modernize-abap-btp-mta](../modernize-abap-btp-mta/SKILL.md). - -**Expected output**: -- `<target>/.target-cap-staging/mta.yaml` — MTA descriptor with srv + db-deployer + approuter modules -- `<target>/.target-cap-staging/Dockerfile` — multi-stage Node.js build -- `<target>/.target-cap-staging/manifest.yml` — CF deployment manifest -- `<target>/.target-cap-staging/.cfignore` - -## Step 8: Generate Architecture Decision Records - -Write `<target>/.target-cap-staging/docs/adr/` with one ADR per major decision: - -- `0001-cap-runtime-node.md` — Why Node.js (vs Java) -- `0002-db-target-hana-sqlite.md` — Why HANA Cloud prod + SQLite dev -- `0003-auth-xsuaa.md` — Why XSUAA (vs IAS / Keycloak) -- `0004-fiori-elements-v4-lrop.md` — Why FE V4 LROP pattern -- `0005-clean-core-level-a.md` — Why released APIs only + replacement strategy from Step 2 -- `0006-side-by-side-no-abap-write.md` — Why we leave the ABAP system read-only - -Each ADR follows the MADR (Markdown Architecture Decision Record) format: - -```markdown -# <number> - <title> - -## Status -Proposed - -## Context -<from inventory: what we observed in the Z package> - -## Decision -<the choice we made + rationale linked to Smart Defaults> - -## Consequences -<what enables / what blocks / what to revisit> -``` - -## Step 9: Final Audit - -Run pre-flight checks against the generated scaffold: - -```bash -cd <target>/.target-cap-staging -npm install -npx cds compile srv --service all --to edmx > /dev/null -npx cds compile db/schema.cds --to sql > /dev/null ``` - -If the target project has `scripts/ci/check-s4-compat-coverage.sh` (or equivalent Clean Core CI gate), invoke it. - -Emit final summary report at `<target>/.target-cap-staging/docs/migration-summary.md`: - +<target>/.target-cap-staging/ +├── db/schema.cds (Step 3) +├── srv/service.cds (Step 4) +├── srv/handlers/ (Step 4) +├── app/<namespace>/ (Step 5) +├── xs-security.json (Step 6) +├── mta.yaml (Step 6) +├── package.json (Step 1) +├── docs/ +│ ├── clean-core-gap.md (Step 2) +│ └── porting-plan.md (orchestrator summary + ADRs) +└── README.md ``` -Modernization Summary — <package> → BTP CAP -=========================================== - -Source ABAP: - Package: ZSALES_PKG - Objects audited: 47 - Tables (TABL): 12 - Reports (PROG): 8 - Function modules: 18 - Classes (CLAS): 6 - CDS views (DDLS): 3 -Clean Core gap (target: public_cloud): - Level A objects: 31 (66%) - Level B objects: 9 (19%) - Level C objects: 5 (11%) - Level D objects: 2 (4%) +The user reviews under the staging dir, then promotes to the real CAP project (rename / move, or invoke with `--apply` to write directly). -Generated artifacts: - db/schema.cds 12 entities, 4 associations - srv/service.cds 3 services, 14 actions - srv/handlers/*.ts 14 handler stubs - app/*/webapp/manifest.json 2 Fiori Elements V4 apps - xs-security.json 9 scopes, 4 role-templates - mta.yaml 3 modules, 2 resources - docs/adr/ 6 ADR drafts - docs/clean-core-gap.md Risk assessment + replacement table +## Hand-off -Next steps (manual): - 1. Review docs/migration-summary.md + docs/clean-core-gap.md - 2. Review handler TODO comments and implement business logic - 3. Test locally: cd target-cap-staging && cds watch - 4. Bind HANA Cloud + run: cds deploy --to hana - 5. Build MTA: mbt build - 6. Deploy: cf deploy mta_archives/*.mtar -``` - -## Re-validate - -Re-run after manual handler implementations: +Generated CAP project is **sandbox**. Manual next steps (not in this skill): ```bash cd <target>/.target-cap-staging -npm test -npx cds compile srv --service all --to edmx +npm install +cds deploy --to hana # or sqlite for dev +mbt build # build MTA archive +cf deploy mta_archives/<archive>.mtar ``` -## BTP vs On-Premise Differences (target architecture) - -| Aspect | BTP target (this skill) | On-Premise CAP target (out of scope v1) | -|---|---|---| -| Runtime | Node.js on Cloud Foundry | Node.js on ABAP Cloud or external server | -| Auth | XSUAA + Destination Service | XSUAA on CF or IAS or Keycloak | -| DB | HANA Cloud (prod) + SQLite (dev) | HANA on-prem + SQLite (dev) | -| S/4 connection | Destination Service + Communication Arrangement | Cloud Connector + Destination | -| Build | `mbt build` → MTA → `cf deploy` | `npm run build` + custom CI/CD | -| Auth propagation | Principal Propagation via X.509 | SAML or SSO custom | - -## Error Handling - -| Error | Cause | Fix | -|---|---|---| -| `SAPManage probe` reports CDS unavailable | System lacks RAP/CDS support | Stop — modernization needs source CDS read access | -| Target directory not empty | Risk of overwriting unrelated content | Ask user to confirm or pick a fresh directory | -| CDS compile fails after schema generation | Type mapping edge case (e.g., unusual `CURR` with non-standard `Semantics`) | Log offending entity, show source, allow user to edit + retry | -| `cf push` step missing CF CLI | User runs without CF CLI installed | Surface clearly + link to CF CLI install docs; this skill stops at scaffold | -| Clean Core gap > 30% C/D | Package too coupled to internal/deprecated APIs | Recommend [migrate-custom-code](../migrate-custom-code/SKILL.md) first to address findings, then retry | -| `npm install` fails offline | No network for CAP deps | Skill output is generated; user runs `npm install` separately | -| Source package contains > 200 objects | Too large for single-run handoff | Suggest splitting into sub-packages; skill processes top-level only | -| sub-skill output missing expected files | Sub-skill failed silently | Re-run failed sub-skill standalone with verbose flag to diagnose | - -## What This Skill Does NOT Do - -- **No SAP write operations** — leaves source ABAP system untouched (read-only ARC-1 mode) -- **No CF deployment automation** — generates artifacts only; `cf push` / `cf deploy` stay manual -- **No HANA service provisioning** — user creates HANA Cloud instance + binds manually -- **No XSUAA service creation** — user creates `xsuaa` service instance via `cf create-service` or BTP cockpit -- **No automatic handler logic generation** — handlers are stubs with `// TODO`; user implements business logic -- **No regression test execution** — generates test scaffold via `generate-cds-unit-test` (separate skill); execution is user's responsibility -- **No migration of ABAP CDS Views to released SAP CDS Views** — the [modernize-abap-clean-core-gap](../modernize-abap-clean-core-gap/SKILL.md) report suggests replacements; manual mapping required -- **No transport-coordinated migration** — this is greenfield CAP, no ABAP transport involvement -- **No big-bang cutover plan** — assume coexistence (source ABAP + target CAP) for a transition period -- **No source-system ATC fix application** — that's [migrate-custom-code](../migrate-custom-code/SKILL.md) - -## When to Use This Skill - -- **Greenfield BTP CAP migration** of a Z* package previously in ECC / S/4HANA on-premise / PCE -- **Lift-and-redesign** of a custom ABAP-only application stack to BTP-native architecture -- **POC / pilot** for evaluating BTP CAP as a target for custom code -- **Quarterly modernization assessment** — run with `--skip fiori,mta,auth` to get just the inventory + gap analysis as a planning artifact -- **Pre-deployment audit** — verify Clean Core compliance before committing to a BTP migration roadmap - -## When NOT to Use This Skill +For audit / hardening / CI gates of the generated CAP project, see [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit). -- **The package must stay in S/4 as ABAP Cloud** — use [generate-rap-service-researched](../generate-rap-service-researched/SKILL.md) instead (RAP on ABAP Cloud) -- **Single object refactor** — use [migrate-custom-code](../migrate-custom-code/SKILL.md) (ATC-driven fix) -- **Unused code retirement** — run [sap-unused-code](../sap-unused-code/SKILL.md) first to scope what's actually live -- **The package has > 30% Level C/D objects** — too risky; iterate on source-side fixes first -- **You need Kyma deployment** — v1 targets CF only; track Kyma support in v2 +## When NOT to use -## Follow-up +- ABAP-system-internal ATC fixes → use [`../migrate-custom-code/SKILL.md`](../migrate-custom-code/SKILL.md) +- Single-object refactor (not whole package) → invoke a sub-skill directly +- Multi-package coordinated migration → split per package, run orchestrator N times +- Java CAP runtime → v2 (not yet supported) +- Kyma deployment → v2 (defaults to CF for now) -After successful run, the user typically runs: +## Recommended companion plugins -- [generate-cds-unit-test](../generate-cds-unit-test/SKILL.md) → for the new CAP CDS entities -- [generate-abap-unit-test](../generate-abap-unit-test/SKILL.md) → for any remaining source ABAP objects kept in coexistence -- [analyze-chat-session](../analyze-chat-session/SKILL.md) → review the modernization run, capture learnings -- Manual `cds watch` + Fiori UI review -- Manual `mbt build` + `cf deploy <mtar>` +From [secondsky/sap-skills](https://github.com/secondsky/sap-skills): -## References +- `sap-abap` (MUST) — ABAP source patterns the sub-skills consume +- `sap-abap-cds` (MUST) — CDS view patterns for Step 3 +- `sap-cap-capire` (MUST, 4 agents) — CAP scaffolding patterns for Steps 3-4 +- `sap-btp-developer-guide` (MUST) — BTP deployment for Step 6 +- `sap-fiori-tools` (SHOULD) — Fiori Elements scaffolding for Step 5 -- [SAP CAP documentation](https://cap.cloud.sap) -- [SAP Fiori Elements V4](https://experience.sap.com/fiori-design-web/floorplan-list-report/) -- [BTP Cloud Foundry deployment](https://help.sap.com/docs/btp/sap-business-technology-platform/cloud-foundry-environment) -- [Clean Core principles](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) -- [SAP API release state repository](https://github.com/SAP/abap-atc-cr-cv-s4hc) -- [MTA development descriptor](https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications-in-cloud-foundry-environment) +Plus ARC-1 MCP (mandatory — system probe + source read in Steps 1-2). diff --git a/skills/sap-erp-clean-core-refactor/SKILL.md b/skills/sap-erp-clean-core-refactor/SKILL.md index f2134ff0..e624a833 100644 --- a/skills/sap-erp-clean-core-refactor/SKILL.md +++ b/skills/sap-erp-clean-core-refactor/SKILL.md @@ -1,675 +1,161 @@ --- name: sap-erp-clean-core-refactor -description: Plans and executes a custom-code refactor of an SAP ABAP / ERP / S/4HANA system to return to Clean Core compliance — inventories every Z/Y object via the ARC-1 MCP server, classifies each one against Clean Core Levels A/B/C/D (delegating to `sap-clean-core-atc`), then performs **just-in-time** documentation lookups on a curated list of authoritative SAP sources (Apify on-demand for HTTP/SPA pages, git-clone for static repositories, MCP servers when available) to decide, per object, between an **in-place rewrite to released APIs** or an **extraction to a side-by-side BTP extension** (handing off the scaffold to `modernize-abap-to-btp-cap`). No pre-crawled knowledge base — content is queried only when a specific finding needs it, results cached per-finding under `.cache/`. Use when asked to "refactor custom code to Clean Core", "plan side-by-side extensions", "Clean Core return on ERP", "remove non-released API consumption", "extract Z code to BTP", or to produce a documented migration plan with effort estimates and dependency ordering. +description: Plans and executes a Clean Core refactor of SAP ABAP custom code (Z*/Y*). Inventories objects via ARC-1, classifies them as Level A/B/C/D (via `sap-clean-core-atc`), and decides per object whether to rewrite-in-place, extract to a side-by-side BTP extension, keep at Level B, or remove. Documentation lookups are just-in-time (no pre-built KB). Use when asked to "refactor custom code to Clean Core", "plan side-by-side extensions", "Clean Core return on ERP", or to produce a documented migration plan. --- -# SAP ERP — Clean Core Return + Side-by-Side Refactor - -Three convictions drive this skill: - -1. **"Clean Core" is not "ABAP migration"**. The goal is to reclassify custom code where it lives — rewrite to released APIs where possible, extract to BTP where it doesn't fit — without conflating it with a system upgrade. -2. **Documentation is queried just-in-time, not pre-crawled**. A pre-built knowledge base is expensive (Apify costs €400-780/year for weekly full crawl of 18 sources), stale by day 7, and produces gigabytes of content most of which is never read. Instead: maintain a **list** of authoritative SAP sources; query each one **only when a specific finding needs it**; cache the response locally for 30 days. -3. **The user pays for their own lookups**. Apify costs are per-invocation (~€0.005-0.02 per page). For a typical customer refactor (50-200 lookups), total cost is **€0.50-€5** charged to the user's own Apify account — not to a centralized infrastructure that no one wants to maintain. - -This skill does NOT ship a pre-built KB. It ships: -- A **curated source catalog** ([`./SOURCES.md`](./SOURCES.md)) listing the 18 authoritative SAP documentation sources. -- A **JIT lookup protocol** (Step 4 below) describing how to consult each source via Apify / git / MCP / WebFetch. -- A **per-finding cache** under `.cache/sap-clean-core/<hash>/` so repeat lookups within 30 days return instantly. - -## v1 Guardrails - -- **Three modes, in increasing risk order**: `discover` (read-only inventory), `plan` (inventory + JIT-driven refactor plan), `execute` (apply in-place rewrites + scaffold BTP extensions). -- **Never modify the ERP system without `execute` mode** and explicit user authorization. ABAP-side changes route through ARC-1 with version-aware writes. -- **Never invent released-API claims**. Every "rewrite to API X" suggestion must cite the source consulted (URL + retrieval timestamp + cached snapshot path). -- **Side-by-side scaffolds delegated**, not generated inline. The skill produces the **plan**; [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md) does the actual CAP generation. -- **Lookups are bounded**. Per-finding budget: max 5 Apify pages + 1 git lookup + 1 MCP query. If the per-finding budget is exhausted without an answer, surface a "research-required" item to the user, do not proceed with a guess. - -## Smart Defaults (apply silently, do NOT ask) - -| Aspect | Default | Why | -| --- | --- | --- | -| Mode | `plan` | Most users invoke this skill expecting a plan, not execution | -| Package scope | Top-level Z* package + recursive sub-packages | Customer-owned namespaces; SAP-standard untouched | -| Source system | The system bound to the ARC-1 MCP server | Single source of truth for discovery | -| Per-finding lookup budget | 5 Apify pages + 1 git lookup + 1 MCP query | Bounded cost; surface unresolved findings instead of unbounded research | -| Cache TTL | 30 days (stable docs), 7 days (community / blogs) | Balance freshness vs cost; user can `--force-refresh` to invalidate | -| Cache location | `.cache/sap-clean-core/<topic-hash>/` (gitignored) | Per-project local; not committed | -| Output destination | `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md` | Committed; serves as the decision record | -| Target Clean Core level | `A` | Most restrictive; works for all cloud targets | -| Side-by-side target | Asked once at session start | Reuses Step 0 of [`sap-cap-stack-audit-full`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md) deployment-target decision tree | -| Level B handling | `keep_at_level_b` by default (compliant; documented) | B is already Clean-Core compliant. Pushing to A is opt-in via `--aggressive` / `--push-to-a` / `--target-level=A` because escalation adds cost and may be wrong choice (most B objects use SAP-recommended patterns) | -| Level C target | A (via released-API rewrite) when equivalent exists, else B (via BAdI substitution), else side-by-side (ERP becomes A) | Cost-minimizing default. Override with `--target-level=B` to settle for B even when A is cheaper | -| Level D target | B (via BAdI / enhancement-point rewrite) when feasible, else side-by-side (ERP becomes A) | Cost-minimizing default. D objects usually have business logic that genuinely needs an extension surface; BAdI-rewrite is the SAP-recommended path. Override with `--target-level=A` to prefer side-by-side over BAdI | -| Mixed-findings objects | Object level = MAX of finding levels (e.g. one D finding + one B finding → object treated as D) | The refactor decision is per-object, not per-finding, but the plan reports residual findings post-refactor | +# SAP ERP — Clean Core Refactor -## Input - -Single argument with format `<package-or-object> [mode] [flags]`: - -| Argument | Meaning | -|---|---| -| `<package>` | Top-level customer package (e.g. `ZFI`, `ZHR`, `Y_CUSTOM`) — recursively scoped | -| `<object>` | Single object focus mode (e.g. `ZCL_INVOICE_HANDLER`, `ZFM_GET_BP_DATA`) | -| `mode` | `discover` · `plan` (default) · `execute` | -| `--target=` | `btp-cf` *(default)* · `btp-kyma` · `onprem-kyma` (drives side-by-side target choice). Default is BTP Cloud Foundry — most widely-adopted SAP-managed runtime, broadest service ecosystem (Free + paid tiers), mature `mta.yaml` tooling, lowest operational ceremony | -| `--force-refresh` | Bypass the local cache; re-query the source even if cached < 30 days | -| `--budget=N` | Override the default per-finding Apify lookup budget (default 5 pages) | -| `--aggressive` | Synonym for `--target-level=A`. For every Level B finding, also research Level A escalation paths (rewrite via Customizing OR side-by-side extraction). Emits **multi-option proposals** instead of default `keep_at_level_b`. Adds ~30-50% to plan generation cost | -| `--push-to-a=A,B,C` | Selective B→A escalation: only the listed object names get the expanded analysis. Cheaper than `--aggressive`, requires you know which objects upfront | -| `--target-level=A` | **Ambitious mode** — always prefer Level A outcomes. Forces side-by-side extraction over BAdI-rewrite for Level D when both feasible; forces released-API replacement over BAdI-substitution for Level C even when BAdI is cheaper. Activates Level B escalation analysis (same as `--aggressive`) | -| `--target-level=B` | **Minimal-compliance mode** — settle for B everywhere. For Level C, pick the cheapest path that reaches B (instead of A via released API) when that saves effort. For Level D, BAdI-rewrite only (never side-by-side just for "purity"). Useful when customer's appetite is "good enough compliance with minimum disruption" | - -Examples: -- `ZFI plan` — typical first call (defaults: target=`btp-cf`, mode=`plan`, Level B kept as B) -- `ZCL_INVOICE_HANDLER plan` — single-object focus -- `ZFI plan --target=btp-kyma` — override target to Kyma when the customer wants the Kubernetes operational model -- `ZFI execute` — apply the plan (requires confirmation per object) -- `ZFI plan --force-refresh` — re-query every source even if cached -- `ZFI plan --aggressive` — explore Level A escalation for every Level B (target stays at default `btp-cf`) -- `ZFI plan --target=btp-kyma --push-to-a=ZTABLE_TAX_RATES_LOCAL,ZCL_VENDOR_LOOKUP_EXT` — Kyma target + selective B→A for two specific objects - -## Step 1: Pre-flight - -### 1a — Verify ARC-1 MCP server connectivity - -The skill cannot proceed without an authenticated ARC-1 connection to the source SAP system. Probe via a known-cheap call: - -``` -SAPSearch(searchType="package_lookup", source="active", query="<package>") -``` - -If the call fails, stop with a clear error and direct the user to the ARC-1 setup docs. - -### 1b — Verify Apify MCP availability (or offer manual mode) - -JIT lookups against HTTP/SPA sources need Apify. Probe for the Apify MCP server: - -``` -mcp__apify__search-apify-docs(query="website content crawler") -``` - -If unavailable, the skill degrades to **manual mode**: it produces a plan with explicit "consult this URL manually" pointers, requires the user to paste back the relevant doc snippets. Slower but no Apify dependency. - -### 1c — Resolve the side-by-side deployment target - -The plan's "extract to BTP" suggestions differ per target (BTP CF vs Kyma vs on-prem). If `--target` wasn't passed, ask the user once (same dialog as [`sap-cap-stack-audit-full`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-stack-audit-full/SKILL.md) Step 0). - -Record the target as `$TARGET`. The plan's side-by-side suggestions consult the matching section of [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md). - -### 1d — Initialize the local cache - -```bash -mkdir -p .cache/sap-clean-core -grep -q "^\.cache/$" .gitignore 2>/dev/null || echo ".cache/" >> .gitignore -``` +Plans and executes the refactor of ABAP custom code to Clean Core. Three modes: -Cache entries are gitignored. Each entry is `.cache/sap-clean-core/<sha256-of-topic>/<source-id>-<yyyy-mm-dd>.md` so the same topic queried twice in the same week reads from disk. - -### 1e — Bootstrap system context (one-time per system) - -Before the first invocation against a given SAP system, run [`../bootstrap-system-context/SKILL.md`](../bootstrap-system-context/SKILL.md) to capture: SID, release, installed components, feature toggles, ATC preset, formatter settings. The output `system-info.md` grounds all subsequent decisions (which BAdIs are available on this release; which formatter to apply post-rewrite; which transport backend is configured). - -The refactor skill checks for `system-info.md` in the working directory; if missing, prompts the user to run bootstrap first. Idempotent and cheap (~30s, read-only). Without it, the plan is system-generic; with it, the plan is system-grounded. - -## Step 2: Inventory via ARC-1 MCP - -Discover every customer-owned object in scope. - -### 2a — Enumerate packages - -``` -SAPSearch(searchType="package_tree", source="active", root="<package>") -``` +| Mode | What it does | Writes? | +|---|---|---| +| `discover` | Inventory the Z*/Y* package | No | +| `plan` (default) | Inventory + classify + decide per object → emit `docs/refactor/<date>-clean-core-plan.md` | No | +| `execute` | Apply the plan: rewrite ABAP, scaffold BTP extensions, document Level B keepers, remove unused | Yes (with per-object confirmation) | -### 2b — Enumerate objects per package +## Input ``` -SAPSearch(searchType="tadir_lookup", source="active", filter={pgmid: 'R3TR', devclass: '<pkg>'}) +<package-or-object> [mode] [flags] ``` -Collect object_type, object_name, package, last_changed_at, size_loc into an inventory TSV. - -### 2c — Filter by namespace ownership - -Keep only `Z*`, `Y*`, and `/<customer-namespace>/*` prefixes. - -### 2d — Detect inactive / orphaned objects - -Cross-check with [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) when available. Mark each row with `usage_status`: `ACTIVE` / `UNUSED` / `Z_ONLY`. +Examples: +- `ZFI plan` — typical first call (default target = `btp-cf`) +- `ZCL_INVOICE_HANDLER plan` — single-object focus +- `ZFI execute` — apply plan with per-object confirmation +- `ZFI plan --target=btp-kyma --aggressive` — Kyma side-by-side target + push Level B → A -### 2e — Impact / dependency analysis (`SAPContext`) +**Flags**: -For every non-A object that may end up rewritten or extracted, ARC-1's `SAPContext(action="impact")` is the **most important MCP call** the skill makes. It returns: +| Flag | Effect | +|---|---| +| `--target=btp-cf` (default) `· btp-kyma · onprem-kyma` | Side-by-side runtime when extracting to BTP | +| `--target-level=A` (synonym of `--aggressive`) | Prefer Level A everywhere; explore B→A escalation | +| `--target-level=B` | Settle for B; cheaper paths preferred | +| `--push-to-a=A,B,C` | Selective B→A for listed objects only | +| `--force-refresh` | Bypass the 30-day cache; re-query sources | +| `--budget=N` | Per-finding Apify lookup budget (default 5) | -- **Upstream dependencies**: what the object reads / consumes. -- **Reverse dependencies (fan-in)**: who calls the object — count + identity of caller objects. -- **CDS upstream / downstream**: for CDS views, the view dependency graph. +After plan emission, edit `docs/refactor/<date>-clean-core-plan.md` to override any decision before `execute`. -The fan-in count drives effort and risk estimation: +## Decision tree (per object) -| Fan-in | Risk multiplier | Strategy hint | +| Start Level | Default Target | Path | |---|---|---| -| 0 (orphan) | 0× | candidate for `remove_unused` even if SCMON shows recent hits (might be one-off) | -| 1-3 callers, all internal Z | 1× | low-risk rewrite_in_place | -| 4-10 callers | 2× | medium-risk; consider keeping a thin adapter layer when rewriting | -| 11-50 callers | 4× | high-risk rewrite; **prefer extract_to_side_by_side** if BTP available (move new logic to BTP, leave a thin proxy in ERP that's auto-deprecation-tagged) | -| 50+ callers | 8× + manual review | **mandatory architectural review** before any decision — surface as `research_required` regardless of other signals | - -Persist the impact table alongside the inventory TSV. It feeds Step 4 decision logic. - -## Step 3: Classification via `sap-clean-core-atc` - -Delegate to the existing [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md). Receive back per-object Clean Core Level + ATC findings + top-finding categories. +| A | A | no_action | +| Unused | — | remove_unused (with sign-off) | +| C | **A** | `rewrite_in_place` via released API (or `extract_to_side_by_side` if no equivalent; `keep_at_level_b` if only data-access) | +| D | **B** | `rewrite_in_place` via BAdI / enhancement-point (or `extract_to_side_by_side` if BAdI not feasible or `--target-level=A`) | +| B | **B** | `keep_at_level_b` (default). Escalates to A only with `--aggressive` / `--push-to-a` / `--target-level=A` | -Augment the inventory with `clean_core_level`, `atc_findings_count`, `top_findings_category`. +**Side-by-side outcome** = Level A on the ERP side (the Z object disappears; logic lives on BTP under separate Clean Core gate). -## Step 4: JIT documentation lookup per non-A finding +## Workflow -For each object not classified `A` (or `UNUSED`), perform a bounded just-in-time research pass. The goal: enough evidence to decide between **rewrite-in-place** / **side-by-side** / **keep-at-B**. +### Step 1 — Pre-flight -### 4a — Cache-first lookup +- Verify ARC-1 MCP is connected (`SAPSearch` probe). +- Verify Apify MCP is available — if not, the skill degrades to **manual mode** (emits "consult URL X" pointers; user pastes back snippets). +- Resolve `$TARGET` (`--target=…` or ask once). +- One-time per system: run [`../bootstrap-system-context/SKILL.md`](../bootstrap-system-context/SKILL.md) to capture release / ATC preset / formatter into `system-info.md`. +- Init `.cache/sap-clean-core/` (gitignored). -Compute the **topic hash**: `sha256("<finding-category>:<key-object>:<key-context>")`. Examples: -- `sha256("non-released-api:BAPI_INCOMINGINVOICE_CREATE1:supplier-invoice")` -- `sha256("direct-db-access:VBAK:sales-order-header")` +### Step 2 — Inventory + impact analysis -Look in `.cache/sap-clean-core/<topic-hash>/` for entries dated within the TTL window (30d / 7d). Use them if present. +- Enumerate Z*/Y* objects: `SAPSearch(package_tree)` + `SAPSearch(tadir_lookup)`. +- Dead-code: delegate to [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) (requires `SAP_ALLOW_FREE_SQL=true`). +- **Impact analysis** for every non-A candidate: `SAPContext(action="impact")` → fan-in count drives effort × risk: -### 4b — JIT lookup protocol (cache-miss path) - -Consult sources in this order, stopping as soon as evidence is sufficient (bounded by `--budget`): - -**1. Git-cloned authoritative sources** (free, fast): - -| Source | Query method | When to use | +| Fan-in | Risk × | Strategy | |---|---|---| -| `SAP/abap-atc-cr-cv-s4hc` | local grep against JSON files | Always for any ABAP object: classify Level + edition availability | -| `SAP-samples/*` (curated) | local grep against `*.md` / `*.cds` / `*.ts` | When looking for side-by-side patterns in a known domain | -| `SAP/cloud-sdk` (docs only) | local grep against `docs-md/` | When the plan involves Cloud SDK consumption from BTP | - -These are git-clone-able weekly without cost; the project's installer ships a script that clones them under `.cache/git/` on first use and `git pull --ff-only` on subsequent runs. +| 0 | 0× | `remove_unused` candidate | +| 1-3 internal | 1× | low-risk `rewrite_in_place` | +| 4-10 | 2× | medium-risk; keep a thin adapter when rewriting | +| 11-50 | 4× | prefer `extract_to_side_by_side` if BTP available | +| 50+ | 8× | mandatory `research_required` (architectural review) | -**2. MCP-server-backed lookup** (free, fast, may not be installed): +### Step 3 — Classification -| MCP server | When to use | -|---|---| -| `mcp-sap-docs` | Help portal search, SAP Notes, Communication Scenario lookup | -| `context7` | Non-SAP library docs (rare in this skill) | - -**3. JIT Apify lookup** (per-page cost, user pays): - -| Source | Apify actor | Per-page cost (est.) | When to use | -|---|---|---|---| -| `api.sap.com` (specific service page) | `apify/puppeteer-scraper` | ~€0.01 | OData service lifecycle — released? deprecated? CS membership? | -| `help.sap.com` (specific topic page) | `apify/website-content-crawler` | ~€0.005 | Clean Core principles, extensibility guidance | -| `developers.sap.com/tutorials/<X>` | `apify/website-content-crawler` | ~€0.005 | A concrete how-to for a specific extension pattern | -| `cap.cloud.sap/docs/<X>` | `apify/website-content-crawler` | ~€0.005 | CAP scaffold patterns for the side-by-side option | -| `community.sap.com` (Q&A search) | `apify/website-content-crawler` | ~€0.01 | When stuck on a specific symptom; query the recent-90d archive | -| `blogs.sap.com` (tag-filtered) | `apify/website-content-crawler` | ~€0.01 | Architecture essays on a specific side-by-side pattern | - -**4. WebFetch fallback** (free for simple HTML; not for SPAs): - -If neither MCP nor Apify is available, use the agent's WebFetch primitive against simple-HTML SAP pages. Fails on api.sap.com (React SPA); works on help.sap.com. +Delegate to [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md). Receive back per-object Level A/B/C/D + ATC finding categories. -### 4c — Cite + cache +### Step 4 — JIT lookup + decide -For every consulted source, record: -- URL -- retrieval timestamp -- snippet relevant to the finding -- the **decision the snippet supports**: rewrite_in_place / extract_to_side_by_side / keep_at_level_b +For each non-A finding, consult sources in this order until evidence is sufficient (bounded by `--budget`): -Persist as `.cache/sap-clean-core/<topic-hash>/<source-id>-<yyyy-mm-dd>.md`. The plan in Step 5 cites these paths. +1. **Cache hit**: `.cache/sap-clean-core/<sha256-of-topic>/<source>-<date>.md` (30-day TTL stable / 7-day community). +2. **Tier-1 git** (free): grep `abap-atc-cr-cv-s4hc`, curated `SAP-samples`, `cloud-sdk` (all installed as local clones). +3. **Tier-4 MCP** (free when installed): `mcp-sap-docs`, `context7`. +4. **Tier-2 Apify** (paid, ~€0.005-0.02/page): `api.sap.com`, `help.sap.com`, `developers.sap.com`, community, blogs. +5. **Pattern mining** (free, optional): `SAPRead(VERSIONS, VERSION_SOURCE)` for the customer's own history — find how similar Z objects have already been migrated. Cuts rewrite effort 30-50%. -### 4d — Budget exhaustion handling +If budget exhausts without an answer, flag `research_required` and optionally invoke [`../explain-abap-code/SKILL.md`](../explain-abap-code/SKILL.md) for a deep dive. -If the per-finding lookup budget runs out without a definitive answer, mark the finding as **research-required** in the plan. Do **not** guess. The plan's "Research backlog" section flags these for human investigation. +Full source catalog: [`./SOURCES.md`](./SOURCES.md). Battle-tested patterns referenced for decision-making: [`./PATTERNS.md`](./PATTERNS.md). -For `research_required` findings, the skill optionally invokes [`../explain-abap-code/SKILL.md`](../explain-abap-code/SKILL.md) on the specific object to produce a structured deep-dive (object purpose, dependencies, style classification, suggested investigation paths). This narrows the human research scope from "open the code" to "verify or refute this hypothesis", reducing per-finding manual effort by ~50%. - -### 4d-quater — Pattern mining (`SAPRead(VERSIONS)`) - -Before generating rewrite suggestions for a non-trivial finding, the skill mines the project's own history via ARC-1: - -``` -SAPRead(type="VERSIONS", object_type="CLAS", object_name="<similar-Z-class>") -SAPRead(type="VERSION_SOURCE", object_type="CLAS", object_name="<class>", revision="<rev-with-relevant-change>") -``` - -The signal: how have **similar** Z objects in this package or neighbouring packages been migrated **already** by the customer's own team? Pattern matches on naming (`ZCL_*_OLD`, `ZCL_*_V2`, `ZCL_NEW_*`), version history of the target class, or `SAPTransport(action="history")` on the package. - -When a pattern emerges (e.g. "every BAPI replacement in this customer's code follows pattern X with helper Z"), the rewrite suggestion adopts the same pattern instead of a generic SAP-recommended one. **Reduces rewrite effort by 30-50% on customers with established conventions**. The mined pattern is cited in the plan alongside the SAP-released-API citation. - -### 4d-bis — Decision tree (per-object) - -For each Z/Y object with a starting Clean Core Level (A / B / C / D / Unused), apply this decision tree to determine the **decision** and the **target level**. The default behaviour is cost-minimizing per object; `--target-level` and `--aggressive` / `--push-to-a` flags shift the preference. - -``` -Object starting level = A? - YES → decision = no_action (target = A) - STOP - -Object starting level = Unused? - YES → decision = remove_unused (target = N/A) - STOP - -Object starting level = D (Modification)? - YES → Released-API equivalent fully replaces the modification? - YES → decision = rewrite_in_place via released API (target = A) - NO → BAdI / enhancement-point eligible substitute exists? - YES (default behaviour) → decision = rewrite_in_place via BAdI (target = B) - YES + --target-level=A flag → continue to side-by-side check - NO → continue to side-by-side check - Side-by-side extraction feasible (CAP + BTP target available)? - YES → decision = extract_to_side_by_side (target = A, ERP-side; logic lives on BTP) - NO → decision = accept_as_d_documented (requires explicit sign-off + ATC exemption) - STOP - -Object starting level = C (Warning — non-released-api or direct-db-access)? - YES → Released-API / released-CDS equivalent exists? - YES (default behaviour) → decision = rewrite_in_place via released API (target = A) - YES + --target-level=B flag → BAdI substitution path cheaper? if yes, decision = rewrite_in_place via BAdI (target = B) - NO → continue - Is the residual logic data-access-only (no business logic)? - YES → decision = keep_at_level_b with documentation (target = B) - NO → Side-by-side extraction feasible? - YES → decision = extract_to_side_by_side (target = A, ERP-side) - NO → decision = keep_at_level_b with documentation (target = B) - STOP - -Object starting level = B (Eligible)? - YES → --aggressive or --push-to-a covers this object or --target-level=A? - YES → emit multi-option proposal (Step 4e/4f) - default_decision: keep_at_level_b (target = B) - option 1: rewrite_in_place via Customizing (target = A) - option 2: extract_to_side_by_side (target = A, ERP-side) - NO → decision = keep_at_level_b with documentation (target = B) - STOP -``` +### Step 5 — Emit plan -**Key invariants**: -- A → only stays A (no_action). -- B → stays B (default) OR goes to A (only via opt-in escalation). -- C → goes to A (default) OR to B (only via `--target-level=B`) OR side-by-side (A, ERP-side) when no equivalent. -- D → goes to B (default via BAdI) OR to A via side-by-side (when BAdI not feasible OR `--target-level=A` is on) OR accept_as_d (last resort with explicit sign-off). -- Unused → removed (no level applies). - -**Side-by-side outcome — clarification**: when an object is extracted to BTP, the **ERP-side artefact disappears**. The ERP no longer contains the custom code, so by absence the ERP is at Level A for that domain. The logic continues to live on BTP as a CAP extension (not classified by ABAP Clean Core levels — BTP-side compliance is governed by the deployment-target gate, see [`sap-cap-clean-core-enforce`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md)). - -### 4e — Level B → Level A escalation (aggressive mode) - -By default, an object classified Level B by `sap-clean-core-atc` is decided `keep_at_level_b` — it is already Clean-Core compliant; pushing to A is effort with diminishing returns, and the chosen pattern (BAdI, Key User Extensibility, custom CDS on released base, …) is usually the SAP-recommended one for the use case. - -When the user opts into escalation (`--aggressive` for all Level B, OR `--push-to-a=A,B,C` for selected objects), the skill performs an **extended analysis** per Level B finding: it researches Level A escalation paths in addition to the default decision, and emits a **multi-option proposal** instead of a single decision. - -**Why this is not the default**: - -- Level B is already compliant (Clean Core green). -- Most B objects exist because the SAP-recommended pattern IS a B-eligible extension (BAdI, key-user, custom CDS on released base). Pushing to A means *removing the extension*, which often defeats the purpose. -- Each `--aggressive` pass adds ~30-50% to per-finding cost (extra Apify lookups for Customizing alternatives + side-by-side patterns + extension samples cross-check). - -**When the user DOES want escalation**: - -| Scenario | Reason | -|---|---| -| Governance mandate: "zero custom logic in ERP" | Organizational policy supersedes SAP defaults | -| Target deployment: S/4HANA Public Cloud strict mode | Public Cloud rejects most B patterns; must go A or side-by-side | -| Strong BTP team available | Side-by-side extraction is cleaner long-term | -| Object's B pattern was an accident ("did it custom 4 years ago for laziness") | Customizing alternative exists and is simpler | -| Object's B pattern uses a deprecated BAdI / enhancement-point | Forced migration anyway — escalate to choose target | - -**When the user should NOT escalate**: - -| Scenario | Reason | -|---|---| -| Object uses an SAP-blessed BAdI / Key User app | You're already at the SAP-recommended pattern; A would mean abandoning it | -| Object is a stable lookup table (e.g. country-specific tax rates) | B with documentation is correct; A would mean "no logic", which loses business meaning | -| Customer has no BTP plan and no Customizing alternative | Side-by-side and config rewrite both blocked; B is the only option | -| Effort budget is tight | A escalation costs 2-5× the effort of B-keep | - -### 4f — Extended decision logic for escalated findings - -When an object is in the escalation set (matched by `--aggressive` or `--push-to-a`): - -1. **First**, perform the standard Level B classification check (Step 4b) — record what pattern made it eligible. -2. **Then**, run the escalation lookup: - - Search the KB for "Customizing alternative to <pattern>" → if found, propose `rewrite_in_place` via standard Customizing. - - Search the KB for "side-by-side pattern for <domain>" → if found, propose `extract_to_side_by_side`. - - If neither escalation path is found, fall back to `keep_at_level_b` (as default). -3. **Emit a multi-option proposal** in the plan instead of a single decision: - -```yaml -ZTABLE_TAX_RATES_LOCAL # currently Level B via BAdI BADI_TAX_RATE_DETERMINATION - default_decision: keep_at_level_b - default_effort: S (2-4 h) - default_risk: low - escalation_options: - - decision: rewrite_in_place - replacement: Tax Customizing (T007A entries via SM30 / Key User app) - effort: M (8-12 h) - risk: medium (may not cover all custom rules — needs validation per country) - kb_evidence: kb-cache/<hash>/help-sap-tax-customizing-2026-05-13.md - - decision: extract_to_side_by_side - pattern: BTP CAP tax service consumed via Cloud SDK - effort: L (16-24 h) - risk: medium (introduces BTP runtime dependency) - kb_evidence: kb-cache/<hash>/cap-tax-service-pattern-2026-05-13.md - recommended: extract_to_side_by_side (consistent with vendor risk score extension already planned) -``` - -The recommendation is informed by **consistency with other decisions in the same plan**: if the plan already has 5 side-by-side extensions and this Level B is in the same domain, recommend side-by-side too. If the plan is otherwise all in-place rewrites, recommend Customizing. - -### 4g — Per-object override mechanism (post-plan editing) - -After the plan is emitted, the user can ALWAYS override any decision by editing `docs/refactor/<date>-clean-core-plan.md` between Step 5 and Step 6. The plan file is the contract: `execute` mode reads it back and respects the latest decision. Useful for: - -- Overriding `keep_at_level_b` to `rewrite_in_place` for one specific object after manual inspection. -- Downgrading an `extract_to_side_by_side` to `keep_at_level_b` after cost concerns surface. -- Adding research notes to `research_required` findings before re-running. - -This is the **finest-grained control mechanism**, complementary to `--aggressive` / `--push-to-a`. No re-run needed; just edit and execute. - -## Step 5: Refactor plan emission - -Output to `docs/refactor/<yyyy-mm-dd>-clean-core-plan.md`: - -```markdown -# Clean Core Return + Side-by-Side Refactor Plan — <yyyy-mm-dd> - -## Target deployment -- Side-by-side framework: SAP CAP (Node.js + TypeScript) -- Side-by-side runtime: <btp-cf | btp-kyma | onprem-kyma> -- Lookup budget: 5 Apify pages + 1 git lookup + 1 MCP query per finding (default) -- Apify lookups consumed: <N> (estimated cost: €<X>) -- Cache hits: <N> (no cost; reused from prior runs) - -## Inventory summary -- Total Z/Y objects in scope: <N> -- Active: <N> | Unused: <N> -- Level A: <N> | Level B: <N> | Level C: <N> | Level D: <N> - -## Refactor plan per object - -Per-object table with explicit starting-level → target-level transitions: +Write `docs/refactor/<date>-clean-core-plan.md` with one row per object: | Object | Start Level | Target Level | Decision | Replacement / Pattern | Effort | Risk | KB evidence | -|---|---|---|---|---|---|---|---| -| ZCL_INVOICE_HANDLER | D | A | rewrite_in_place | API_SUPPLIERINVOICE_PROCESS_SRV (released) | M (6h) | low | .cache/.../api-supplierinvoice-2026-05-13.md | -| ZCL_VENDOR_RISK_SCORE | C | A (ERP-side) | extract_to_side_by_side | CAP service on BTP Kyma + Event Mesh subscribe to BusinessPartnerChanged | L (24h) | medium | .cache/.../cap-event-mesh-2026-05-13.md | -| ZTABLE_TAX_RATES_LOCAL | D | B | rewrite_in_place via BAdI | BADI_TAX_RATE_DETERMINATION (eligible) | M (8h) | medium | .cache/.../tax-badi-2026-05-13.md | -| ZRFI_REPORT_OLD | (Unused) | — | remove_unused | (none) | S (1h) | low | sap-unused-code report | -| ZDDLS_VENDOR_VIEW | B | B | keep_at_level_b | (annotation hygiene fix only) | S (2h) | low | (no escalation requested) | -| (… rest of objects …) | | | | | | | | - -**Column meanings**: -- **Start Level**: classification by `sap-clean-core-atc` before refactor (A/B/C/D/Unused). -- **Target Level**: classification expected after refactor lands. For `extract_to_side_by_side`, the target is "A (ERP-side)" because the ERP no longer contains the artefact; the logic lives on BTP under separate compliance rules. -- **Decision**: one of `no_action` / `rewrite_in_place` / `rewrite_in_place via BAdI` / `extract_to_side_by_side` / `keep_at_level_b` / `remove_unused` / `accept_as_d_documented` / `research_required`. -- **Replacement / Pattern**: the concrete substitute (released API, BAdI name, BTP CAP pattern, …). - -## Level B escalation proposals (only if --aggressive or --push-to-a was used) -[For each Level B object in the escalation set, multi-option proposal showing: - - default_decision (keep_at_level_b) - - escalation_options (rewrite_in_place via Customizing, extract_to_side_by_side via BTP) - - recommended option with reasoning - - per-option effort + risk + KB evidence] - -The user reviews and chooses per object by editing the plan file before invoking execute. -Default behaviour (no flag): every Level B is kept; no escalation proposals are generated. - -## Side-by-side extension catalog -[For every "extract" outcome, the proposed CAP extension scaffold spec] - -## Sequencing -1. Remove unused (parallel): <N> objects -2. Document Level B keep-as-is (parallel): <N> objects -3. Rewrite in-place — phase 1 (low-risk): <N> objects -4. Rewrite in-place — phase 2 (medium-risk): <N> objects -5. Side-by-side extraction — per-extension PR cadence: <N> extensions -6. Level B escalations chosen by user (if any): mixed cadence per chosen path - -## Effort estimate -- Total: <hours> across <count> objects (default plan) -- Critical path: <hours> -- Quick wins (unused + documentation): <hours> -- Escalation deltas (if aggressive/push-to-a used): - * Keep at B (default): <hours> - * Rewrite via Customizing (option 1): <hours> - * Side-by-side extraction (option 2): <hours> - User chooses per object; effort can vary 2-5× depending on choices. - -## Research backlog (budget-exhausted findings) -[Items where the lookup budget was insufficient; flag for human research] - -## Source citations -[Path → URL → retrieval timestamp for every cache entry that fed a decision] -``` -## Step 6: Execute mode (opt-in) +Plus: inventory summary, side-by-side extension catalog (per `extract` outcome), suggested sequencing (quick wins → in-place phase 1 → in-place phase 2 → side-by-side parallel), research backlog, source citations. -`mode = execute` enables real changes. The skill processes objects one at a time, asks confirmation per object, and routes each to: - -### 6-pre — Generate regression tests BEFORE the rewrite - -For every object whose decision is `rewrite_in_place` and that has no existing unit test, the skill invokes [`../generate-abap-unit-test/SKILL.md`](../generate-abap-unit-test/SKILL.md) (for `CLAS` / `FUGR`) or [`../generate-cds-unit-test/SKILL.md`](../generate-cds-unit-test/SKILL.md) (for `DDLS`) **before** the rewrite is applied. - -The pre-rewrite tests capture the **current behaviour** as a baseline. After the rewrite they become the regression gate: if any pre-rewrite test fails post-rewrite, the rewrite is rolled back via `SAPGit`. Without this gate, "ATC pass" alone is not a sufficient verification — ATC checks syntax/policy compliance, not semantic equivalence. - -For objects where test generation is infeasible (too complex, too many DB dependencies), the skill flags the rewrite as **`rewrite_in_place_no_tests`** in the plan: the user can still proceed but acknowledges the higher risk. - -### 6a — Rewrite in-place (Outcome 1) - -Delegate to ARC-1 MCP: -``` -SAPRead(type="VERSIONS", ...) → confirm no concurrent writer -SAPWrite(action="update", object_type="CLAS", object_name="ZCL_X", source="<rewritten-source>") -SAPActivate(scope="object", object_type="CLAS", object_name="ZCL_X") -SAPLint(action="format", object_name="ZCL_X") ← honor project formatter from system-info.md -SAPLint(action="run_atc", scope="object", object_name="ZCL_X", target_level="A") -SAPDiagnose(action="run_unit_tests", scope="object", object_name="ZCL_X") ← run regression tests -``` +**User reviews the plan and edits any decision** before `execute`. -For rewrites that introduce RAP behavior pool logic, delegate to [`../generate-rap-logic/SKILL.md`](../generate-rap-logic/SKILL.md). For rewrites that introduce a full RAP service stack (rare in refactor but possible), delegate to [`../generate-rap-service-researched/SKILL.md`](../generate-rap-service-researched/SKILL.md). +### Step 6 — Execute (opt-in) -Roll back via `SAPGit` if ATC regresses OR regression tests fail. +Per object, ask confirmation. Then dispatch: -### 6b — Side-by-side scaffold (Outcome 2) - -Delegate to [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). The source ABAP object is **not yet removed** — both coexist until QA confirms parity. Optionally annotate the ABAP source as deprecated. - -When the side-by-side target has a Fiori Elements UI, [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) generates the UI on top of the new CAP service. - -### 6c — Document Level B keep-as-is (Outcome 3) - -Delegate to [`../sap-object-documenter/SKILL.md`](../sap-object-documenter/SKILL.md) which produces structured SKTD documentation explaining the eligible-Level-B rationale (which BAdI / enhancement-point pattern the object uses, what business rule it implements, why no released alternative). Update ATC exemption configuration if the project uses one (`SAPLint` exemption file). - -### 6.5 — Transport management - -For every object touched in this execute pass, route the change through a coordinated transport: - -``` -SAPTransport(action="requirement_check", object="<obj>", target="<TR>") ← ensure deps reachable -SAPTransport(action="create", description="Clean Core Phase <N> — <package>") OR reuse open TR -SAPTransport(action="reassign", object="<obj>", from="<auto>", to="<chosen-TR>") -``` - -For projects with abapGit / gCTS, [`SAPGit`](https://github.com/marianfoo/arc-1) (with `SAP_ALLOW_GIT_WRITES=true`) auto-commits the change with a structured message: `chore(clean-core): rewrite ZCL_X to released API X (Phase N)`. - -## Step 7: Verification - -After any execute action: -- **ATC regression check** (`SAPLint(run_atc)`): the gate that blocks the execute loop if findings count regresses. -- **Unit test regression** (`SAPDiagnose(run_unit_tests)`): runs the tests generated in Step 6-pre PLUS pre-existing tests. Any failure aborts the loop. -- **Cross-check against the audit catalog** used by [`sap-cap-clean-core-enforce`](https://github.com/Raistlin82/sap-cap-toolkit/blob/main/skills/sap-cap-clean-core-enforce/SKILL.md) if the CAP side has been deployed. -- **Compare ATC counts before/after**; any net regression aborts the execute loop and surfaces a finding. -- **Optional**: invoke [`../analyze-chat-session/SKILL.md`](../analyze-chat-session/SKILL.md) at session end to capture learnings (which rewrite patterns worked, which findings recurred) and propose new skill traps for the team. - -## BTP vs On-Premise Differences - -The decision tree is the same; what differs is the **side-by-side target framework choice**: - -| Target | Side-by-side framework | Storage | Eventing | -|---|---|---|---| -| BTP CF *(default)* | CAP Node.js / TypeScript | HANA HDI | BTP Event Mesh | -| BTP Kyma | CAP Node.js / TypeScript | PostgreSQL in-cluster or HANA Cloud | BTP Event Mesh or Kyma-native NATS | -| On-Premise Kyma | CAP Node.js / TypeScript | HANA on-prem or PostgreSQL on-prem | Kyma-native NATS | - -See [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md) Category 3 for per-target deployment patterns. - -## Cost model - -The skill is designed so total Apify cost for a typical refactor is **€0.50-€5** charged to the **user's own Apify account**, not to a centralized infrastructure. - -| Item | Estimated cost | +| Decision | Action | |---|---| -| Discovery (ARC-1 only) | €0 | -| Classification (ATC + git-clone) | €0 | -| Per-finding JIT Apify (5 pages × ~€0.01) | €0.05 | -| Refactor plan of 50 findings (default mode) | ~€2.50 | -| Refactor plan of 200 findings (default mode) | ~€10 | -| Re-running the plan within 30 days (cache hit) | €0 | -| `--aggressive` mode delta (extra escalation lookups per Level B) | +30-50% on plan cost | -| `--push-to-a=<list>` selective (3-5 extra lookups per listed object) | +€0.05-€0.10 per object | - -Example: a 200-finding refactor with **default** = ~€10. The same with `--aggressive` = ~€13-15. The same with `--push-to-a` on 8 specific objects = ~€11. - -For projects with no Apify budget, the skill operates in **manual mode**: it emits the plan with `consult URL X manually` pointers; the user pastes back doc snippets; the skill incorporates them. Slower, but zero cost. Escalation modes (`--aggressive` / `--push-to-a`) still work in manual mode — they just produce more "consult manually" pointers per object. - -## Error Handling - -| Symptom | Likely cause | Action | -| --- | --- | --- | -| ARC-1 MCP not reachable | MCP server not started / not authenticated | Stop; print setup instructions | -| Apify MCP not available | User hasn't installed it | Degrade to manual mode; emit URL pointers, prompt for paste-back | -| Apify rate-limited | User's account hit per-second limit | Backoff with jitter; warn on persistent throttling | -| ATC regression after execute | Rewrite introduced new findings | Roll back via SAPGit; surface diff | -| Side-by-side scaffold generation fails | `modernize-abap-to-btp-cap` errored | Capture error; plan unchanged; flag for retry | -| Budget exhausted on a finding | Source landscape doesn't cover this case | Mark research-required; do NOT guess | -| Cache poisoned (stale snapshot) | Source page changed without TTL expiry | User invokes `--force-refresh` | - -## What This Skill Does NOT Do - -- Does **not** plan a system upgrade. Use SAP Activate methodology for that. -- Does **not** decide whether the customer should adopt BTP — strategic decision. -- Does **not** invent SAP knowledge — every claim is source-cited. -- Does **not** modify SAP-standard code (only `Z*` / `Y*` / customer namespace). -- Does **not** generate the side-by-side CAP scaffold inline — delegates to `modernize-abap-to-btp-cap`. -- Does **not** perform data migration from Z-tables to BTP storage — separate ETL exercise. -- Does **not** pre-build or maintain a centralized SAP documentation cache. JIT only. +| `rewrite_in_place` | (1) Generate regression test via [`../generate-abap-unit-test/SKILL.md`](../generate-abap-unit-test/SKILL.md) or [`../generate-cds-unit-test/SKILL.md`](../generate-cds-unit-test/SKILL.md). (2) `SAPWrite(action="update")` + `SAPActivate` + `SAPLint(format+run_atc)` + `SAPDiagnose(run_unit_tests)`. (3) Rollback via `SAPGit` if regression. For RAP behavior pool delegate to [`../generate-rap-logic/SKILL.md`](../generate-rap-logic/SKILL.md) | +| `extract_to_side_by_side` | Delegate to [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md). ABAP source stays deprecated-tagged until QA confirms parity. UI side: [`../convert-ui5-to-fiori-elements/SKILL.md`](../convert-ui5-to-fiori-elements/SKILL.md) | +| `keep_at_level_b` | Delegate to [`../sap-object-documenter/SKILL.md`](../sap-object-documenter/SKILL.md) (SKTD rationale + ATC exemption) | +| `remove_unused` | Stakeholder sign-off → `SAPSearch(where_used)` → `SAPWrite(action="delete")` | -## When to Use This Skill +Transport: `SAPTransport(requirement_check → create → reassign)`. Optional `SAPGit` commit if `SAP_ALLOW_GIT_WRITES=true`. -- Customer is planning a Clean Core compliance program. -- Customer needs a refactor plan with effort estimates to scope the program. -- Pre-S/4HANA migration: classify and reduce custom code before the system move. -- Post-S/4HANA: re-validate Clean Core compliance and extract residual custom logic to BTP. -- Annual / quarterly governance review. +### Step 7 — Verify -## When NOT to Use +Cumulative `SAPLint(run_atc)` + `SAPDiagnose(run_unit_tests)` on the whole package. Net ATC regression aborts the loop. Optional `analyze-chat-session` at session end for learnings. -- For a single small change to a Z object (use ARC-1 directly). -- For green-field BTP development (use `modernize-abap-to-btp-cap` directly). -- For a system upgrade plan (use SAP Activate). -- When the customer hasn't decided to adopt BTP — propose a discovery / readiness assessment first. +## Cost -## Battle-Tested Patterns Referenced - -This skill builds on [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md): - -- **3.0 Deployment target decision matrix** — selecting the right BTP target for the extension. -- **3.A.* / 3.B.* / 3.C.* / 3.D.*** — target-specific deployment patterns. -- **3.11 Clean Core Level A as deployment gate** — the gate the CAP extension must pass. +| Item | Cost | +|---|---| +| ARC-1 MCP / arc-1 native skills / Tier-1 git clones / MCP-server lookups | €0 | +| Tier-2 Apify per page | €0.005-0.02 | +| Typical refactor (50-200 objects) | **€0.50-€5 total**, user pays own Apify account | +| Re-run within 30 days (cache hits) | €0 | +| `--aggressive` mode delta | +30-50% | -For the ABAP side: -- [`../sap-clean-core-atc/SKILL.md`](../sap-clean-core-atc/SKILL.md) — Level A/B/C/D classification. -- [`../sap-unused-code/SKILL.md`](../sap-unused-code/SKILL.md) — dead-code identification. -- [`../migrate-custom-code/SKILL.md`](../migrate-custom-code/SKILL.md) — ATC fix patterns when rewriting. +No centralized infra. No pre-built KB. Manual mode (no Apify) works at zero cost but slower. -For the side-by-side: -- [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md) — top-level orchestrator. -- [`../modernize-abap-cap-schema/SKILL.md`](../modernize-abap-cap-schema/SKILL.md) — Z-table → CDS entity. -- [`../modernize-abap-cap-service/SKILL.md`](../modernize-abap-cap-service/SKILL.md) — FM / program → CAP service. +## Companion files -## Recommended Companion Plugins +| File | What | +|---|---| +| [`./SOURCES.md`](./SOURCES.md) | 23 authoritative SAP sources in 4 tiers (Tier-1 git / Tier-2 Apify / Tier-3 manual / Tier-4 MCP) | +| [`./PATTERNS.md`](./PATTERNS.md) | ~70 battle-tested patterns in 8 categories (UI5/FE V4, CAP/TS, BTP/Kyma deployment with 4-target matrix, security, customizing, lifecycle, events, ecosystem plugins). Consulted during Step 1 target resolution + Step 6 side-by-side scaffold | +| [`./INTEGRATIONS.md`](./INTEGRATIONS.md) | Step-by-step mapping: refactor phase × ARC-1 MCP tool × arc-1 native skill × secondsky/sap-skills plugin | -The plugins below split into **MUST** (the skill is materially less useful without them) and **SHOULD/OPTIONAL** (situationally valuable depending on the refactor's target). +## Recommended companion plugins -### MUST — required for the skill to function correctly +**MUST** (from [secondsky/sap-skills](https://github.com/secondsky/sap-skills)): +- `sap-abap` — ABAP language patterns (Step 6 rewrite ABAP) +- `sap-abap-cds` — CDS view design (Step 6 when introducing CDS) +- `sap-cap-capire` — CAP framework + 4 dispatchable agents (Step 6 side-by-side) +- `sap-btp-developer-guide` — BTP reference (Step 1 target resolution) -| Plugin / Skill / MCP | Used for | -|---|---| -| **ARC-1 MCP server** | All 12 ABAP-side operations: discovery (`SAPSearch`), impact (`SAPContext`), classification (`SAPLint`), pattern mining (`SAPRead`), rewrite (`SAPWrite` + `SAPActivate`), verification (`SAPDiagnose`), transport (`SAPTransport`). The skill is non-functional without it | -| **`sap-abap`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap)) | Authoritative reference for ABAP language patterns (Cloud-compatible syntax, OO, EML, RTTI/RTTC, exception handling, ABAP SQL, dynamic programming). Required during Step 6a `rewrite_in_place` so the generated ABAP is Cloud-compatible by construction | -| **`sap-abap-cds`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-abap-cds)) | CDS view design reference (associations, annotations, DCL access control, CURR/QUAN handling, CASE expressions, built-in functions). Required when the rewrite introduces new CDS views (e.g. replacing direct-DB-access with a view projection) | -| **`sap-cap-capire`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-cap-capire)) | CAP framework knowledge: CDS modeling, service handlers, deployment, multitenancy. Ships **4 dispatchable agents** the skill invokes during Step 6b side-by-side scaffold: `cap-cds-modeler`, `cap-service-developer`, `cap-performance-debugger`, `cap-project-architect` | -| **`sap-btp-developer-guide`** ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sap-btp-developer-guide)) | Comprehensive BTP reference; broad enough to cover all four target deployments. Required during Step 1c side-by-side target resolution and Step 6b scaffold generation | +**Optional**: Apify MCP (JIT lookup), `mcp-sap-docs` (preferred over Apify when installed), `context7` (non-SAP libs), plus situational SHOULD plugins listed in [`./INTEGRATIONS.md`](./INTEGRATIONS.md). -### SHOULD — strongly recommended depending on refactor scope +## When NOT to use -| Plugin / Skill | When | -|---|---| -| `sap-btp-cloud-platform` | When the side-by-side scaffold binds BTP-managed services | -| `sap-btp-connectivity` | When the extension consumes S/4 via Destination service + Cloud Connector | -| `sap-fiori-tools` | When the side-by-side extension exposes a Fiori Elements UI | -| `sapui5` ([secondsky/sap-skills](https://github.com/secondsky/sap-skills/tree/main/plugins/sapui5)) | UI5 framework reference for the extension UI; ships 4 dispatchable agents (ui5-api-explorer, ui5-app-scaffolder, ui5-code-quality-advisor, ui5-migration-specialist) | -| `sapui5-linter` | Code quality gate for the generated UI5 | -| `sap-cloud-sdk` | When the extension uses Cloud SDK to consume S/4 services | -| `sap-cloud-sdk-ai` | When the extension includes LLM/embedding logic (e.g. document classification, vendor risk scoring) | +- Single small change to one Z object → use ARC-1 directly. +- Green-field BTP development → use [`../modernize-abap-to-btp-cap/SKILL.md`](../modernize-abap-to-btp-cap/SKILL.md) directly. +- System upgrade plan → use SAP Activate methodology. +- Customer has no BTP plan AND no Customizing alternative → the toolkit can still help (no-BTP customers get `rewrite_in_place` + `keep_at_level_b` paths only, compliance score lower but progress is real). -### OPTIONAL — situational +## Companion repository -| Plugin / Skill | When | -|---|---| -| **Apify MCP server** | JIT documentation lookups against HTTP/SPA sources (api.sap.com, help.sap.com). Falls back to manual mode if absent | -| `mcp-sap-docs` | Preferred over Apify for SAP Help Portal queries when installed | -| `context7` | Generic library docs for non-SAP dependencies in the extension | -| `sap-btp-job-scheduling` | When the side-by-side includes scheduled jobs | -| `sap-btp-cloud-logging` | Observability for the production extension | -| `sap-btp-master-data-integration` | When the extension subscribes to MDI events (BusinessPartnerChanged etc.) | -| `sap-btp-cloud-transport-management` | When the customer uses cTMS for coordinated transport across S/4 + BTP | -| `sap-btp-cias` | When the customer uses SAP Cloud Identity Services (IAS) instead of XSUAA | -| `sap-btp-business-application-studio` | Dev environment reference for hand-off to the customer's developer team | -| `sap-btp-integration-suite` | When the side-by-side pattern uses iFlows instead of direct CAP | -| `sap-btp-build-work-zone-advanced` | When the extension UI must surface in SAP Work Zone | - -### arc-1 native skill chain (already cross-linked in the steps above) - -| arc-1 skill | Invoked from | -|---|---| -| [`bootstrap-system-context`](../bootstrap-system-context/SKILL.md) | Step 1e (prereq) | -| [`setup-abap-mirror`](../setup-abap-mirror/SKILL.md) | Step 1 (optional read-only exploration) | -| [`explain-abap-code`](../explain-abap-code/SKILL.md) | Step 4d (research_required fallback) | -| [`sap-clean-core-atc`](../sap-clean-core-atc/SKILL.md) | Step 3 (delegate) | -| [`sap-unused-code`](../sap-unused-code/SKILL.md) | Step 2d (delegate) | -| [`sap-object-documenter`](../sap-object-documenter/SKILL.md) | Step 6c (delegate Level B documentation) | -| [`generate-abap-unit-test`](../generate-abap-unit-test/SKILL.md) | Step 6-pre (regression test for CLAS/FUGR before rewrite) | -| [`generate-cds-unit-test`](../generate-cds-unit-test/SKILL.md) | Step 6-pre (regression test for DDLS before rewrite) | -| [`generate-rap-logic`](../generate-rap-logic/SKILL.md) | Step 6a (when rewrite introduces RAP behavior pool logic) | -| [`generate-rap-service-researched`](../generate-rap-service-researched/SKILL.md) | Step 6a (when rewrite introduces full RAP stack — rare) | -| [`migrate-segw-to-rap`](../migrate-segw-to-rap/SKILL.md) | Step 6a (when the Z package contains a SEGW V2 service to modernize in parallel) | -| [`migrate-custom-code`](../migrate-custom-code/SKILL.md) | Step 6a (sibling — ATC fix patterns for rewrite findings) | -| [`modernize-abap-to-btp-cap`](../modernize-abap-to-btp-cap/SKILL.md) chain | Step 6b (side-by-side scaffold orchestrator) | -| [`convert-ui5-to-fiori-elements`](../convert-ui5-to-fiori-elements/SKILL.md) | Step 6b (Fiori Elements UI on top of new CAP service) | -| [`analyze-chat-session`](../analyze-chat-session/SKILL.md) | Step 7 (session-end learnings capture) | - -See [`./INTEGRATIONS.md`](./INTEGRATIONS.md) for the full step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 skill × secondsky plugin. See [`sap-cap-fiori-battle-tested-patterns`](./PATTERNS.md) for the broader companion plugin map across CAP audit skills. - -## See also - -The skill ships **four files** so it is fully self-contained: - -- [`./SKILL.md`](./SKILL.md) — this protocol document -- [`./SOURCES.md`](./SOURCES.md) — curated catalog of 23 authoritative SAP documentation sources (the URLs + when-to-use guidance, no crawled content) -- [`./PATTERNS.md`](./PATTERNS.md) — ~70 production-distilled battle-tested patterns in 8 categories (UI5/FE V4 traps, CAP/TS pitfalls, BTP/Kyma/OnPrem deployment, security, customizing, lifecycle, events, ecosystem plugins). Consulted during Step 1c target resolution + Step 6b side-by-side scaffold. Same content is also available as a standalone skill in [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit) for CAP-only audiences -- [`./INTEGRATIONS.md`](./INTEGRATIONS.md) — step-by-step mapping of refactor phase × ARC-1 MCP tool × arc-1 native skill × [secondsky/sap-skills](https://github.com/secondsky/sap-skills) plugin × external sources - -External cross-links to the CAP audit toolkit are operational ("see also" / coordinate with), not critical dependencies of this skill. - -## References - -- [SAP/abap-atc-cr-cv-s4hc README](https://github.com/SAP/abap-atc-cr-cv-s4hc/blob/main/README.md) — Released ABAP objects authority -- [SAP API Hub (`api.sap.com`)](https://api.sap.com/) — OData service catalog + lifecycle -- [Clean Core principles (help.sap.com)](https://help.sap.com/docs/btp/sap-business-technology-platform/clean-core) -- [SAP Custom Code Migration Guide](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/c160bf4ba0fc415da4d34d29c1547d27/d4f8e6cb9c4d4fd99b6a96b3e64dd8e2.html) -- [Apify Website Content Crawler docs](https://apify.com/apify/website-content-crawler) -- [Apify Puppeteer Scraper docs](https://apify.com/apify/puppeteer-scraper) +For audit / hardening / CI gates of the CAP applications this skill generates, see [`Raistlin82/sap-cap-toolkit`](https://github.com/Raistlin82/sap-cap-toolkit) (8 skills: clean-core-enforce, customizing-honor, security-rbac-matrix, fiori-app-audit, text-polish, stack-audit-full, ci-gates-pattern, fiori-battle-tested-patterns).